// The length in bytes of additional fields stored in the payload section. // This may be variable based on flags, but should be no more than 8 bytes. public static int GetPayloadFieldsLength(Proto2Frame frame) { switch (frame.Type) { // TODO: Extract constants case Proto2FrameType.DATA: // Variable 0 or 1 return(frame.DataHasPadding ? 1 : 0); case Proto2FrameType.HEADERS: return((frame.HeadersHasPadding ? 1 : 0) + (frame.HeadersHasPriority ? 5 : 0)); // Variable 0 to 6 case Proto2FrameType.GOAWAY: return(8); // Last stream id and error code. case Proto2FrameType.PRIORITY: return(5); // Stream dependency and weight case Proto2FrameType.RST_STREAM: return(4); // Error code case Proto2FrameType.WINDOW_UPDATE: return(4); // Update size case Proto2FrameType.PING: // 8 bytes of opaque data case Proto2FrameType.SETTINGS: // Settings are general payload case Proto2FrameType.CONTINUATION: // None case Proto2FrameType.PUSH_PROMISE: // Not implemented frames are ignored at this phase default: return(0); } }
public const int SettingSize = 6; // 2 bytes for the id, 4 bytes for the value. public static bool ReadFrame(ReadOnlySequence <byte> readableBuffer, Proto2Frame frame, uint maxFrameSize, out ReadOnlySequence <byte> framePayload) { framePayload = ReadOnlySequence <byte> .Empty; if (readableBuffer.Length < HeaderLength) { return(false); } var headerSlice = readableBuffer.Slice(0, HeaderLength); var header = headerSlice.ToSpan(); var payloadLength = (int)Bitshifter.ReadUInt24BigEndian(header); if (payloadLength > maxFrameSize) { throw new Proto2ConnectionErrorException(CoreStrings.FormatProto2ErrorFrameOverLimit(payloadLength, maxFrameSize), Proto2ErrorCode.FRAME_SIZE_ERROR); } // Make sure the whole frame is buffered var frameLength = HeaderLength + payloadLength; if (readableBuffer.Length < frameLength) { return(false); } frame.PayloadLength = payloadLength; frame.Type = (Proto2FrameType)header[TypeOffset]; frame.Flags = header[FlagsOffset]; frame.StreamId = (int)Bitshifter.ReadUInt31BigEndian(header.Slice(StreamIdOffset)); var extendedHeaderLength = ReadExtendedFields(frame, readableBuffer); // The remaining payload minus the extra fields framePayload = readableBuffer.Slice(HeaderLength + extendedHeaderLength, payloadLength - extendedHeaderLength); return(true); }
public Proto2FrameWriter( PipeWriter outputPipeWriter, ConnectionContext connectionContext, Proto2Connection http2Connection, OutputFlowControl connectionOutputFlowControl, ITimeoutControl timeoutControl, MinDataRate minResponseDataRate, string connectionId, IKestrelTrace log) { _outputWriter = outputPipeWriter; _connectionContext = connectionContext; _http2Connection = http2Connection; _connectionOutputFlowControl = connectionOutputFlowControl; _connectionId = connectionId; _log = log; _timeoutControl = timeoutControl; _minResponseDataRate = minResponseDataRate; _flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, log); _outgoingFrame = new Proto2Frame(); _headerEncodingBuffer = new byte[_maxFrameSize]; }
public Task OnDataAsync(Proto2Frame dataFrame, ReadOnlySequence <byte> payload) { // Since padding isn't buffered, immediately count padding bytes as read for flow control purposes. if (dataFrame.DataHasPadding) { // Add 1 byte for the padding length prefix. OnDataRead(dataFrame.DataPadLength + 1); } var dataPayload = payload.Slice(0, dataFrame.DataPayloadLength); // minus padding var endStream = dataFrame.DataEndStream; if (dataPayload.Length > 0) { lock (_completionLock) { RequestBodyStarted = true; if (endStream) { // No need to send any more window updates for this stream now that we've received all the data. // Call before flushing the request body pipe, because that might induce a window update. _inputFlowControl.StopWindowUpdates(); } _inputFlowControl.Advance((int)dataPayload.Length); // This check happens after flow control so that when we throw and abort, the byte count is returned to the connection // level accounting. if (InputRemaining.HasValue) { // https://tools.ietf.org/html/rfc7540#section-8.1.2.6 if (dataPayload.Length > InputRemaining.Value) { throw new Proto2StreamErrorException(StreamId, CoreStrings.Proto2StreamErrorMoreDataThanLength, Proto2ErrorCode.PROTOCOL_ERROR); } InputRemaining -= dataPayload.Length; } // Ignore data frames for aborted streams, but only after counting them for purposes of connection level flow control. if (!IsAborted) { foreach (var segment in dataPayload) { RequestBodyPipe.Writer.Write(segment.Span); } // If the stream is completed go ahead and call RequestBodyPipe.Writer.Complete(). // Data will still be available to the reader. if (!endStream) { var flushTask = RequestBodyPipe.Writer.FlushAsync(); // It shouldn't be possible for the RequestBodyPipe to fill up an return an incomplete task if // _inputFlowControl.Advance() didn't throw. Debug.Assert(flushTask.IsCompleted); } } } } if (endStream) { OnEndStreamReceived(); } return(Task.CompletedTask); }
private static int ReadExtendedFields(Proto2Frame frame, ReadOnlySequence <byte> readableBuffer) { // Copy in any extra fields for the given frame type var extendedHeaderLength = GetPayloadFieldsLength(frame); if (extendedHeaderLength > frame.PayloadLength) { throw new Proto2ConnectionErrorException( CoreStrings.FormatProto2ErrorUnexpectedFrameLength(frame.Type, expectedLength: extendedHeaderLength), Proto2ErrorCode.FRAME_SIZE_ERROR); } var extendedHeaders = readableBuffer.Slice(HeaderLength, extendedHeaderLength).ToSpan(); // Parse frame type specific fields switch (frame.Type) { /* +---------------+ |Pad Length? (8)| +---------------+-----------------------------------------------+ | Data (*) ... +---------------------------------------------------------------+ | Padding (*) ... +---------------------------------------------------------------+ */ case Proto2FrameType.DATA: // Variable 0 or 1 frame.DataPadLength = frame.DataHasPadding ? extendedHeaders[0] : (byte)0; break; /* https://tools.ietf.org/html/rfc7540#section-6.2 +---------------+ |Pad Length? (8)| +-+-------------+-----------------------------------------------+ |E| Stream Dependency? (31) | +-+-------------+-----------------------------------------------+ | Weight? (8) | +-+-------------+-----------------------------------------------+ | Header Block Fragment (*) ... +---------------------------------------------------------------+ | Padding (*) ... +---------------------------------------------------------------+ */ case Proto2FrameType.HEADERS: if (frame.HeadersHasPadding) { frame.HeadersPadLength = extendedHeaders[0]; extendedHeaders = extendedHeaders.Slice(1); } else { frame.HeadersPadLength = 0; } if (frame.HeadersHasPriority) { frame.HeadersStreamDependency = (int)Bitshifter.ReadUInt31BigEndian(extendedHeaders); frame.HeadersPriorityWeight = extendedHeaders.Slice(4)[0]; } else { frame.HeadersStreamDependency = 0; frame.HeadersPriorityWeight = 0; } break; /* https://tools.ietf.org/html/rfc7540#section-6.8 +-+-------------------------------------------------------------+ |R| Last-Stream-ID (31) | +-+-------------------------------------------------------------+ | Error Code (32) | +---------------------------------------------------------------+ | Additional Debug Data (*) | +---------------------------------------------------------------+ */ case Proto2FrameType.GOAWAY: frame.GoAwayLastStreamId = (int)Bitshifter.ReadUInt31BigEndian(extendedHeaders); frame.GoAwayErrorCode = (Proto2ErrorCode)BinaryPrimitives.ReadUInt32BigEndian(extendedHeaders.Slice(4)); break; /* https://tools.ietf.org/html/rfc7540#section-6.3 +-+-------------------------------------------------------------+ |E| Stream Dependency (31) | +-+-------------+-----------------------------------------------+ | Weight (8) | +-+-------------+ */ case Proto2FrameType.PRIORITY: frame.PriorityStreamDependency = (int)Bitshifter.ReadUInt31BigEndian(extendedHeaders); frame.PriorityWeight = extendedHeaders.Slice(4)[0]; break; /* https://tools.ietf.org/html/rfc7540#section-6.4 +---------------------------------------------------------------+ | Error Code (32) | +---------------------------------------------------------------+ */ case Proto2FrameType.RST_STREAM: frame.RstStreamErrorCode = (Proto2ErrorCode)BinaryPrimitives.ReadUInt32BigEndian(extendedHeaders); break; /* https://tools.ietf.org/html/rfc7540#section-6.9 +-+-------------------------------------------------------------+ |R| Window Size Increment (31) | +-+-------------------------------------------------------------+ */ case Proto2FrameType.WINDOW_UPDATE: frame.WindowUpdateSizeIncrement = (int)Bitshifter.ReadUInt31BigEndian(extendedHeaders); break; case Proto2FrameType.PING: // Opaque payload 8 bytes long case Proto2FrameType.SETTINGS: // Settings are general payload case Proto2FrameType.CONTINUATION: // None case Proto2FrameType.PUSH_PROMISE: // Not implemented frames are ignored at this phase default: return(0); } return(extendedHeaderLength); }