Beispiel #1
0
        // 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);
            }
        }
Beispiel #2
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);
        }
Beispiel #3
0
 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];
 }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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);
        }