diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysOptions.cs b/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysOptions.cs index 2c97b4e..fe0c6d1 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysOptions.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/HttpSysOptions.cs @@ -81,6 +81,12 @@ public long RequestQueueLimit } } + /// + /// The amount of time to wait for active requests to drain while the server is shutting down. + /// New requests will receive a 503 response in this time period. The default is 5 seconds. + /// + public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(5); + internal void SetRequestQueueLimit(RequestQueue requestQueue) { _requestQueue = requestQueue; diff --git a/src/Microsoft.AspNetCore.Server.HttpSys/MessagePump.cs b/src/Microsoft.AspNetCore.Server.HttpSys/MessagePump.cs index bd6dbec..2392156 100644 --- a/src/Microsoft.AspNetCore.Server.HttpSys/MessagePump.cs +++ b/src/Microsoft.AspNetCore.Server.HttpSys/MessagePump.cs @@ -221,7 +221,15 @@ public void Dispose() if (_outstandingRequests > 0) { LogHelper.LogInfo(_logger, "Stopping, waiting for " + _outstandingRequests + " request(s) to drain."); - _shutdownSignal.WaitOne(); + var drained = _shutdownSignal.WaitOne(Listener.Options.ShutdownTimeout); + if (drained) + { + LogHelper.LogInfo(_logger, "All requests drained successfully."); + } + else + { + LogHelper.LogInfo(_logger, "Timed out, terminating " + _outstandingRequests + " request(s)."); + } } // All requests are finished _listener.Dispose(); diff --git a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs index 2794e46..2ae9440 100644 --- a/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs +++ b/test/Microsoft.AspNetCore.Server.HttpSys.FunctionalTests/ServerTests.cs @@ -68,7 +68,7 @@ public async Task Server_EchoHelloWorld_Success() } [ConditionalFact] - public async Task Server_ShutdownDurringRequest_Success() + public async Task Server_ShutdownDuringRequest_Success() { Task responseTask; ManualResetEvent received = new ManualResetEvent(false); @@ -87,6 +87,30 @@ public async Task Server_ShutdownDurringRequest_Success() Assert.Equal("Hello World", response); } + [ConditionalFact] + public async Task Server_ShutdownDuringLongRunningRequest_TimesOut() + { + Task responseTask; + var received = new ManualResetEvent(false); + bool? shutdown = null; + var waitForShutdown = new ManualResetEvent(false); + string address; + using (Utilities.CreateHttpServer(out address, httpContext => + { + received.Set(); + shutdown = waitForShutdown.WaitOne(TimeSpan.FromSeconds(15)); + httpContext.Response.ContentLength = 11; + return httpContext.Response.WriteAsync("Hello World"); + })) + { + responseTask = SendRequestAsync(address); + Assert.True(received.WaitOne(TimeSpan.FromSeconds(10))); + } + Assert.False(shutdown.HasValue); + waitForShutdown.Set(); + await Assert.ThrowsAsync(async () => await responseTask); + } + [ConditionalFact] public void Server_AppException_ClientReset() {