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, options.Protocols); 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 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); }