Implement IServer.StopAsync
This commit is contained in:
Родитель
bc003985c6
Коммит
4e8872b94d
|
@ -79,12 +79,6 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(5);
|
||||
|
||||
internal void SetRequestQueueLimit(RequestQueue requestQueue)
|
||||
{
|
||||
_requestQueue = requestQueue;
|
||||
|
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
|
||||
private bool _stopping;
|
||||
private int _outstandingRequests;
|
||||
private ManualResetEvent _shutdownSignal;
|
||||
private TaskCompletionSource<object> _shutdownSignal;
|
||||
|
||||
private readonly ServerAddressesFeature _serverAddresses;
|
||||
|
||||
|
@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
_processRequest = new Action<object>(ProcessRequestAsync);
|
||||
_maxAccepts = _options.MaxAccepts;
|
||||
EnableResponseCaching = _options.EnableResponseCaching;
|
||||
_shutdownSignal = new ManualResetEvent(false);
|
||||
_shutdownSignal = new TaskCompletionSource<object>();
|
||||
}
|
||||
|
||||
internal HttpSysListener Listener { get; }
|
||||
|
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
|
||||
public IFeatureCollection Features { get; }
|
||||
|
||||
public void Start<TContext>(IHttpApplication<TContext> application)
|
||||
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
|
||||
{
|
||||
if (application == null)
|
||||
{
|
||||
|
@ -124,6 +124,8 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
Listener.Start();
|
||||
|
||||
ActivateRequestProcessingLimits();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void ActivateRequestProcessingLimits()
|
||||
|
@ -224,7 +226,8 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
{
|
||||
if (Interlocked.Decrement(ref _outstandingRequests) == 0 && _stopping)
|
||||
{
|
||||
_shutdownSignal.Set();
|
||||
LogHelper.LogInfo(_logger, "All requests drained.");
|
||||
_shutdownSignal.TrySetResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,24 +245,30 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
context.Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_stopping = true;
|
||||
// Wait for active requests to drain
|
||||
if (_outstandingRequests > 0)
|
||||
{
|
||||
LogHelper.LogInfo(_logger, "Stopping, waiting for " + _outstandingRequests + " request(s) to drain.");
|
||||
var drained = _shutdownSignal.WaitOne(Listener.Options.ShutdownTimeout);
|
||||
if (drained)
|
||||
{
|
||||
LogHelper.LogInfo(_logger, "All requests drained successfully.");
|
||||
}
|
||||
else
|
||||
|
||||
var waitForStop = new TaskCompletionSource<object>();
|
||||
cancellationToken.Register(() =>
|
||||
{
|
||||
LogHelper.LogInfo(_logger, "Timed out, terminating " + _outstandingRequests + " request(s).");
|
||||
waitForStop.TrySetResult(0);
|
||||
});
|
||||
|
||||
return Task.WhenAny(_shutdownSignal.Task, waitForStop.Task);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
// All requests are finished
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_stopping = true;
|
||||
Listener.Dispose();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
serverAddressesFeature.PreferHostingUrls = true;
|
||||
server.Listener.Options.UrlPrefixes.Add(serverAddress);
|
||||
|
||||
server.Start(new DummyApplication());
|
||||
server.StartAsync(new DummyApplication(), CancellationToken.None).Wait();
|
||||
|
||||
Assert.Equal(overrideAddress, serverAddressesFeature.Addresses.Single());
|
||||
}
|
||||
|
@ -46,7 +47,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
serverAddressesFeature.Addresses.Add(overrideAddress);
|
||||
server.Listener.Options.UrlPrefixes.Add(serverAddress);
|
||||
|
||||
server.Start(new DummyApplication());
|
||||
server.StartAsync(new DummyApplication(), CancellationToken.None).Wait();
|
||||
|
||||
Assert.Equal(serverAddress, serverAddressesFeature.Addresses.Single());
|
||||
}
|
||||
|
@ -63,7 +64,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
serverAddressesFeature.PreferHostingUrls = true;
|
||||
server.Listener.Options.UrlPrefixes.Add(serverAddress);
|
||||
|
||||
server.Start(new DummyApplication());
|
||||
server.StartAsync(new DummyApplication(), CancellationToken.None).Wait();
|
||||
|
||||
Assert.Equal(serverAddress, serverAddressesFeature.Addresses.Single());
|
||||
}
|
||||
|
@ -84,7 +85,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
serverAddressesFeature.Addresses.Add(serverAddress);
|
||||
server.Listener.Options.UrlPrefixes.Add(overrideAddress);
|
||||
|
||||
server.Start(new DummyApplication());
|
||||
server.StartAsync(new DummyApplication(), CancellationToken.None).Wait();
|
||||
|
||||
Assert.Equal(overrideAddress, serverAddressesFeature.Addresses.Single());
|
||||
}
|
||||
|
@ -100,7 +101,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
var serverAddressesFeature = server.Features.Get<IServerAddressesFeature>();
|
||||
serverAddressesFeature.Addresses.Add(serverAddress);
|
||||
|
||||
server.Start(new DummyApplication());
|
||||
server.StartAsync(new DummyApplication(), CancellationToken.None).Wait();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,7 +110,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
{
|
||||
using (var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory()))
|
||||
{
|
||||
server.Start(new DummyApplication());
|
||||
server.StartAsync(new DummyApplication(), CancellationToken.None).Wait();
|
||||
|
||||
Assert.Equal(Constants.DefaultServerAddress, server.Features.Get<IServerAddressesFeature>().Addresses.Single());
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using System.Net;
|
|||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
@ -312,7 +313,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
server.Listener.Options.UrlPrefixes.Add(UrlPrefix.Create(rootUri.Scheme, rootUri.Host, rootUri.Port, path));
|
||||
}
|
||||
|
||||
server.Start(new DummyApplication(app));
|
||||
server.StartAsync(new DummyApplication(app), CancellationToken.None).Wait();
|
||||
return server;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
Task<string> responseTask;
|
||||
ManualResetEvent received = new ManualResetEvent(false);
|
||||
string address;
|
||||
using (Utilities.CreateHttpServer(out address, httpContext =>
|
||||
using (var server = Utilities.CreateHttpServer(out address, httpContext =>
|
||||
{
|
||||
received.Set();
|
||||
httpContext.Response.ContentLength = 11;
|
||||
|
@ -82,11 +82,34 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
{
|
||||
responseTask = SendRequestAsync(address);
|
||||
Assert.True(received.WaitOne(10000));
|
||||
await server.StopAsync(new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token);
|
||||
}
|
||||
string response = await responseTask;
|
||||
Assert.Equal("Hello World", response);
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task Server_DisposeWithoutStopDuringRequest_Aborts()
|
||||
{
|
||||
Task<string> responseTask;
|
||||
var received = new ManualResetEvent(false);
|
||||
var stopped = new ManualResetEvent(false);
|
||||
string address;
|
||||
using (var server = Utilities.CreateHttpServer(out address, httpContext =>
|
||||
{
|
||||
received.Set();
|
||||
Assert.True(stopped.WaitOne(TimeSpan.FromSeconds(10)));
|
||||
httpContext.Response.ContentLength = 11;
|
||||
return httpContext.Response.WriteAsync("Hello World");
|
||||
}))
|
||||
{
|
||||
responseTask = SendRequestAsync(address);
|
||||
Assert.True(received.WaitOne(TimeSpan.FromSeconds(10)));
|
||||
}
|
||||
stopped.Set();
|
||||
await Assert.ThrowsAsync<HttpRequestException>(async () => await responseTask);
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task Server_ShutdownDuringLongRunningRequest_TimesOut()
|
||||
{
|
||||
|
@ -95,7 +118,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
bool? shutdown = null;
|
||||
var waitForShutdown = new ManualResetEvent(false);
|
||||
string address;
|
||||
using (Utilities.CreateHttpServer(out address, httpContext =>
|
||||
using (var server = Utilities.CreateHttpServer(out address, httpContext =>
|
||||
{
|
||||
received.Set();
|
||||
shutdown = waitForShutdown.WaitOne(TimeSpan.FromSeconds(15));
|
||||
|
@ -105,8 +128,9 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
{
|
||||
responseTask = SendRequestAsync(address);
|
||||
Assert.True(received.WaitOne(TimeSpan.FromSeconds(10)));
|
||||
}
|
||||
Assert.False(shutdown.HasValue);
|
||||
await server.StopAsync(new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token);
|
||||
}
|
||||
waitForShutdown.Set();
|
||||
await Assert.ThrowsAsync<HttpRequestException>(async () => await responseTask);
|
||||
}
|
||||
|
@ -271,7 +295,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
|
||||
using (server)
|
||||
{
|
||||
server.Start(new DummyApplication());
|
||||
await server.StartAsync(new DummyApplication(), CancellationToken.None);
|
||||
string response = await SendRequestAsync(address);
|
||||
Assert.Equal(string.Empty, response);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
@ -55,7 +56,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
server.Listener.Options.Authentication.AllowAnonymous = allowAnonymous;
|
||||
try
|
||||
{
|
||||
server.Start(new DummyApplication(app));
|
||||
server.StartAsync(new DummyApplication(app), CancellationToken.None).Wait();
|
||||
return server;
|
||||
}
|
||||
catch (HttpSysException)
|
||||
|
@ -76,7 +77,7 @@ namespace Microsoft.AspNetCore.Server.HttpSys
|
|||
{
|
||||
var server = new MessagePump(Options.Create(new HttpSysOptions()), new LoggerFactory());
|
||||
server.Features.Get<IServerAddressesFeature>().Addresses.Add(UrlPrefix.Create(scheme, host, port, path).ToString());
|
||||
server.Start(new DummyApplication(app));
|
||||
server.StartAsync(new DummyApplication(app), CancellationToken.None).Wait();
|
||||
return server;
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче