/* https://tools.ietf.org/html/rfc7540#section-4.1 +-----------------------------------------------+ | Length (24) | +---------------+---------------+---------------+ | Type (8) | Flags (8) | +-+-------------+---------------+-------------------------------+ |R| Stream Identifier (31) | +=+=============================================================+ | Frame Payload (0...) ... +---------------------------------------------------------------+ */ internal static void WriteHeader(Http2Frame frame, PipeWriter output) { var buffer = output.GetSpan(Http2FrameReader.HeaderLength); Bitshifter.WriteUInt24BigEndian(buffer, (uint)frame.PayloadLength); buffer = buffer.Slice(3); buffer[0] = (byte)frame.Type; buffer[1] = frame.Flags; buffer = buffer.Slice(2); Bitshifter.WriteUInt31BigEndian(buffer, (uint)frame.StreamId); output.Advance(Http2FrameReader.HeaderLength); }
public Http2FrameWriter( PipeWriter outputPipeWriter, ConnectionContext connectionContext, OutputFlowControl connectionOutputFlowControl, ITimeoutControl timeoutControl, string connectionId, IKestrelTrace log) { _outputWriter = outputPipeWriter; _connectionContext = connectionContext; _connectionOutputFlowControl = connectionOutputFlowControl; _connectionId = connectionId; _log = log; _flusher = new StreamSafePipeFlusher(_outputWriter, timeoutControl); _outgoingFrame = new Http2Frame(_maxFrameSize); }
public Http2FrameWriter( PipeWriter outputPipeWriter, ConnectionContext connectionContext, OutputFlowControl connectionOutputFlowControl, ITimeoutControl timeoutControl, string connectionId, IKestrelTrace log) { _outputWriter = outputPipeWriter; _connectionContext = connectionContext; _connectionOutputFlowControl = connectionOutputFlowControl; _connectionId = connectionId; _log = log; _flusher = new TimingPipeFlusher(_outputWriter, timeoutControl); _outgoingFrame = new Http2Frame(); _headerEncodingBuffer = new byte[_maxFrameSize]; }
public Task OnDataAsync(Http2Frame dataFrame) { // TODO: content-length accounting // 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 payload = dataFrame.DataPayload; var endStream = (dataFrame.DataFlags & Http2DataFrameFlags.END_STREAM) == Http2DataFrameFlags.END_STREAM; if (payload.Count > 0) { 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(payload.Count); RequestBodyPipe.Writer.Write(payload); 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); }
public const int SettingSize = 6; // 2 bytes for the id, 4 bytes for the value. public static bool TryReadFrame(ref ReadOnlySequence <byte> buffer, Http2Frame frame, uint maxFrameSize, out ReadOnlySequence <byte> framePayload) { framePayload = ReadOnlySequence <byte> .Empty; if (buffer.Length < HeaderLength) { return(false); } var headerSlice = buffer.Slice(0, HeaderLength); var header = headerSlice.ToSpan(); var payloadLength = (int)Bitshifter.ReadUInt24BigEndian(header); if (payloadLength > maxFrameSize) { throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorFrameOverLimit(payloadLength, maxFrameSize), Http2ErrorCode.FRAME_SIZE_ERROR); } // Make sure the whole frame is buffered var frameLength = HeaderLength + payloadLength; if (buffer.Length < frameLength) { return(false); } frame.PayloadLength = payloadLength; frame.Type = (Http2FrameType)header[TypeOffset]; frame.Flags = header[FlagsOffset]; frame.StreamId = (int)Bitshifter.ReadUInt31BigEndian(header.Slice(StreamIdOffset)); var extendedHeaderLength = ReadExtendedFields(frame, buffer); // The remaining payload minus the extra fields framePayload = buffer.Slice(HeaderLength + extendedHeaderLength, payloadLength - extendedHeaderLength); buffer = buffer.Slice(framePayload.End); return(true); }
public Http2FrameWriter( PipeWriter outputPipeWriter, ConnectionContext connectionContext, Http2Connection 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 Http2Frame(); _headerEncodingBuffer = new byte[_maxFrameSize]; }
public Http2FrameWriter( PipeWriter outputPipeWriter, ConnectionContext connectionContext, Http2Connection http2Connection, OutputFlowControl connectionOutputFlowControl, ITimeoutControl timeoutControl, MinDataRate minResponseDataRate, string connectionId, MemoryPool <byte> memoryPool, IKestrelTrace log) { // Allow appending more data to the PipeWriter when a flush is pending. _outputWriter = new ConcurrentPipeWriter(outputPipeWriter, memoryPool, _writeLock); _connectionContext = connectionContext; _http2Connection = http2Connection; _connectionOutputFlowControl = connectionOutputFlowControl; _connectionId = connectionId; _log = log; _timeoutControl = timeoutControl; _minResponseDataRate = minResponseDataRate; _flusher = new TimingPipeFlusher(_outputWriter, timeoutControl, log); _outgoingFrame = new Http2Frame(); _headerEncodingBuffer = new byte[_maxFrameSize]; }
public static bool ReadFrame(ReadOnlyBuffer <byte> readableBuffer, Http2Frame frame, out SequencePosition consumed, out SequencePosition examined) { consumed = readableBuffer.Start; examined = readableBuffer.End; if (readableBuffer.Length < Http2Frame.HeaderLength) { return(false); } var headerSlice = readableBuffer.Slice(0, Http2Frame.HeaderLength); headerSlice.CopyTo(frame.Raw); if (readableBuffer.Length < Http2Frame.HeaderLength + frame.Length) { return(false); } readableBuffer.Slice(Http2Frame.HeaderLength, frame.Length).CopyTo(frame.Payload); consumed = examined = readableBuffer.GetPosition(readableBuffer.Start, Http2Frame.HeaderLength + frame.Length); return(true); }
public static bool ReadFrame(ReadableBuffer readableBuffer, Http2Frame frame, out ReadCursor consumed, out ReadCursor examined) { consumed = readableBuffer.Start; examined = readableBuffer.End; if (readableBuffer.Length < Http2Frame.HeaderLength) { return(false); } var headerSlice = readableBuffer.Slice(0, Http2Frame.HeaderLength); headerSlice.CopyTo(frame.Raw); if (readableBuffer.Length < Http2Frame.HeaderLength + frame.Length) { return(false); } readableBuffer.Slice(Http2Frame.HeaderLength, frame.Length).CopyTo(frame.Payload); consumed = examined = readableBuffer.Move(readableBuffer.Start, Http2Frame.HeaderLength + frame.Length); return(true); }
public void ParseFrame(Http2Frame frame) { var settingsCount = frame.Length / 6; for (var i = 0; i < settingsCount; i++) { var offset = i * 6; var id = (Http2SettingsParameter)((frame.Payload[offset] << 8) | frame.Payload[offset + 1]); var value = (uint)((frame.Payload[offset + 2] << 24) | (frame.Payload[offset + 3] << 16) | (frame.Payload[offset + 4] << 8) | frame.Payload[offset + 5]); switch (id) { case Http2SettingsParameter.SETTINGS_HEADER_TABLE_SIZE: HeaderTableSize = value; break; case Http2SettingsParameter.SETTINGS_ENABLE_PUSH: if (value != 0 && value != 1) { throw new Http2SettingsParameterOutOfRangeException(Http2SettingsParameter.SETTINGS_ENABLE_PUSH, lowerBound: 0, upperBound: 1); } EnablePush = value == 1; break; case Http2SettingsParameter.SETTINGS_MAX_CONCURRENT_STREAMS: MaxConcurrentStreams = value; break; case Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE: if (value > int.MaxValue) { throw new Http2SettingsParameterOutOfRangeException(Http2SettingsParameter.SETTINGS_INITIAL_WINDOW_SIZE, lowerBound: 0, upperBound: int.MaxValue); } InitialWindowSize = value; break; case Http2SettingsParameter.SETTINGS_MAX_FRAME_SIZE: if (value < Http2Frame.MinAllowedMaxFrameSize || value > Http2Frame.MaxAllowedMaxFrameSize) { throw new Http2SettingsParameterOutOfRangeException(Http2SettingsParameter.SETTINGS_MAX_FRAME_SIZE, lowerBound: Http2Frame.MinAllowedMaxFrameSize, upperBound: Http2Frame.MaxAllowedMaxFrameSize); } MaxFrameSize = value; break; case Http2SettingsParameter.SETTINGS_MAX_HEADER_LIST_SIZE: MaxHeaderListSize = value; break; default: // http://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2 // // An endpoint that receives a SETTINGS frame with any unknown or unsupported identifier MUST ignore that setting. break; } } }
public Task OnDataAsync(Http2Frame 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) { 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); if (IsAborted) { // Ignore data frames for aborted streams, but only after counting them for purposes of connection level flow control. return(Task.CompletedTask); } // 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 Http2StreamErrorException(StreamId, CoreStrings.Http2StreamErrorMoreDataThanLength, Http2ErrorCode.PROTOCOL_ERROR); } InputRemaining -= dataPayload.Length; } foreach (var segment in dataPayload) { RequestBodyPipe.Writer.Write(segment.Span); } 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); }
public Task OnDataAsync(Http2Frame dataFrame, in ReadOnlySequence <byte> payload)
private static int ReadExtendedFields(Http2Frame frame, in ReadOnlySequence <byte> readableBuffer)
private static int ReadExtendedFields(Http2Frame frame, ReadOnlySequence <byte> readableBuffer) { // Copy in any extra fields for the given frame type var extendedHeaderLength = GetPayloadFieldsLength(frame); if (extendedHeaderLength > frame.PayloadLength) { throw new Http2ConnectionErrorException( CoreStrings.FormatHttp2ErrorUnexpectedFrameLength(frame.Type, expectedLength: extendedHeaderLength), Http2ErrorCode.FRAME_SIZE_ERROR); } var extendedHeaders = readableBuffer.Slice(HeaderLength, extendedHeaderLength).ToSpan(); // Parse frame type specific fields switch (frame.Type) { /* +---------------+ |Pad Length? (8)| +---------------+-----------------------------------------------+ | Data (*) ... +---------------------------------------------------------------+ | Padding (*) ... +---------------------------------------------------------------+ */ case Http2FrameType.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 Http2FrameType.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 Http2FrameType.GOAWAY: frame.GoAwayLastStreamId = (int)Bitshifter.ReadUInt31BigEndian(extendedHeaders); frame.GoAwayErrorCode = (Http2ErrorCode)BinaryPrimitives.ReadUInt32BigEndian(extendedHeaders.Slice(4)); break; /* https://tools.ietf.org/html/rfc7540#section-6.3 +-+-------------------------------------------------------------+ |E| Stream Dependency (31) | +-+-------------+-----------------------------------------------+ | Weight (8) | +-+-------------+ */ case Http2FrameType.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 Http2FrameType.RST_STREAM: frame.RstStreamErrorCode = (Http2ErrorCode)BinaryPrimitives.ReadUInt32BigEndian(extendedHeaders); break; /* https://tools.ietf.org/html/rfc7540#section-6.9 +-+-------------------------------------------------------------+ |R| Window Size Increment (31) | +-+-------------------------------------------------------------+ */ case Http2FrameType.WINDOW_UPDATE: frame.WindowUpdateSizeIncrement = (int)Bitshifter.ReadUInt31BigEndian(extendedHeaders); break; case Http2FrameType.PING: // Opaque payload 8 bytes long case Http2FrameType.SETTINGS: // Settings are general payload case Http2FrameType.CONTINUATION: // None case Http2FrameType.PUSH_PROMISE: // Not implemented frames are ignored at this phase default: return(0); } return(extendedHeaderLength); }