示例#1
0
 private void ResetRequestHeaderParsingState()
 {
     _currentHeadersStream      = null;
     _requestHeaderParsingState = RequestHeaderParsingState.Ready;
     _parsedPseudoHeaderFields  = PseudoHeaderFields.None;
     _isMethodConnect           = false;
 }
        private void ResetRequestHeaderParsingState()
        {
            if (_requestHeaderParsingState != RequestHeaderParsingState.Trailers)
            {
                _highestOpenedStreamId = _currentHeadersStream.StreamId;
            }

            _currentHeadersStream      = null;
            _requestHeaderParsingState = RequestHeaderParsingState.Ready;
            _parsedPseudoHeaderFields  = PseudoHeaderFields.None;
            _isMethodConnect           = false;
        }
示例#3
0
    public void Initialize(Http3StreamContext context)
    {
        base.Initialize(context);

        InputRemaining = null;

        _context = context;

        _errorCodeFeature   = _context.ConnectionFeatures.GetRequiredFeature <IProtocolErrorCodeFeature>();
        _streamIdFeature    = _context.ConnectionFeatures.GetRequiredFeature <IStreamIdFeature>();
        _streamAbortFeature = _context.ConnectionFeatures.GetRequiredFeature <IStreamAbortFeature>();

        _appCompletedTaskSource.Reset();
        _isClosed = 0;
        _requestHeaderParsingState = default;
        _parsedPseudoHeaderFields  = default;
        _totalParsedHeaderSize     = 0;
        _isMethodConnect           = false;
        _completionState           = default;
        StreamTimeoutTicks         = 0;

        if (_frameWriter == null)
        {
            _frameWriter = new Http3FrameWriter(
                context.StreamContext,
                context.TimeoutControl,
                context.ServiceContext.ServerOptions.Limits.MinResponseDataRate,
                context.MemoryPool,
                context.ServiceContext.Log,
                _streamIdFeature,
                context.ClientPeerSettings,
                this);

            _http3Output = new Http3OutputProducer(
                _frameWriter,
                context.MemoryPool,
                this,
                context.ServiceContext.Log);
            Output          = _http3Output;
            RequestBodyPipe = CreateRequestBodyPipe(64 * 1024); // windowSize?
            QPackDecoder    = new QPackDecoder(_context.ServiceContext.ServerOptions.Limits.Http3.MaxRequestHeaderFieldSize);
        }
        else
        {
            _http3Output.StreamReset();
            RequestBodyPipe.Reset();
            QPackDecoder.Reset();
        }

        _frameWriter.Reset(context.Transport.Output, context.ConnectionId);
    }
示例#4
0
        private bool IsPseudoHeaderField(Span<byte> name, out PseudoHeaderFields headerField)
        {
            headerField = PseudoHeaderFields.None;

            if (name.IsEmpty || name[0] != (byte)':')
            {
                return false;
            }

            // Skip ':'
            name = name.Slice(1);

            if (name.SequenceEqual(_pathBytes))
            {
                headerField = PseudoHeaderFields.Path;
            }
            else if (name.SequenceEqual(_methodBytes))
            {
                headerField = PseudoHeaderFields.Method;
            }
            else if (name.SequenceEqual(_schemeBytes))
            {
                headerField = PseudoHeaderFields.Scheme;
            }
            else if (name.SequenceEqual(_statusBytes))
            {
                headerField = PseudoHeaderFields.Status;
            }
            else if (name.SequenceEqual(_authorityBytes))
            {
                headerField = PseudoHeaderFields.Authority;
            }
            else
            {
                headerField = PseudoHeaderFields.Unknown;
            }

            return true;
        }
示例#5
0
        private static bool IsPseudoHeaderField(ReadOnlySpan <byte> name, out PseudoHeaderFields headerField)
        {
            headerField = PseudoHeaderFields.None;

            if (name.IsEmpty || name[0] != (byte)':')
            {
                return(false);
            }

            if (name.SequenceEqual(PathBytes))
            {
                headerField = PseudoHeaderFields.Path;
            }
            else if (name.SequenceEqual(MethodBytes))
            {
                headerField = PseudoHeaderFields.Method;
            }
            else if (name.SequenceEqual(SchemeBytes))
            {
                headerField = PseudoHeaderFields.Scheme;
            }
            else if (name.SequenceEqual(StatusBytes))
            {
                headerField = PseudoHeaderFields.Status;
            }
            else if (name.SequenceEqual(AuthorityBytes))
            {
                headerField = PseudoHeaderFields.Authority;
            }
            else
            {
                headerField = PseudoHeaderFields.Unknown;
            }

            return(true);
        }
示例#6
0
        private void ValidateHeader(ReadOnlySpan <byte> name, ReadOnlySpan <byte> value)
        {
            // http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.1

            /*
             * Intermediaries that process HTTP requests or responses
             * (i.e., any intermediary not acting as a tunnel) MUST NOT forward a
             * malformed request or response. Malformed requests or responses that
             * are detected MUST be treated as a stream error of type H3_MESSAGE_ERROR.
             *
             * For malformed requests, a server MAY send an HTTP response prior to
             * closing or resetting the stream.  Clients MUST NOT accept a malformed
             * response.  Note that these requirements are intended to protect
             * against several types of common attacks against HTTP; they are
             * deliberately strict because being permissive can expose
             * implementations to these vulnerabilities.*/
            if (IsPseudoHeaderField(name, out var headerField))
            {
                if (_requestHeaderParsingState == RequestHeaderParsingState.Headers)
                {
                    // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.1-4
                    // All pseudo-header fields MUST appear in the header block before regular header fields.
                    // Any request or response that contains a pseudo-header field that appears in a header
                    // block after a regular header field MUST be treated as malformed.
                    throw new Http3StreamErrorException(CoreStrings.HttpErrorPseudoHeaderFieldAfterRegularHeaders, Http3ErrorCode.MessageError);
                }

                if (_requestHeaderParsingState == RequestHeaderParsingState.Trailers)
                {
                    // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.1-3
                    // Pseudo-header fields MUST NOT appear in trailers.
                    throw new Http3StreamErrorException(CoreStrings.HttpErrorTrailersContainPseudoHeaderField, Http3ErrorCode.MessageError);
                }

                _requestHeaderParsingState = RequestHeaderParsingState.PseudoHeaderFields;

                if (headerField == PseudoHeaderFields.Unknown)
                {
                    // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.1-3
                    // Endpoints MUST treat a request or response that contains undefined or invalid pseudo-header
                    // fields as malformed.
                    throw new Http3StreamErrorException(CoreStrings.HttpErrorUnknownPseudoHeaderField, Http3ErrorCode.MessageError);
                }

                if (headerField == PseudoHeaderFields.Status)
                {
                    // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.1-3
                    // Pseudo-header fields defined for requests MUST NOT appear in responses; pseudo-header fields
                    // defined for responses MUST NOT appear in requests.
                    throw new Http3StreamErrorException(CoreStrings.HttpErrorResponsePseudoHeaderField, Http3ErrorCode.MessageError);
                }

                if ((_parsedPseudoHeaderFields & headerField) == headerField)
                {
                    // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1.1-7
                    // All HTTP/3 requests MUST include exactly one valid value for the :method, :scheme, and :path pseudo-header fields
                    throw new Http3StreamErrorException(CoreStrings.HttpErrorDuplicatePseudoHeaderField, Http3ErrorCode.MessageError);
                }

                if (headerField == PseudoHeaderFields.Method)
                {
                    _isMethodConnect = value.SequenceEqual(ConnectBytes);
                }

                _parsedPseudoHeaderFields |= headerField;
            }
            else if (_requestHeaderParsingState != RequestHeaderParsingState.Trailers)
            {
                _requestHeaderParsingState = RequestHeaderParsingState.Headers;
            }

            if (IsConnectionSpecificHeaderField(name, value))
            {
                throw new Http3StreamErrorException(CoreStrings.HttpErrorConnectionSpecificHeaderField, Http3ErrorCode.MessageError);
            }

            // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.1-3
            // A request or response containing uppercase header field names MUST be treated as malformed.
            for (var i = 0; i < name.Length; i++)
            {
                if (name[i] >= 65 && name[i] <= 90)
                {
                    if (_requestHeaderParsingState == RequestHeaderParsingState.Trailers)
                    {
                        throw new Http3StreamErrorException(CoreStrings.HttpErrorTrailerNameUppercase, Http3ErrorCode.MessageError);
                    }
                    else
                    {
                        throw new Http3StreamErrorException(CoreStrings.HttpErrorHeaderNameUppercase, Http3ErrorCode.MessageError);
                    }
                }
            }
        }
        private void ValidateHeader(Span <byte> name, Span <byte> value)
        {
            // http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.1
            if (IsPseudoHeaderField(name, out var headerField))
            {
                if (_requestHeaderParsingState == RequestHeaderParsingState.Headers)
                {
                    // All pseudo-header fields MUST appear in the header block before regular header fields.
                    // Any request or response that contains a pseudo-header field that appears in a header
                    // block after a regular header field MUST be treated as malformed (Section 8.1.2.6).
                    throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorPseudoHeaderFieldAfterRegularHeaders, Http2ErrorCode.PROTOCOL_ERROR);
                }

                if (_requestHeaderParsingState == RequestHeaderParsingState.Trailers)
                {
                    // Pseudo-header fields MUST NOT appear in trailers.
                    throw new Http2ConnectionErrorException(CoreStrings.Http2ErrorTrailersContainPseudoHeaderField, Http2ErrorCode.PROTOCOL_ERROR);
                }

                _requestHeaderParsingState = RequestHeaderParsingState.PseudoHeaderFields;

                if (headerField == PseudoHeaderFields.Unknown)
                {
                    // Endpoints MUST treat a request or response that contains undefined or invalid pseudo-header
                    // fields as malformed (Section 8.1.2.6).
                    throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorUnknownPseudoHeaderField, Http2ErrorCode.PROTOCOL_ERROR);
                }

                if (headerField == PseudoHeaderFields.Status)
                {
                    // Pseudo-header fields defined for requests MUST NOT appear in responses; pseudo-header fields
                    // defined for responses MUST NOT appear in requests.
                    throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorResponsePseudoHeaderField, Http2ErrorCode.PROTOCOL_ERROR);
                }

                if ((_parsedPseudoHeaderFields & headerField) == headerField)
                {
                    // http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2.3
                    // All HTTP/2 requests MUST include exactly one valid value for the :method, :scheme, and :path pseudo-header fields
                    throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorDuplicatePseudoHeaderField, Http2ErrorCode.PROTOCOL_ERROR);
                }

                if (headerField == PseudoHeaderFields.Method)
                {
                    _isMethodConnect = value.SequenceEqual(_connectBytes);
                }

                _parsedPseudoHeaderFields |= headerField;
            }
            else if (_requestHeaderParsingState != RequestHeaderParsingState.Trailers)
            {
                _requestHeaderParsingState = RequestHeaderParsingState.Headers;
            }

            if (IsConnectionSpecificHeaderField(name, value))
            {
                throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorConnectionSpecificHeaderField, Http2ErrorCode.PROTOCOL_ERROR);
            }

            // http://httpwg.org/specs/rfc7540.html#rfc.section.8.1.2
            // A request or response containing uppercase header field names MUST be treated as malformed (Section 8.1.2.6).
            for (var i = 0; i < name.Length; i++)
            {
                if (name[i] >= 65 && name[i] <= 90)
                {
                    if (_requestHeaderParsingState == RequestHeaderParsingState.Trailers)
                    {
                        throw new Http2ConnectionErrorException(CoreStrings.Http2ErrorTrailerNameUppercase, Http2ErrorCode.PROTOCOL_ERROR);
                    }
                    else
                    {
                        throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorHeaderNameUppercase, Http2ErrorCode.PROTOCOL_ERROR);
                    }
                }
            }
        }