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; }
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); }
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; }
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); }
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); } } } }