// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting.Internal;
namespace Microsoft.AspNetCore.Hosting
{
public static class WebHostExtensions
{
///
/// Attempts to gracefully stop the host with the given timeout.
///
///
/// The timeout for stopping gracefully. Once expired the
/// server may terminate any remaining active connections.
///
public static Task StopAsync(this IWebHost host, TimeSpan timeout)
{
return host.StopAsync(new CancellationTokenSource(timeout).Token);
}
///
/// Block the calling thread until shutdown is triggered via Ctrl+C or SIGTERM.
///
/// The running .
public static void WaitForShutdown(this IWebHost host)
{
host.WaitForShutdownAsync().GetAwaiter().GetResult();
}
///
/// Returns a Task that completes when shutdown is triggered via the given token, Ctrl+C or SIGTERM.
///
/// The running .
/// The token to trigger shutdown.
public static async Task WaitForShutdownAsync(this IWebHost host, CancellationToken token = default)
{
var done = new ManualResetEventSlim(false);
using (var cts = CancellationTokenSource.CreateLinkedTokenSource(token))
{
AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: string.Empty);
try
{
await host.WaitForTokenShutdownAsync(cts.Token);
}
finally
{
done.Set();
}
}
}
///
/// Runs a web application and block the calling thread until host shutdown.
///
/// The to run.
public static void Run(this IWebHost host)
{
host.RunAsync().GetAwaiter().GetResult();
}
///
/// Runs a web application and returns a Task that only completes when the token is triggered or shutdown is triggered.
///
/// The to run.
/// The token to trigger shutdown.
public static async Task RunAsync(this IWebHost host, CancellationToken token = default)
{
// Wait for token shutdown if it can be canceled
if (token.CanBeCanceled)
{
await host.RunAsync(token, shutdownMessage: null);
return;
}
// If token cannot be canceled, attach Ctrl+C and SIGTERM shutdown
var done = new ManualResetEventSlim(false);
using (var cts = new CancellationTokenSource())
{
var shutdownMessage = host.Services.GetRequiredService().SuppressStatusMessages ? string.Empty : "Application is shutting down...";
AttachCtrlcSigtermShutdown(cts, done, shutdownMessage: shutdownMessage);
try
{
await host.RunAsync(cts.Token, "Application started. Press Ctrl+C to shut down.");
}
finally
{
done.Set();
}
}
}
private static async Task RunAsync(this IWebHost host, CancellationToken token, string shutdownMessage)
{
using (host)
{
await host.StartAsync(token);
var hostingEnvironment = host.Services.GetService();
var options = host.Services.GetRequiredService();
if (!options.SuppressStatusMessages)
{
Console.WriteLine($"Hosting environment: {hostingEnvironment.EnvironmentName}");
Console.WriteLine($"Content root path: {hostingEnvironment.ContentRootPath}");
var serverAddresses = host.ServerFeatures.Get()?.Addresses;
if (serverAddresses != null)
{
foreach (var address in serverAddresses)
{
Console.WriteLine($"Now listening on: {address}");
}
}
if (!string.IsNullOrEmpty(shutdownMessage))
{
Console.WriteLine(shutdownMessage);
}
}
await host.WaitForTokenShutdownAsync(token);
}
}
private static void AttachCtrlcSigtermShutdown(CancellationTokenSource cts, ManualResetEventSlim resetEvent, string shutdownMessage)
{
void Shutdown()
{
if (!cts.IsCancellationRequested)
{
if (!string.IsNullOrEmpty(shutdownMessage))
{
Console.WriteLine(shutdownMessage);
}
try
{
cts.Cancel();
}
catch (ObjectDisposedException) { }
}
// Wait on the given reset event
resetEvent.Wait();
};
AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) => Shutdown();
Console.CancelKeyPress += (sender, eventArgs) =>
{
Shutdown();
// Don't terminate the process immediately, wait for the Main thread to exit gracefully.
eventArgs.Cancel = true;
};
}
private static async Task WaitForTokenShutdownAsync(this IWebHost host, CancellationToken token)
{
var applicationLifetime = host.Services.GetService();
token.Register(state =>
{
((IApplicationLifetime)state).StopApplication();
},
applicationLifetime);
var waitForStop = new TaskCompletionSource