public virtual void Initialize(IHttpApplication httpApplication) { if(httpApplication == null) throw new ArgumentNullException("httpApplication"); httpApplication.PostRequestHandlerExecute += (sender, e) => this.OnPostRequestHandlerExecute((HttpApplicationWrapper) (HttpApplication) sender); }
private IServer BuildTestServerAndStart(TextReader reader, ISharedTextWriter writer, IHttpApplication<int> application) { var server = new StdioServer(reader, writer); server.Start(application); return server; }
private IServer BuildTestServerAndStart(TextReader reader, ISharedTextWriter writer, IHttpApplication<int> application) { var factory = new StdioServerFactory(reader, writer); var server = factory.CreateServer(new ConfigurationBuilder().Build()); server.Start(application); return server; }
/// <summary> /// Create a new handler. /// </summary> /// <param name="next">The pipeline entry point.</param> public ClientHandler(PathString pathBase, IHttpApplication<Context> application) { if (application == null) { throw new ArgumentNullException(nameof(application)); } _application = application; // PathString.StartsWithSegments that we use below requires the base path to not end in a slash. if (pathBase.HasValue && pathBase.Value.EndsWith("/")) { pathBase = new PathString(pathBase.Value.Substring(0, pathBase.Value.Length - 1)); } _pathBase = pathBase; }
public virtual void OnPostRequestHandlerExecute(IHttpApplication httpApplication) { if(httpApplication == null) throw new ArgumentNullException("httpApplication"); if(!this.HtmlInvestigator.IsHtmlResponse(httpApplication.Context)) return; IEnumerable<IHtmlTransformer> htmlTransformers = this.HtmlTransformingContext.GetTransformers().ToArray(); if(!htmlTransformers.Any()) return; TransformableStream transformableStream = new TransformableStream(httpApplication.Response.Filter, httpApplication.Response.ContentEncoding); transformableStream.Transform += (sender, streamTransformingEventArgs) => this.OnTransform(streamTransformingEventArgs, htmlTransformers); httpApplication.Response.Filter = transformableStream; }
public void Init(IHttpApplication context) { Debug.WriteLine("AntiScrapeModule.Init"); using (var container = new UnityContainer()) { container.Configure(x => { x.AddRegistry<IoCRegistry>(); }); _storage = container.Resolve<IDataStorage>(); } context.BeginRequest += OnBeginRequest; context.PostMapRequestHandler += OnPostMapRequestHandler; _application = context; }
public Http2Stream(IHttpApplication <TContext> application, Http2StreamContext context) { Initialize(context); _application = application; }
public Middle(IHttpApplication app) { _app = app; }
public void Start <TContext>(IHttpApplication <TContext> application, IKafkaBusSubscriber subscriber) { //TODO: Code a better way to prevent same server from starting twice. if (_disposables != null) { // The server has already started and/or has not been cleaned up yet throw new InvalidOperationException("Server has already started."); } _disposables = new Stack <IDisposable>(); try { var information = this.Features.Get <IServerInformation>(); if (information?.Subscriber == null) { if (subscriber != null) { information = new ServerInformation() { Subscriber = subscriber }; //information.Subscriber = subscriber; } else { throw new InvalidOperationException($"KafkaBus subscriber could not be found. To use the KafkaBus server, call app.{nameof(ServerExtensions.ConfigureKafkaBusServer)} in Startup.Configure method and specify a subscriber to use."); } } else { subscriber = information.Subscriber; } //TODO: Add _logger properly this._logger.LogInformation($"{nameof(Server)} is starting the KafkaBus host."); var host = new KafkaBusHost <TContext>(subscriber, application, this._applicationLifetime, this._logFactory); //Register host for disposal //TODO: Make IApplicationLifeTime.Stopping to stop polling the queue. this._applicationLifetime.ApplicationStopping.Register(() => { //TODO: Make ApplicationStopping event stop dequeueing items (StopPollingQueue) host.Dispose(); }); this._applicationLifetime.ApplicationStopped.Register(() => host.Dispose()); _disposables.Push(host); host.Start(); foreach (var name in subscriber.ConnectionNames) { information.AddAddress(name); } } catch (Exception ex) { this._logger.LogError(ex.Message + Environment.NewLine + ex.StackTrace); Dispose(); throw; } }
public Task StartAsync <TContext>(IHttpApplication <TContext> application, CancellationToken cancellationToken) { if (application == null) { throw new ArgumentNullException(nameof(application)); } var hostingUrlsPresent = _serverAddresses.Addresses.Count > 0; var serverAddressCopy = _serverAddresses.Addresses.ToList(); _serverAddresses.Addresses.Clear(); if (_serverAddresses.PreferHostingUrls && hostingUrlsPresent) { if (_options.UrlPrefixes.Count > 0) { _logger.LogWarning(LoggerEventIds.ClearedPrefixes, $"Overriding endpoints added to {nameof(HttpSysOptions.UrlPrefixes)} since {nameof(IServerAddressesFeature.PreferHostingUrls)} is set to true." + $" Binding to address(es) '{string.Join(", ", _serverAddresses.Addresses)}' instead. "); Listener.Options.UrlPrefixes.Clear(); } UpdateUrlPrefixes(serverAddressCopy); } else if (_options.UrlPrefixes.Count > 0) { if (hostingUrlsPresent) { _logger.LogWarning(LoggerEventIds.ClearedAddresses, $"Overriding address(es) '{string.Join(", ", _serverAddresses.Addresses)}'. " + $"Binding to endpoints added to {nameof(HttpSysOptions.UrlPrefixes)} instead."); _serverAddresses.Addresses.Clear(); } } else if (hostingUrlsPresent) { UpdateUrlPrefixes(serverAddressCopy); } else if (Listener.RequestQueue.Created) { _logger.LogDebug(LoggerEventIds.BindingToDefault, $"No listening endpoints were configured. Binding to {Constants.DefaultServerAddress} by default."); Listener.Options.UrlPrefixes.Add(Constants.DefaultServerAddress); } // else // Attaching to an existing queue, don't add a default. // Can't call Start twice Debug.Assert(Application == null); Debug.Assert(application != null); Application = new ApplicationWrapper <TContext>(application); Listener.Start(); // Update server addresses after we start listening as port 0 // needs to be selected at the point of binding. foreach (var prefix in _options.UrlPrefixes) { _serverAddresses.Addresses.Add(prefix.FullPrefix); } // Dispatch to get off the SynchronizationContext and use UnsafeQueueUserWorkItem to avoid capturing the ExecutionContext ThreadPool.UnsafeQueueUserWorkItem(state => state.ActivateRequestProcessingLimits(), this, preferLocal: false); return(Task.CompletedTask); }
private async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> httpApplication) { try { KestrelEventSource.Log.ConnectionStart(this); AdaptedPipeline adaptedPipeline = null; var adaptedPipelineTask = Task.CompletedTask; // _adaptedTransport must be set prior to adding the connection to the manager in order // to allow the connection to be aported prior to protocol selection. _adaptedTransport = _context.Transport; var application = _context.Application; if (_context.ConnectionAdapters.Count > 0) { adaptedPipeline = new AdaptedPipeline(_adaptedTransport, application, new Pipe(AdaptedInputPipeOptions), new Pipe(AdaptedOutputPipeOptions)); _adaptedTransport = adaptedPipeline; } // Do this before the first await so we don't yield control to the transport until we've // added the connection to the connection manager _context.ServiceContext.ConnectionManager.AddConnection(_context.HttpConnectionId, this); _lastTimestamp = _context.ServiceContext.SystemClock.UtcNow.Ticks; _context.ConnectionFeatures.Set <IConnectionTimeoutFeature>(this); if (adaptedPipeline != null) { // Stream can be null here and run async will close the connection in that case var stream = await ApplyConnectionAdaptersAsync(); adaptedPipelineTask = adaptedPipeline.RunAsync(stream); } IRequestProcessor requestProcessor = null; lock (_protocolSelectionLock) { // Ensure that the connection hasn't already been stopped. if (_protocolSelectionState == ProtocolSelectionState.Initializing) { switch (SelectProtocol()) { case HttpProtocols.Http1: // _http1Connection must be initialized before adding the connection to the connection manager requestProcessor = _http1Connection = CreateHttp1Connection(_adaptedTransport, application); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.Http2: // _http2Connection must be initialized before yielding control to the transport thread, // to prevent a race condition where _http2Connection.Abort() is called just as // _http2Connection is about to be initialized. requestProcessor = CreateHttp2Connection(_adaptedTransport, application); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.None: // An error was already logged in SelectProtocol(), but we should close the connection. Abort(ex: null); break; default: // SelectProtocol() only returns Http1, Http2 or None. throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None."); } _requestProcessor = requestProcessor; } } if (requestProcessor != null) { await requestProcessor.ProcessRequestsAsync(httpApplication); } await adaptedPipelineTask; await _socketClosedTcs.Task; } catch (Exception ex) { Log.LogCritical(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}."); } finally { _context.ServiceContext.ConnectionManager.RemoveConnection(_context.HttpConnectionId); DisposeAdaptedConnections(); if (_http1Connection?.IsUpgraded == true) { _context.ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne(); } KestrelEventSource.Log.ConnectionStop(this); } }
public ApplicationWrapper(IHttpApplication <TContext> application) { _application = application; }
public async Task ProcessRequestAsync <TContext>(IHttpApplication <TContext> application) where TContext : notnull { Exception?error = null; try { while (_isClosed == 0) { var result = await Input.ReadAsync(); var readableBuffer = result.Buffer; var consumed = readableBuffer.Start; var examined = readableBuffer.End; try { if (!readableBuffer.IsEmpty) { while (Http3FrameReader.TryReadFrame(ref readableBuffer, _incomingFrame, out var framePayload)) { Log.Http3FrameReceived(ConnectionId, _streamIdFeature.StreamId, _incomingFrame); consumed = examined = framePayload.End; await ProcessHttp3Stream(application, framePayload, result.IsCompleted&& readableBuffer.IsEmpty); } } if (result.IsCompleted) { await OnEndStreamReceived(); return; } } finally { Input.AdvanceTo(consumed, examined); } } } catch (Http3StreamErrorException ex) { error = ex; Abort(new ConnectionAbortedException(ex.Message, ex), ex.ErrorCode); } catch (Http3ConnectionErrorException ex) { error = ex; _errorCodeFeature.Error = (long)ex.ErrorCode; _context.StreamLifetimeHandler.OnStreamConnectionError(ex); } catch (ConnectionAbortedException ex) { error = ex; } catch (ConnectionResetException ex) { error = ex; var resolvedErrorCode = _errorCodeFeature.Error >= 0 ? _errorCodeFeature.Error : 0; AbortCore(new IOException(CoreStrings.HttpStreamResetByClient, ex), (Http3ErrorCode)resolvedErrorCode); } catch (Exception ex) { error = ex; Log.LogWarning(0, ex, "Stream threw an unexpected exception."); } finally { var streamError = error as ConnectionAbortedException ?? new ConnectionAbortedException("The stream has completed.", error !); await Input.CompleteAsync(); var appCompleted = _appCompleted?.Task ?? Task.CompletedTask; if (!appCompleted.IsCompletedSuccessfully) { // At this point in the stream's read-side is complete. However, with HTTP/3 // the write-side of the stream can still be aborted by the client on request // aborted. // // To get notification of request aborted we register to connection closed // token. It will notify this type that the client has aborted the request // and Kestrel will complete pipes and cancel the RequestAborted token. // // Only subscribe to this event after the stream's read-side is complete to // avoid interactions between reading that is in-progress and an abort. // This means while reading, read-side abort will handle getting abort notifications. // // We don't need to hang on to the CancellationTokenRegistration from register. // The CTS is cleaned up in StreamContext.DisposeAsync. // // TODO: Consider a better way to provide this notification. For perf we want to // make the ConnectionClosed CTS pay-for-play, and change this event to use // something that is more lightweight than a CTS. _context.StreamContext.ConnectionClosed.Register(static s =>
private async Task ProcessHeadersFrameAsync <TContext>(IHttpApplication <TContext> application) { if (_currentHeadersStream != null) { throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorHeadersInterleaved(_incomingFrame.Type, _incomingFrame.StreamId, _currentHeadersStream.StreamId), Http2ErrorCode.PROTOCOL_ERROR); } if (_incomingFrame.StreamId == 0) { throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdZero(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR); } if (_incomingFrame.HeadersHasPadding && _incomingFrame.HeadersPadLength >= _incomingFrame.Length) { throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorPaddingTooLong(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR); } if (_incomingFrame.HeadersHasPriority && _incomingFrame.HeadersStreamDependency == _incomingFrame.StreamId) { throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamSelfDependency(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.PROTOCOL_ERROR); } if (_streams.TryGetValue(_incomingFrame.StreamId, out var stream)) { // http://httpwg.org/specs/rfc7540.html#rfc.section.5.1 // // ...an endpoint that receives any frames after receiving a frame with the // END_STREAM flag set MUST treat that as a connection error (Section 5.4.1) // of type STREAM_CLOSED, unless the frame is permitted as described below. // // (The allowed frame types after END_STREAM are WINDOW_UPDATE, RST_STREAM and PRIORITY) if (stream.EndStreamReceived) { throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamHalfClosedRemote(_incomingFrame.Type, stream.StreamId), Http2ErrorCode.STREAM_CLOSED); } // This is the last chance for the client to send END_STREAM if ((_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_STREAM) == 0) { throw new Http2ConnectionErrorException(CoreStrings.Http2ErrorHeadersWithTrailersNoEndStream, Http2ErrorCode.PROTOCOL_ERROR); } // Since we found an active stream, this HEADERS frame contains trailers _currentHeadersStream = stream; _requestHeaderParsingState = RequestHeaderParsingState.Trailers; var endHeaders = (_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_HEADERS) == Http2HeadersFrameFlags.END_HEADERS; await DecodeTrailersAsync(endHeaders, _incomingFrame.HeadersPayload); } else if (_incomingFrame.StreamId <= _highestOpenedStreamId) { // http://httpwg.org/specs/rfc7540.html#rfc.section.5.1.1 // // The first use of a new stream identifier implicitly closes all streams in the "idle" // state that might have been initiated by that peer with a lower-valued stream identifier. // // If we couldn't find the stream, it was previously closed (either implicitly or with // END_STREAM or RST_STREAM). throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamClosed(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.STREAM_CLOSED); } else { // Start a new stream _currentHeadersStream = new Http2Stream(new Http2StreamContext { ConnectionId = ConnectionId, StreamId = _incomingFrame.StreamId, ServiceContext = _context.ServiceContext, ConnectionFeatures = _context.ConnectionFeatures, MemoryPool = _context.MemoryPool, LocalEndPoint = _context.LocalEndPoint, RemoteEndPoint = _context.RemoteEndPoint, StreamLifetimeHandler = this, FrameWriter = _frameWriter }); if ((_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_STREAM) == Http2HeadersFrameFlags.END_STREAM) { await _currentHeadersStream.OnDataAsync(Constants.EmptyData, endStream : true); } _currentHeadersStream.Reset(); var endHeaders = (_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_HEADERS) == Http2HeadersFrameFlags.END_HEADERS; await DecodeHeadersAsync(application, endHeaders, _incomingFrame.HeadersPayload); } }
public async Task StartAsync <TContext>(IHttpApplication <TContext> application, CancellationToken cancellationToken) where TContext : notnull { try { ValidateOptions(); if (_hasStarted) { // The server has already started and/or has not been cleaned up yet throw new InvalidOperationException(CoreStrings.ServerAlreadyStarted); } _hasStarted = true; ServiceContext.Heartbeat?.Start(); async Task OnBind(ListenOptions options, CancellationToken onBindCancellationToken) { var hasHttp1 = options.Protocols.HasFlag(HttpProtocols.Http1); var hasHttp2 = options.Protocols.HasFlag(HttpProtocols.Http2); var hasHttp3 = options.Protocols.HasFlag(HttpProtocols.Http3); var hasTls = options.IsTls; // Filter out invalid combinations. if (!hasTls) { // Http/1 without TLS, no-op HTTP/2 and 3. if (hasHttp1) { hasHttp2 = false; hasHttp3 = false; } // Http/3 requires TLS. Note we only let it fall back to HTTP/1, not HTTP/2 else if (hasHttp3) { throw new InvalidOperationException("HTTP/3 requires HTTPS."); } } // Quic isn't registered if it's not supported, throw if we can't fall back to 1 or 2 if (hasHttp3 && _multiplexedTransportFactory is null && !(hasHttp1 || hasHttp2)) { throw new InvalidOperationException("This platform doesn't support QUIC or HTTP/3."); } // Disable adding alt-svc header if endpoint has configured not to or there is no // multiplexed transport factory, which happens if QUIC isn't supported. var addAltSvcHeader = !options.DisableAltSvcHeader && _multiplexedTransportFactory != null; // Add the HTTP middleware as the terminal connection middleware if (hasHttp1 || hasHttp2 || options.Protocols == HttpProtocols.None) // TODO a test fails because it doesn't throw an exception in the right place // when there is no HttpProtocols in KestrelServer, can we remove/change the test? { if (_transportFactory is null) { throw new InvalidOperationException($"Cannot start HTTP/1.x or HTTP/2 server if no {nameof(IConnectionListenerFactory)} is registered."); } options.UseHttpServer(ServiceContext, application, options.Protocols, addAltSvcHeader); var connectionDelegate = options.Build(); // Add the connection limit middleware connectionDelegate = EnforceConnectionLimit(connectionDelegate, Options.Limits.MaxConcurrentConnections, Trace); options.EndPoint = await _transportManager.BindAsync(options.EndPoint, connectionDelegate, options.EndpointConfig, onBindCancellationToken).ConfigureAwait(false); } if (hasHttp3 && _multiplexedTransportFactory is not null) { options.UseHttp3Server(ServiceContext, application, options.Protocols, addAltSvcHeader); var multiplexedConnectionDelegate = ((IMultiplexedConnectionBuilder)options).Build(); // Add the connection limit middleware multiplexedConnectionDelegate = EnforceConnectionLimit(multiplexedConnectionDelegate, Options.Limits.MaxConcurrentConnections, Trace); options.EndPoint = await _transportManager.BindAsync(options.EndPoint, multiplexedConnectionDelegate, options, onBindCancellationToken).ConfigureAwait(false); } } AddressBindContext = new AddressBindContext(_serverAddresses, Options, Trace, OnBind); await BindAsync(cancellationToken).ConfigureAwait(false); } catch { // Don't log the error https://github.com/dotnet/aspnetcore/issues/29801 Dispose(); throw; } // Register the options with the event source so it can be logged (if necessary) KestrelEventSource.Log.AddServerOptions(Options); }
public TestFrame(IHttpApplication <TContext> application, FrameContext context) : base(application, context) { }
public Task StartAsync <TContext>(IHttpApplication <TContext> application, CancellationToken cancellationToken) { SetHttpHandler(new Action <dynamic, dynamic>(async(req, res) => { var resourceName = GetCurrentResourceName(); var bodyStream = (req.method != "GET" && req.method != "HEAD") ? await GetBodyStream(req) : Stream.Null; var oldSc = SynchronizationContext.Current; SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); var cts = new CancellationTokenSource(); req.setCancelHandler(new Action(() => { cts.Cancel(); })); await Task.Factory.StartNew(async() => { var owinEnvironment = new Dictionary <string, object>(); owinEnvironment["owin.RequestBody"] = bodyStream; var headers = new HeaderDictionary(); foreach (var headerPair in req.headers) { headers.Add(headerPair.Key, new string[] { headerPair.Value.ToString() }); } owinEnvironment["owin.RequestHeaders"] = headers; owinEnvironment["owin.RequestMethod"] = req.method; owinEnvironment["owin.RequestPath"] = req.path.Split('?')[0]; owinEnvironment["owin.RequestPathBase"] = "/" + resourceName; owinEnvironment["owin.RequestProtocol"] = "HTTP/1.0"; owinEnvironment["owin.RequestQueryString"] = (req.path.Contains('?')) ? req.path.Split('?', 2)[1] : ""; owinEnvironment["owin.RequestScheme"] = "http"; var outStream = new HttpOutStream(owinEnvironment, res); owinEnvironment["owin.ResponseBody"] = outStream; var outHeaders = new Dictionary <string, string[]>(); owinEnvironment["owin.ResponseHeaders"] = outHeaders; owinEnvironment["owin.CallCancelled"] = cts.Token; owinEnvironment["owin.Version"] = "1.0"; var ofc = new FxOwinFeatureCollection(owinEnvironment); var context = application.CreateContext(new FeatureCollection(ofc)); try { await application.ProcessRequestAsync(context); await ofc.InvokeOnStarting(); } catch (Exception ex) { Debug.WriteLine($"Exception while handling request. {ex}"); await ofc.InvokeOnCompleted(); application.DisposeContext(context, ex); var errorText = Encoding.UTF8.GetBytes("Error."); owinEnvironment["owin.ResponseStatusCode"] = 500; await outStream.WriteAsync(errorText, 0, errorText.Length); await outStream.EndStream(); return; } application.DisposeContext(context, null); await outStream.EndStream(); await ofc.InvokeOnCompleted(); }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); SynchronizationContext.SetSynchronizationContext(oldSc); })); return(Task.CompletedTask); }
public RequestContext(IHttpApplication <TContext> application, MessagePump messagePump, HttpSysListener server, uint?bufferSize, ulong requestId) : base(server, bufferSize, requestId) { _application = application; _messagePump = messagePump; }
internal RequestState(HttpRequestMessage request, PathString pathBase, IHttpApplication <Context> application) { _request = request; _application = application; _responseTcs = new TaskCompletionSource <HttpResponseMessage>(); _requestAbortedSource = new CancellationTokenSource(); _pipelineFinished = false; if (request.RequestUri.IsDefaultPort) { request.Headers.Host = request.RequestUri.Host; } else { request.Headers.Host = request.RequestUri.GetComponents(UriComponents.HostAndPort, UriFormat.UriEscaped); } var contextFeatures = new FeatureCollection(); var requestFeature = new RequestFeature(); contextFeatures.Set <IHttpRequestFeature>(requestFeature); _responseFeature = new ResponseFeature(); contextFeatures.Set <IHttpResponseFeature>(_responseFeature); var requestLifetimeFeature = new HttpRequestLifetimeFeature(); contextFeatures.Set <IHttpRequestLifetimeFeature>(requestLifetimeFeature); requestFeature.Protocol = "HTTP/" + request.Version.ToString(fieldCount: 2); requestFeature.Scheme = request.RequestUri.Scheme; requestFeature.Method = request.Method.ToString(); var fullPath = PathString.FromUriComponent(request.RequestUri); PathString remainder; if (fullPath.StartsWithSegments(pathBase, out remainder)) { requestFeature.PathBase = pathBase.Value; requestFeature.Path = remainder.Value; } else { requestFeature.PathBase = string.Empty; requestFeature.Path = fullPath.Value; } requestFeature.QueryString = QueryString.FromUriComponent(request.RequestUri).Value; foreach (var header in request.Headers) { requestFeature.Headers.Append(header.Key, header.Value.ToArray()); } var requestContent = request.Content; if (requestContent != null) { foreach (var header in request.Content.Headers) { requestFeature.Headers.Append(header.Key, header.Value.ToArray()); } } _responseStream = new ResponseStream(ReturnResponseMessageAsync, AbortRequest); _responseFeature.Body = _responseStream; _responseFeature.StatusCode = 200; requestLifetimeFeature.RequestAborted = _requestAbortedSource.Token; Context = application.CreateContext(contextFeatures); }
public void Start <TContext>(IHttpApplication <TContext> application) { this.Start(application, null); }
public void Start <TContext>(IHttpApplication <TContext> application) { _executor = new Executor <TContext>(application); }
public async Task StartAsync <TContext>(IHttpApplication <TContext> application, CancellationToken cancellationToken) where TContext : notnull { try { if (!BitConverter.IsLittleEndian) { throw new PlatformNotSupportedException(CoreStrings.BigEndianNotSupported); } ValidateOptions(); if (_hasStarted) { // The server has already started and/or has not been cleaned up yet throw new InvalidOperationException(CoreStrings.ServerAlreadyStarted); } _hasStarted = true; ServiceContext.Heartbeat?.Start(); async Task OnBind(ListenOptions options, CancellationToken onBindCancellationToken) { // INVESTIGATE: For some reason, MsQuic needs to bind before // sockets for it to successfully listen. It also seems racy. if ((options.Protocols & HttpProtocols.Http3) == HttpProtocols.Http3) { if (_multiplexedTransportFactory is null) { throw new InvalidOperationException($"Cannot start HTTP/3 server if no {nameof(IMultiplexedConnectionListenerFactory)} is registered."); } options.UseHttp3Server(ServiceContext, application); var multiplexedConnectionDelegate = ((IMultiplexedConnectionBuilder)options).Build(); // Add the connection limit middleware multiplexedConnectionDelegate = EnforceConnectionLimit(multiplexedConnectionDelegate, Options.Limits.MaxConcurrentConnections, Trace); options.EndPoint = await _transportManager.BindAsync(options.EndPoint, multiplexedConnectionDelegate, options, onBindCancellationToken).ConfigureAwait(false); } // Add the HTTP middleware as the terminal connection middleware if ((options.Protocols & HttpProtocols.Http1) == HttpProtocols.Http1 || (options.Protocols & HttpProtocols.Http2) == HttpProtocols.Http2 || options.Protocols == HttpProtocols.None) // TODO a test fails because it doesn't throw an exception in the right place // when there is no HttpProtocols in KestrelServer, can we remove/change the test? { if (_transportFactory is null) { throw new InvalidOperationException($"Cannot start HTTP/1.x or HTTP/2 server if no {nameof(IConnectionListenerFactory)} is registered."); } options.UseHttpServer(ServiceContext, application, options.Protocols); var connectionDelegate = options.Build(); // Add the connection limit middleware connectionDelegate = EnforceConnectionLimit(connectionDelegate, Options.Limits.MaxConcurrentConnections, Trace); options.EndPoint = await _transportManager.BindAsync(options.EndPoint, connectionDelegate, options.EndpointConfig, onBindCancellationToken).ConfigureAwait(false); } } AddressBindContext = new AddressBindContext(_serverAddresses, Options, Trace, OnBind); await BindAsync(cancellationToken).ConfigureAwait(false); } catch { // Don't log the error https://github.com/dotnet/aspnetcore/issues/29801 Dispose(); throw; } }
public async Task ProcessRequestAsync <TContext>(IHttpApplication <TContext> application) where TContext : notnull { Exception?error = null; try { while (_isClosed == 0) { var result = await Input.ReadAsync(); var readableBuffer = result.Buffer; var consumed = readableBuffer.Start; var examined = readableBuffer.End; try { if (!readableBuffer.IsEmpty) { while (Http3FrameReader.TryReadFrame(ref readableBuffer, _incomingFrame, out var framePayload)) { Log.Http3FrameReceived(ConnectionId, _streamIdFeature.StreamId, _incomingFrame); consumed = examined = framePayload.End; await ProcessHttp3Stream(application, framePayload); } } if (result.IsCompleted) { await OnEndStreamReceived(); return; } } finally { Input.AdvanceTo(consumed, examined); } } } // catch ConnectionResetException here? catch (Http3StreamErrorException ex) { error = ex; Abort(new ConnectionAbortedException(ex.Message, ex), ex.ErrorCode); } catch (Http3ConnectionErrorException ex) { error = ex; _errorCodeFeature.Error = (long)ex.ErrorCode; Log.Http3ConnectionError(_http3Connection.ConnectionId, ex); _http3Connection.Abort(new ConnectionAbortedException(ex.Message, ex), ex.ErrorCode); // TODO: HTTP/3 stream will be aborted by connection. Check this is correct. } catch (Exception ex) { error = ex; Log.LogWarning(0, ex, "Stream threw an unexpected exception."); } finally { var streamError = error as ConnectionAbortedException ?? new ConnectionAbortedException("The stream has completed.", error !); await Input.CompleteAsync(); // Make sure application func is completed before completing writer. if (_appCompleted != null) { await _appCompleted.Task; } try { await _frameWriter.CompleteAsync(); } catch { Abort(streamError, Http3ErrorCode.ProtocolError); throw; } finally { await _context.StreamContext.DisposeAsync(); _http3Connection.RemoveStream(_streamIdFeature.StreamId); } } }
public IISHttpContextOfT(MemoryPool <byte> memoryPool, IHttpApplication <TContext> application, IntPtr pInProcessHandler, IISServerOptions options, IISHttpServer server, ILogger logger) : base(memoryPool, pInProcessHandler, options, server, logger) { _application = application; }
private Task ProcessHttp3Stream <TContext>(IHttpApplication <TContext> application, in ReadOnlySequence <byte> payload) where TContext : notnull
public Task StartAsync <TContext>(IHttpApplication <TContext> application, CancellationToken cancellationToken) { if (application == null) { throw new ArgumentNullException(nameof(application)); } var hostingUrlsPresent = _serverAddresses.Addresses.Count > 0; if (_serverAddresses.PreferHostingUrls && hostingUrlsPresent) { if (_options.UrlPrefixes.Count > 0) { LogHelper.LogWarning(_logger, $"Overriding endpoints added to {nameof(AzureRelayOptions.UrlPrefixes)} since {nameof(IServerAddressesFeature.PreferHostingUrls)} is set to true." + $" Binding to address(es) '{string.Join(", ", _serverAddresses.Addresses)}' instead. "); Listener.Options.UrlPrefixes.Clear(); } foreach (var value in _serverAddresses.Addresses) { Listener.Options.UrlPrefixes.Add(UrlPrefix.Create(value)); } } else if (_options.UrlPrefixes.Count > 0) { if (hostingUrlsPresent) { LogHelper.LogWarning(_logger, $"Overriding address(es) '{string.Join(", ", _serverAddresses.Addresses)}'. " + $"Binding to endpoints added to {nameof(AzureRelayOptions.UrlPrefixes)} instead."); _serverAddresses.Addresses.Clear(); } foreach (var prefix in _options.UrlPrefixes) { _serverAddresses.Addresses.Add(prefix.FullPrefix); } } else if (hostingUrlsPresent) { foreach (var value in _serverAddresses.Addresses) { Listener.Options.UrlPrefixes.Add(UrlPrefix.Create(value)); } } else { LogHelper.LogDebug(_logger, $"No listening endpoints were configured."); throw new InvalidOperationException("No listening endpoints were configured."); } // Can't call Start twice Contract.Assert(_application == null); Contract.Assert(application != null); _application = new ApplicationWrapper <TContext>(application); Listener.Start(); return(Task.CompletedTask); }
public Task StartAsync <TContext>(IHttpApplication <TContext> application, CancellationToken cancellationToken) { if (application == null) { throw new ArgumentNullException(nameof(application)); } var hostingUrlsPresent = _serverAddresses.Addresses.Count > 0; if (_serverAddresses.PreferHostingUrls && hostingUrlsPresent) { if (_options.UrlPrefixes.Count > 0) { LogHelper.LogWarning(_logger, $"Overriding endpoints added to {nameof(HttpSysOptions.UrlPrefixes)} since {nameof(IServerAddressesFeature.PreferHostingUrls)} is set to true." + $" Binding to address(es) '{string.Join(", ", _serverAddresses.Addresses)}' instead. "); Listener.Options.UrlPrefixes.Clear(); } foreach (var value in _serverAddresses.Addresses) { Listener.Options.UrlPrefixes.Add(value); } } else if (_options.UrlPrefixes.Count > 0) { if (hostingUrlsPresent) { LogHelper.LogWarning(_logger, $"Overriding address(es) '{string.Join(", ", _serverAddresses.Addresses)}'. " + $"Binding to endpoints added to {nameof(HttpSysOptions.UrlPrefixes)} instead."); _serverAddresses.Addresses.Clear(); } foreach (var prefix in _options.UrlPrefixes) { _serverAddresses.Addresses.Add(prefix.FullPrefix); } } else if (hostingUrlsPresent) { foreach (var value in _serverAddresses.Addresses) { Listener.Options.UrlPrefixes.Add(value); } } else if (Listener.RequestQueue.Created) { LogHelper.LogDebug(_logger, $"No listening endpoints were configured. Binding to {Constants.DefaultServerAddress} by default."); _serverAddresses.Addresses.Add(Constants.DefaultServerAddress); Listener.Options.UrlPrefixes.Add(Constants.DefaultServerAddress); } // else // Attaching to an existing queue, don't add a default. // Can't call Start twice Contract.Assert(_application == null); Contract.Assert(application != null); _application = new ApplicationWrapper <TContext>(application); Listener.Start(); ActivateRequestProcessingLimits(); return(Task.CompletedTask); }
public Task StartRequestProcessing <TContext>(IHttpApplication <TContext> application) { return(_lifetimeTask = ProcessRequestsAsync(application)); }
public Task StartAsync <TContext>(IHttpApplication <TContext> application, CancellationToken cancellationToken) { return(Task.CompletedTask); }
public Task StartAsync <TContext>(IHttpApplication <TContext> application, CancellationToken cancellationToken) { this.Application = new ApplicationWrapper <TContext>(application); return(Task.CompletedTask); }
public Task StartAsync <TContext>(IHttpApplication <TContext> application, CancellationToken cancellationToken) { RequestDelegate = ctx => throw new NotSupportedException(); return(Task.CompletedTask); }
public CompressMiddleware(IHttpApplication app) { _app = app; }
public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> httpApplication) { try { // Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs. _timeoutControl.Initialize(_systemClock.UtcNowTicks); IRequestProcessor requestProcessor = null; switch (SelectProtocol()) { case HttpProtocols.Http1: // _http1Connection must be initialized before adding the connection to the connection manager requestProcessor = _http1Connection = new Http1Connection <TContext>(_context); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.Http2: // _http2Connection must be initialized before yielding control to the transport thread, // to prevent a race condition where _http2Connection.Abort() is called just as // _http2Connection is about to be initialized. requestProcessor = new Http2Connection(_context); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.None: // An error was already logged in SelectProtocol(), but we should close the connection. break; default: // SelectProtocol() only returns Http1, Http2 or None. throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None."); } _requestProcessor = requestProcessor; if (requestProcessor != null) { var connectionHeartbeatFeature = _context.ConnectionFeatures.Get <IConnectionHeartbeatFeature>(); var connectionLifetimeNotificationFeature = _context.ConnectionFeatures.Get <IConnectionLifetimeNotificationFeature>(); // These features should never be null in Kestrel itself, if this middleware is ever refactored to run outside of kestrel, // we'll need to handle these missing. Debug.Assert(connectionHeartbeatFeature != null, nameof(IConnectionHeartbeatFeature) + " is missing!"); Debug.Assert(connectionLifetimeNotificationFeature != null, nameof(IConnectionLifetimeNotificationFeature) + " is missing!"); // Register the various callbacks once we're going to start processing requests // The heart beat for various timeouts connectionHeartbeatFeature?.OnHeartbeat(state => ((HttpConnection)state).Tick(), this); // Register for graceful shutdown of the server using var shutdownRegistration = connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this); // Register for connection close using var closedRegistration = _context.ConnectionContext.ConnectionClosed.Register(state => ((HttpConnection)state).OnConnectionClosed(), this); await requestProcessor.ProcessRequestsAsync(httpApplication); } } catch (Exception ex) { Log.LogCritical(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}."); } finally { if (_http1Connection?.IsUpgraded == true) { _context.ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne(); } } }
public static IHttpServer CreateEmbedHttpServer(IHttpApplication app, IHttpApplication errApp, bool enableIPv4, bool enableIPv6, bool bindAny, int port, int backlog) { return new Embed.HttpServer (app, errApp, enableIPv4, enableIPv6, bindAny, port, backlog); }
public Http3ControlStream(IHttpApplication <TContext> application, Http3StreamContext context, long?headerType) : base(context, headerType) { _application = application; }
internal RequestState(HttpRequestMessage request, PathString pathBase, IHttpApplication<Context> application) { _request = request; _application = application; _responseTcs = new TaskCompletionSource<HttpResponseMessage>(); _requestAbortedSource = new CancellationTokenSource(); _pipelineFinished = false; if (request.RequestUri.IsDefaultPort) { request.Headers.Host = request.RequestUri.Host; } else { request.Headers.Host = request.RequestUri.GetComponents(UriComponents.HostAndPort, UriFormat.UriEscaped); } Context = application.CreateContext(new FeatureCollection()); var httpContext = Context.HttpContext; httpContext.Features.Set<IHttpRequestFeature>(new RequestFeature()); _responseFeature = new ResponseFeature(); httpContext.Features.Set<IHttpResponseFeature>(_responseFeature); var serverRequest = httpContext.Request; serverRequest.Protocol = "HTTP/" + request.Version.ToString(2); serverRequest.Scheme = request.RequestUri.Scheme; serverRequest.Method = request.Method.ToString(); var fullPath = PathString.FromUriComponent(request.RequestUri); PathString remainder; if (fullPath.StartsWithSegments(pathBase, out remainder)) { serverRequest.PathBase = pathBase; serverRequest.Path = remainder; } else { serverRequest.PathBase = PathString.Empty; serverRequest.Path = fullPath; } serverRequest.QueryString = QueryString.FromUriComponent(request.RequestUri); foreach (var header in request.Headers) { serverRequest.Headers.Append(header.Key, header.Value.ToArray()); } var requestContent = request.Content; if (requestContent != null) { foreach (var header in request.Content.Headers) { serverRequest.Headers.Append(header.Key, header.Value.ToArray()); } } _responseStream = new ResponseStream(ReturnResponseMessage, AbortRequest); httpContext.Response.Body = _responseStream; httpContext.Response.StatusCode = 200; httpContext.RequestAborted = _requestAbortedSource.Token; }
public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> httpApplication) { try { AdaptedPipeline adaptedPipeline = null; var adaptedPipelineTask = Task.CompletedTask; // _adaptedTransport must be set prior to wiring up callbacks // to allow the connection to be aborted prior to protocol selection. _adaptedTransport = _context.Transport; if (_context.ConnectionAdapters.Count > 0) { adaptedPipeline = new AdaptedPipeline(_adaptedTransport, new Pipe(AdaptedInputPipeOptions), new Pipe(AdaptedOutputPipeOptions), Log); _adaptedTransport = adaptedPipeline; } // This feature should never be null in Kestrel var connectionHeartbeatFeature = _context.ConnectionFeatures.Get <IConnectionHeartbeatFeature>(); Debug.Assert(connectionHeartbeatFeature != null, nameof(IConnectionHeartbeatFeature) + " is missing!"); connectionHeartbeatFeature?.OnHeartbeat(state => ((HttpConnection)state).Tick(), this); var connectionLifetimeNotificationFeature = _context.ConnectionFeatures.Get <IConnectionLifetimeNotificationFeature>(); Debug.Assert(connectionLifetimeNotificationFeature != null, nameof(IConnectionLifetimeNotificationFeature) + " is missing!"); using (connectionLifetimeNotificationFeature?.ConnectionClosedRequested.Register(state => ((HttpConnection)state).StopProcessingNextRequest(), this)) { // Ensure TimeoutControl._lastTimestamp is initialized before anything that could set timeouts runs. _timeoutControl.Initialize(_systemClock.UtcNow); _context.ConnectionFeatures.Set <IConnectionTimeoutFeature>(_timeoutControl); if (adaptedPipeline != null) { // Stream can be null here and run async will close the connection in that case var stream = await ApplyConnectionAdaptersAsync(); adaptedPipelineTask = adaptedPipeline.RunAsync(stream); } IRequestProcessor requestProcessor = null; lock (_protocolSelectionLock) { // Ensure that the connection hasn't already been stopped. if (_protocolSelectionState == ProtocolSelectionState.Initializing) { var derivedContext = CreateDerivedContext(_adaptedTransport); switch (SelectProtocol()) { case HttpProtocols.Http1: // _http1Connection must be initialized before adding the connection to the connection manager requestProcessor = _http1Connection = new Http1Connection(derivedContext); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.Http2: // _http2Connection must be initialized before yielding control to the transport thread, // to prevent a race condition where _http2Connection.Abort() is called just as // _http2Connection is about to be initialized. requestProcessor = new Http2Connection(derivedContext); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.None: // An error was already logged in SelectProtocol(), but we should close the connection. Abort(ex: null); break; default: // SelectProtocol() only returns Http1, Http2 or None. throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None."); } _requestProcessor = requestProcessor; } } _context.Transport.Input.OnWriterCompleted( (_, state) => ((HttpConnection)state).OnInputOrOutputCompleted(), this); _context.Transport.Output.OnReaderCompleted( (_, state) => ((HttpConnection)state).OnInputOrOutputCompleted(), this); if (requestProcessor != null) { await requestProcessor.ProcessRequestsAsync(httpApplication); } await adaptedPipelineTask; } } catch (Exception ex) { Log.LogCritical(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}."); } finally { DisposeAdaptedConnections(); if (_http1Connection?.IsUpgraded == true) { _context.ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne(); } } }
public Http3ConnectionMiddleware(ServiceContext serviceContext, IHttpApplication <TContext> application) { _serviceContext = serviceContext; _application = application; }
public async Task ProcessRequestsAsync <TContext>(IHttpApplication <TContext> application) { Exception error = null; var errorCode = Http2ErrorCode.NO_ERROR; try { while (!_stopping) { var result = await Input.ReadAsync(); var readableBuffer = result.Buffer; var consumed = readableBuffer.Start; var examined = readableBuffer.End; try { if (!readableBuffer.IsEmpty) { if (ParsePreface(readableBuffer, out consumed, out examined)) { break; } } else if (result.IsCompleted) { return; } } finally { Input.Advance(consumed, examined); } } if (!_stopping) { await _frameWriter.WriteSettingsAsync(_serverSettings); } while (!_stopping) { var result = await Input.ReadAsync(); var readableBuffer = result.Buffer; var consumed = readableBuffer.Start; var examined = readableBuffer.End; try { if (!readableBuffer.IsEmpty) { if (Http2FrameReader.ReadFrame(readableBuffer, _incomingFrame, out consumed, out examined)) { Log.LogTrace($"Connection id {ConnectionId} received {_incomingFrame.Type} frame with flags 0x{_incomingFrame.Flags:x} and length {_incomingFrame.Length} for stream ID {_incomingFrame.StreamId}"); await ProcessFrameAsync <TContext>(application); } } else if (result.IsCompleted) { return; } } finally { Input.Advance(consumed, examined); } } } catch (ConnectionResetException ex) { // Don't log ECONNRESET errors when there are no active streams on the connection. Browsers like IE will reset connections regularly. if (_streams.Count > 0) { Log.RequestProcessingError(ConnectionId, ex); } error = ex; } catch (Http2ConnectionErrorException ex) { Log.Http2ConnectionError(ConnectionId, ex); error = ex; errorCode = ex.ErrorCode; } catch (HPackDecodingException ex) { Log.HPackDecodingError(ConnectionId, _currentHeadersStream.StreamId, ex); error = ex; errorCode = Http2ErrorCode.COMPRESSION_ERROR; } catch (Exception ex) { error = ex; errorCode = Http2ErrorCode.INTERNAL_ERROR; throw; } finally { try { foreach (var stream in _streams.Values) { stream.Abort(error); } await _frameWriter.WriteGoAwayAsync(_highestOpenedStreamId, errorCode); } finally { Input.Complete(); _frameWriter.Abort(ex: null); } } }
public RequestState(Uri uri, PathString pathBase, CancellationToken cancellationToken, IHttpApplication<Context> application) { _clientWebSocketTcs = new TaskCompletionSource<WebSocket>(); _application = application; // HttpContext Context = _application.CreateContext(new FeatureCollection()); var httpContext = Context.HttpContext; // Request httpContext.Features.Set<IHttpRequestFeature>(new RequestFeature()); var request = httpContext.Request; request.Protocol = "HTTP/1.1"; var scheme = uri.Scheme; scheme = (scheme == "ws") ? "http" : scheme; scheme = (scheme == "wss") ? "https" : scheme; request.Scheme = scheme; request.Method = "GET"; var fullPath = PathString.FromUriComponent(uri); PathString remainder; if (fullPath.StartsWithSegments(pathBase, out remainder)) { request.PathBase = pathBase; request.Path = remainder; } else { request.PathBase = PathString.Empty; request.Path = fullPath; } request.QueryString = QueryString.FromUriComponent(uri); request.Headers.Add("Connection", new string[] { "Upgrade" }); request.Headers.Add("Upgrade", new string[] { "websocket" }); request.Headers.Add("Sec-WebSocket-Version", new string[] { "13" }); request.Headers.Add("Sec-WebSocket-Key", new string[] { CreateRequestKey() }); request.Body = Stream.Null; // Response httpContext.Features.Set<IHttpResponseFeature>(new ResponseFeature()); var response = httpContext.Response; response.Body = Stream.Null; response.StatusCode = 200; // WebSocket httpContext.Features.Set<IHttpWebSocketFeature>(this); }
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; ValueTask outboundControlStreamTask = default; try { var outboundControlStream = await CreateNewUnidirectionalStreamAsync(application); lock (_sync) { OutboundControlStream = outboundControlStream; } // Don't delay on waiting to send outbound control stream settings. outboundControlStreamTask = ProcessOutboundControlStreamAsync(outboundControlStream); while (_isClosed == 0) { // Don't pass a cancellation token to AcceptAsync. // AcceptAsync will return null if the connection is gracefully shutting down or aborted. var streamContext = await _multiplexedContext.AcceptAsync(); try { if (streamContext == null) { break; } var streamDirectionFeature = streamContext.Features.Get <IStreamDirectionFeature>(); var streamIdFeature = streamContext.Features.Get <IStreamIdFeature>(); Debug.Assert(streamDirectionFeature != null); Debug.Assert(streamIdFeature != null); var httpConnectionContext = new Http3StreamContext( streamContext.ConnectionId, protocols: default, connectionContext: null !, // TODO connection context is null here. Should we set it to anything? _context.ServiceContext, streamContext.Features, _context.MemoryPool, streamContext.LocalEndPoint as IPEndPoint, streamContext.RemoteEndPoint as IPEndPoint, streamContext.Transport, _streamLifetimeHandler, streamContext, _clientSettings, _serverSettings); httpConnectionContext.TimeoutControl = _context.TimeoutControl; if (!streamDirectionFeature.CanWrite) { // Unidirectional stream var stream = new Http3ControlStream <TContext>(application, httpConnectionContext); _streamLifetimeHandler.OnStreamCreated(stream); ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false); } else { // Request stream UpdateHighestStreamId(streamIdFeature.StreamId); var stream = new Http3Stream <TContext>(application, httpConnectionContext); _streamLifetimeHandler.OnStreamCreated(stream); KestrelEventSource.Log.RequestQueuedStart(stream, AspNetCore.Http.HttpProtocol.Http3); ThreadPool.UnsafeQueueUserWorkItem(stream, preferLocal: false); } }