public Http3OutputProducer( Http3FrameWriter frameWriter, MemoryPool <byte> pool, Http3Stream stream, KestrelTrace log) { _frameWriter = frameWriter; _memoryPool = pool; _stream = stream; _log = log; _pipe = CreateDataPipe(pool); _pipeWriter = _pipe.Writer; _pipeReader = _pipe.Reader; _flusher = new TimingPipeFlusher(timeoutControl: null, log); _flusher.Initialize(_pipeWriter); _dataWriteProcessingTask = ProcessDataWrites().Preserve(); }
public static MessageBody For(Http3Stream context) { return(new Http3MessageBody(context)); }
private Http3MessageBody(Http3Stream context) : base(context) { _context = context; }
public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> application) where TContext : notnull { // An endpoint MAY avoid creating an encoder stream if it's not going to // be used(for example if its encoder doesn't wish to use the dynamic // table, or if the maximum size of the dynamic table permitted by the // peer is zero). // An endpoint MAY avoid creating a decoder stream if its decoder sets // the maximum capacity of the dynamic table to zero. // Don't create Encoder and Decoder as they aren't used now. Exception? error = null; Http3ControlStream?outboundControlStream = null; ValueTask outboundControlStreamTask = default; bool clientAbort = false; try { outboundControlStream = await CreateNewUnidirectionalStreamAsync(application); lock (_sync) { OutboundControlStream = outboundControlStream; } // Don't delay on waiting to send outbound control stream settings. outboundControlStreamTask = ProcessOutboundControlStreamAsync(outboundControlStream); while (_stoppedAcceptingStreams == 0) { var streamContext = await _multiplexedContext.AcceptAsync(_acceptStreamsCts.Token); try { // Return null on server close or cancellation. if (streamContext == null) { if (_acceptStreamsCts.Token.IsCancellationRequested) { _acceptStreamsCts = new CancellationTokenSource(); } // There is no stream so continue to skip to UpdateConnectionState in finally. // UpdateConnectionState is responsible for updating connection to // stop accepting streams and break out of accept loop. continue; } var streamDirectionFeature = streamContext.Features.Get <IStreamDirectionFeature>(); var streamIdFeature = streamContext.Features.Get <IStreamIdFeature>(); Debug.Assert(streamDirectionFeature != null); Debug.Assert(streamIdFeature != null); if (!streamDirectionFeature.CanWrite) { // Unidirectional stream var stream = new Http3ControlStream <TContext>(application, CreateHttpStreamContext(streamContext)); _streamLifetimeHandler.OnStreamCreated(stream); ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false); } else { // Request stream // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-5.2-2 if (_gracefulCloseStarted) { // https://quicwg.org/base-drafts/draft-ietf-quic-http.html#section-4.1.2-3 streamContext.Features.Get <IProtocolErrorCodeFeature>() !.Error = (long)Http3ErrorCode.RequestRejected; streamContext.Abort(new ConnectionAbortedException("HTTP/3 connection is closing and no longer accepts new requests.")); await streamContext.DisposeAsync(); continue; } // Request stream IDs are tracked. UpdateHighestOpenedRequestStreamId(streamIdFeature.StreamId); var persistentStateFeature = streamContext.Features.Get <IPersistentStateFeature>(); Debug.Assert(persistentStateFeature != null, $"Required {nameof(IPersistentStateFeature)} not on stream context."); Http3Stream <TContext> stream; // Check whether there is an existing HTTP/3 stream on the transport stream. // A stream will only be cached if the transport stream itself is reused. if (!persistentStateFeature.State.TryGetValue(StreamPersistentStateKey, out var s)) { stream = new Http3Stream <TContext>(application, CreateHttpStreamContext(streamContext)); persistentStateFeature.State.Add(StreamPersistentStateKey, stream); } else { stream = (Http3Stream <TContext>)s !; stream.InitializeWithExistingContext(streamContext.Transport); } _streamLifetimeHandler.OnStreamCreated(stream); KestrelEventSource.Log.RequestQueuedStart(stream, AspNetCore.Http.HttpProtocol.Http3); ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false); } } finally { UpdateConnectionState(); } } } catch (ConnectionResetException ex) { lock (_streams) { if (_activeRequestCount > 0) { Log.RequestProcessingError(_context.ConnectionId, ex); } } error = ex; clientAbort = true; } catch (IOException ex) { Log.RequestProcessingError(_context.ConnectionId, ex); error = ex; } catch (ConnectionAbortedException ex) { Log.RequestProcessingError(_context.ConnectionId, ex); error = ex; } catch (Http3ConnectionErrorException ex) { Log.Http3ConnectionError(_context.ConnectionId, ex); error = ex; } catch (Exception ex) { error = ex; } finally { try { // Don't try to send GOAWAY if the client has already closed the connection. if (!clientAbort) { if (TryStopAcceptingStreams() || _gracefulCloseStarted) { await SendGoAwayAsync(GetCurrentGoAwayStreamId()); } } // Abort active request streams. lock (_streams) { foreach (var stream in _streams.Values) { stream.Abort(CreateConnectionAbortError(error, clientAbort), (Http3ErrorCode)_errorCodeFeature.Error); } } if (outboundControlStream != null) { // Don't gracefully close the outbound control stream. If the peer detects // the control stream closes it will close with a procotol error. // Instead, allow control stream to be automatically aborted when the // connection is aborted. await outboundControlStreamTask; } // Complete Abort(CreateConnectionAbortError(error, clientAbort), (Http3ErrorCode)_errorCodeFeature.Error); // Wait for active requests to complete. while (_activeRequestCount > 0) { await _streamCompletionAwaitable; } _context.TimeoutControl.CancelTimeout(); } catch { Abort(CreateConnectionAbortError(error, clientAbort), Http3ErrorCode.InternalError); throw; } finally { // Connection can close without processing any request streams. var streamId = _highestOpenedRequestStreamId != DefaultHighestOpenedRequestStreamId ? _highestOpenedRequestStreamId : (long?)null; Log.Http3ConnectionClosed(_context.ConnectionId, streamId); } } }
public Http3MessageBody(Http3Stream context) : base(context) { _context = context; ExtendedConnect = _context.IsExtendedConnectRequest; }