Exemple #1
0
        /* 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);
        }
Exemple #2
0
 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);
 }
Exemple #3
0
 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];
 }
Exemple #7
0
 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];
 }
Exemple #8
0
        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);
        }
Exemple #9
0
        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);
        }
Exemple #10
0
        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);
        }
Exemple #12
0
 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);
        }