public async Task ImmediateShutdownDuringOnConnectionAsyncDoesNotCrash() { var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); listenOptions.Use(next => { return(async context => { await tcs.Task; await next(context); }); }); var serviceContext = new TestServiceContext(LoggerFactory); await using (var server = new TestServer(TestApp.EchoApp, serviceContext, listenOptions)) { Task stopTask; using (var connection = server.CreateConnection()) { stopTask = server.StopAsync(); tcs.TrySetResult(); } await stopTask; } }
public async Task ThrowingSynchronousConnectionMiddlewareDoesNotCrashServer(RequestDelegate requestDelegate) { var connectionId = ""; var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); listenOptions.Use(next => context => { connectionId = context.ConnectionId; throw new InvalidOperationException(); }); var serviceContext = new TestServiceContext(LoggerFactory); await using (var server = new TestServer(requestDelegate, serviceContext, listenOptions)) { using (var connection = server.CreateConnection()) { await connection.Send( "POST / HTTP/1.0", "Content-Length: 1000", "\r\n"); await connection.WaitForConnectionClose(); } } Assert.Contains(LogMessages, m => m.Message.Contains("Unhandled exception while processing " + connectionId + ".")); }
public async Task CanReadAndWriteWithRewritingConnectionAdapter(RequestDelegate requestDelegate) { RewritingConnectionMiddleware middleware = null; var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); listenOptions.Use(next => { middleware = new RewritingConnectionMiddleware(next); return(middleware.OnConnectionAsync); }); var serviceContext = new TestServiceContext(LoggerFactory); var sendString = "POST / HTTP/1.0\r\nContent-Length: 12\r\n\r\nHello World?"; await using (var server = new TestServer(requestDelegate, serviceContext, listenOptions)) { using (var connection = server.CreateConnection()) { // "?" changes to "!" await connection.Send(sendString); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Connection: close", $"Date: {serviceContext.DateHeaderValue}", "", "Hello World!"); } } Assert.Equal(sendString.Length, middleware.BytesRead); }
public async Task ImmediateShutdownAfterOnConnectionAsyncDoesNotCrash(RequestDelegate requestDelegate) { var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); listenOptions.Use(next => new AsyncConnectionMiddleware(next).OnConnectionAsync); var serviceContext = new TestServiceContext(LoggerFactory); ThrowOnUngracefulShutdown = false; var stopTask = Task.CompletedTask; await using (var server = new TestServer(requestDelegate, serviceContext, listenOptions)) using (var shutdownCts = new CancellationTokenSource(TestConstants.DefaultTimeout)) { using (var connection = server.CreateConnection()) { // We assume all CI servers are really slow, so we use a 30 second default test timeout // instead of the 5 second default production timeout. If this test is still flaky, // *then* we can consider collecting and investigating memory dumps. stopTask = server.StopAsync(shutdownCts.Token); } await stopTask; } }
public async Task DisposeAsyncAfterReplacingTransportClosesConnection() { var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); var connectionCloseTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var mockDuplexPipe = new MockDuplexPipe(); listenOptions.Use(next => { return(async context => { context.Transport = mockDuplexPipe; await context.DisposeAsync(); await connectionCloseTcs.Task; }); }); var serviceContext = new TestServiceContext(LoggerFactory); await using (var server = new TestServer(TestApp.EmptyApp, serviceContext, listenOptions)) { using (var connection = server.CreateConnection()) { await connection.WaitForConnectionClose(); connectionCloseTcs.SetResult(); } } Assert.False(mockDuplexPipe.WasCompleted); }
/// <summary> /// Configure Kestrel to use HTTPS. This does not use default certificates or other defaults specified via config or /// <see cref="KestrelServerOptions.ConfigureHttpsDefaults(Action{HttpsConnectionAdapterOptions})"/>. /// </summary> /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param> /// <param name="callbackOptions">Options for a per connection callback.</param> /// <returns>The <see cref="ListenOptions"/>.</returns> public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandshakeCallbackOptions callbackOptions) { if (callbackOptions is null) { throw new ArgumentNullException(nameof(callbackOptions)); } if (callbackOptions.OnConnection is null) { throw new ArgumentException($"{nameof(TlsHandshakeCallbackOptions.OnConnection)} must not be null."); } var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance; listenOptions.IsTls = true; listenOptions.HttpsCallbackOptions = callbackOptions; listenOptions.Use(next => { // Set the list of protocols from listen options. // Set it inside Use delegate so Protocols and UseHttps can be called out of order. callbackOptions.HttpProtocols = listenOptions.Protocols; var middleware = new HttpsConnectionMiddleware(next, callbackOptions, loggerFactory); return(middleware.OnConnectionAsync); }); return(listenOptions); }
/// <summary> /// Configure Kestrel to use HTTPS. This does not use default certificates or other defaults specified via config or /// <see cref="KestrelServerOptions.ConfigureHttpsDefaults(Action{HttpsConnectionAdapterOptions})"/>. /// </summary> /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param> /// <param name="callbackOptions">Options for a per connection callback.</param> /// <returns>The <see cref="ListenOptions"/>.</returns> public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandshakeCallbackOptions callbackOptions) { if (callbackOptions is null) { throw new ArgumentNullException(nameof(callbackOptions)); } if (callbackOptions.OnConnection is null) { throw new ArgumentException($"{nameof(TlsHandshakeCallbackOptions.OnConnection)} must not be null."); } if (listenOptions.Protocols.HasFlag(HttpProtocols.Http3)) { throw new NotSupportedException($"{nameof(UseHttps)} with {nameof(TlsHandshakeCallbackOptions)} is not supported with HTTP/3."); } var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance; listenOptions.IsTls = true; listenOptions.Use(next => { // Set the list of protocols from listen options callbackOptions.HttpProtocols = listenOptions.Protocols; var middleware = new HttpsConnectionMiddleware(next, callbackOptions, loggerFactory); return(middleware.OnConnectionAsync); }); return(listenOptions); }
public async Task CanReadAndWriteWithAsyncConnectionMiddleware(RequestDelegate requestDelegate) { var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); listenOptions.Use(next => new AsyncConnectionMiddleware(next).OnConnectionAsync); var serviceContext = new TestServiceContext(LoggerFactory); await using (var server = new TestServer(requestDelegate, serviceContext, listenOptions)) { using (var connection = server.CreateConnection()) { await connection.Send( "POST / HTTP/1.0", "Content-Length: 12", "", "Hello World?"); await connection.ReceiveEnd( "HTTP/1.1 200 OK", "Connection: close", $"Date: {serviceContext.DateHeaderValue}", "", "Hello World!"); } } }
/// <summary> /// Emits verbose logs for bytes read from and written to the connection. /// </summary> /// <returns> /// The <see cref="ListenOptions"/>. /// </returns> public static ListenOptions UseConnectionLogging(this ListenOptions listenOptions, string loggerName) { var loggerFactory = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService <ILoggerFactory>(); var logger = loggerName == null?loggerFactory.CreateLogger <LoggingConnectionMiddleware>() : loggerFactory.CreateLogger(loggerName); listenOptions.Use(next => new LoggingConnectionMiddleware(next, logger).OnConnectionAsync); return(listenOptions); }
public static ListenOptions UseWindowsAuthentication(this ListenOptions options) { options.Use(next => { var middleware = new AuthenticationConnectionMiddleware(next); return(middleware.OnConnectedAsync); }); return(options); }
/// <summary> /// Configure Kestrel to use HTTPS. /// </summary> /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param> /// <param name="httpsOptionsCallback">Callback to configure HTTPS options.</param> /// <param name="state">State for the <paramref name="httpsOptionsCallback"/>.</param> /// <param name="handshakeTimeout">Specifies the maximum amount of time allowed for the TLS/SSL handshake. This must be positive and finite.</param> /// <returns>The <see cref="ListenOptions"/>.</returns> internal static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsOptionsCallback httpsOptionsCallback, object state, TimeSpan handshakeTimeout) { var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance; listenOptions.IsTls = true; listenOptions.Use(next => { var middleware = new HttpsConnectionMiddleware(next, httpsOptionsCallback, state, handshakeTimeout, loggerFactory); return(middleware.OnConnectionAsync); }); return(listenOptions); }
private TestServer CreateServerWithMaxConnections(RequestDelegate app, ResourceCounter concurrentConnectionCounter) { var serviceContext = new TestServiceContext(LoggerFactory); var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); listenOptions.Use(next => { var middleware = new ConnectionLimitMiddleware(next, concurrentConnectionCounter, serviceContext.Log); return(middleware.OnConnectionAsync); }); return(new TestServer(app, serviceContext, listenOptions)); }
/// <summary> /// Configure Kestrel to use HTTPS. /// </summary> /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param> /// <param name="httpsOptions">Options to configure HTTPS.</param> /// <returns>The <see cref="ListenOptions"/>.</returns> public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions) { var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance; listenOptions.IsTls = true; listenOptions.Use(next => { // Set the list of protocols from listen options httpsOptions.HttpProtocols = listenOptions.Protocols; var middleware = new HttpsConnectionMiddleware(next, httpsOptions, loggerFactory); return(middleware.OnConnectionAsync); }); return(listenOptions); }
public async Task ImmediateFinAfterOnConnectionAsyncClosesGracefully(RequestDelegate requestDelegate) { var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); listenOptions.Use(next => new AsyncConnectionMiddleware(next).OnConnectionAsync); var serviceContext = new TestServiceContext(LoggerFactory); await using (var server = new TestServer(requestDelegate, serviceContext, listenOptions)) { using (var connection = server.CreateConnection()) { // FIN connection.ShutdownSend(); await connection.WaitForConnectionClose(); } } }
internal static ListenOptions UseConnectionTracing(this ListenOptions listenOptions, IConnectionTracingEventSource eventSource) { listenOptions.Use(next => new ConnectionTracingMiddleware(next, eventSource).OnConnectionAsync); return(listenOptions); }
public static IConnectionBuilder UseProtocolMultiplexing(this ListenOptions options) => options.Use(next => new ProtocolMultiplexingMiddleware(next).OnConnectionAsync);