Make IISExpress shutdown gracefully. (#1066)

This commit is contained in:
Justin Kotalik 2018-07-19 23:20:13 -07:00 committed by GitHub
parent 89fda83bbd
commit 256aed8369
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 235 additions and 98 deletions

View File

@ -5,36 +5,36 @@
<PropertyGroup Label="Package Versions">
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
<InternalAspNetCoreSdkPackageVersion>2.2.0-preview1-17099</InternalAspNetCoreSdkPackageVersion>
<MicrosoftAspNetCoreAllPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreAllPackageVersion>
<MicrosoftAspNetCoreAuthenticationCorePackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
<MicrosoftAspNetCoreHostingPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreHostingPackageVersion>
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
<MicrosoftAspNetCoreHttpOverridesPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreHttpOverridesPackageVersion>
<MicrosoftAspNetCoreHttpPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreHttpPackageVersion>
<MicrosoftAspNetCoreHttpSysSourcesPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreHttpSysSourcesPackageVersion>
<MicrosoftAspNetCoreAllPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreAllPackageVersion>
<MicrosoftAspNetCoreAuthenticationCorePackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreAuthenticationCorePackageVersion>
<MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreBenchmarkRunnerSourcesPackageVersion>
<MicrosoftAspNetCoreHostingAbstractionsPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreHostingAbstractionsPackageVersion>
<MicrosoftAspNetCoreHostingPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreHostingPackageVersion>
<MicrosoftAspNetCoreHttpExtensionsPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreHttpExtensionsPackageVersion>
<MicrosoftAspNetCoreHttpOverridesPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreHttpOverridesPackageVersion>
<MicrosoftAspNetCoreHttpPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreHttpPackageVersion>
<MicrosoftAspNetCoreHttpSysSourcesPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreHttpSysSourcesPackageVersion>
<MicrosoftAspNetCoreServerIntegrationTestingPackageVersion>0.6.0-a-preview1-DeploymentParameters-17085</MicrosoftAspNetCoreServerIntegrationTestingPackageVersion>
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreServerKestrelPackageVersion>
<MicrosoftAspNetCoreTestHostPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreTestHostPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.2.0-preview1-34694</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
<MicrosoftAspNetCoreServerKestrelPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreServerKestrelPackageVersion>
<MicrosoftAspNetCoreTestHostPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreTestHostPackageVersion>
<MicrosoftAspNetCoreTestingPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreTestingPackageVersion>
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.2.0-preview1-34709</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
<MicrosoftBuildFrameworkPackageVersion>15.6.82</MicrosoftBuildFrameworkPackageVersion>
<MicrosoftBuildUtilitiesCorePackageVersion>15.6.82</MicrosoftBuildUtilitiesCorePackageVersion>
<MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>2.2.0-preview1-34694</MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.2.0-preview1-34694</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.2.0-preview1-34694</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.2.0-preview1-34694</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.2.0-preview1-34694</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>2.2.0-preview1-34694</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>2.2.0-preview1-34694</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>2.2.0-preview1-34694</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>2.2.0-preview1-34694</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>2.2.0-preview1-34694</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>2.2.0-preview1-34709</MicrosoftExtensionsBuffersMemoryPoolSourcesPackageVersion>
<MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>2.2.0-preview1-34709</MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>2.2.0-preview1-34709</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.2.0-preview1-34709</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.2.0-preview1-34709</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>2.2.0-preview1-34709</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>2.2.0-preview1-34709</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>2.2.0-preview1-34709</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>2.2.0-preview1-34709</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>2.2.0-preview1-34709</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftNETCoreApp20PackageVersion>2.0.9</MicrosoftNETCoreApp20PackageVersion>
<MicrosoftNETCoreApp21PackageVersion>2.1.2</MicrosoftNETCoreApp21PackageVersion>
<MicrosoftNETCoreApp22PackageVersion>2.2.0-preview1-26618-02</MicrosoftNETCoreApp22PackageVersion>
<MicrosoftNetHttpHeadersPackageVersion>2.2.0-preview1-34694</MicrosoftNetHttpHeadersPackageVersion>
<MicrosoftNetHttpHeadersPackageVersion>2.2.0-preview1-34709</MicrosoftNetHttpHeadersPackageVersion>
<MicrosoftNETTestSdkPackageVersion>15.6.1</MicrosoftNETTestSdkPackageVersion>
<MicrosoftWebAdministrationPackageVersion>11.1.0</MicrosoftWebAdministrationPackageVersion>
<NETStandardLibrary20PackageVersion>2.0.3</NETStandardLibrary20PackageVersion>

View File

@ -224,6 +224,13 @@ Language=English
%1
.
Messageid=1033
SymbolicName=ASPNETCORE_EVENT_APP_SHUTDOWN_SUCCESSFUL
Language=English
%1
.
;
;#endif // _ASPNETCORE_MODULE_MSG_H_
;

View File

@ -26,6 +26,7 @@
#define ASPNETCORE_EVENT_SENT_SHUTDOWN_HTTP_REQUEST_MSG L"Sent shutdown HTTP message to process '%d' and received http status '%d'."
#define ASPNETCORE_EVENT_APP_SHUTDOWN_FAILURE_MSG L"Failed to gracefully shutdown application '%s'."
#define ASPNETCORE_EVENT_LOAD_CLR_FALIURE_MSG L"Application '%s' with physical root '%s' failed to load clr and managed application, ErrorCode = '0x%x."
#define ASPNETCORE_EVENT_APP_SHUTDOWN_SUCCESSFUL_MSG L"Application '%s' has shutdown."
#define ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG L"Only one inprocess application is allowed per IIS application pool. Please assign the application '%s' to a different IIS application pool."
#define ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG L"Mixed hosting model is not supported. Application '%s' configured with different hostingModel value '%d' other than the one of running application(s)."
#define ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG L"Failed to start application '%s', ErrorCode '0x%x'."

View File

@ -126,6 +126,14 @@ Finished:
//
exit(hr);
}
else
{
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_INFORMATION_TYPE,
ASPNETCORE_EVENT_APP_SHUTDOWN_SUCCESSFUL,
ASPNETCORE_EVENT_APP_SHUTDOWN_SUCCESSFUL_MSG,
m_pConfig->QueryConfigPath()->QueryStr());
}
}
VOID

View File

@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
switch (deploymentParameters.ServerType)
{
case ServerType.IISExpress:
return new IIS.IISExpressDeployer(deploymentParameters, loggerFactory);
return new IISExpressDeployer(deploymentParameters, loggerFactory);
case ServerType.IIS:
return new IISDeployer(deploymentParameters, loggerFactory);
default:

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
/// </summary>
internal class IISApplication
{
private static readonly TimeSpan _timeout = TimeSpan.FromSeconds(5);
private static readonly TimeSpan _timeout = TimeSpan.FromSeconds(10);
private static readonly TimeSpan _retryDelay = TimeSpan.FromMilliseconds(200);
private readonly ServerManager _serverManager = new ServerManager();
private readonly DeploymentParameters _deploymentParameters;
@ -32,6 +32,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
"config",
"applicationhost.config");
public Process HostProcess { get; set; }
public IISApplication(DeploymentParameters deploymentParameters, ILogger logger)
{
_deploymentParameters = deploymentParameters;
@ -63,7 +65,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
}
AddTemporaryAppHostConfig();
ConfigureAppPool(contentRoot);
var apppool = ConfigureAppPool(contentRoot);
ConfigureSite(contentRoot, port);
@ -76,7 +78,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
_serverManager.CommitChanges();
await WaitUntilSiteStarted();
await WaitUntilSiteStarted(apppool);
}
}
@ -89,7 +91,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
config.Save(webConfigFile);
}
private async Task WaitUntilSiteStarted()
private async Task WaitUntilSiteStarted(ApplicationPool appPool)
{
var sw = Stopwatch.StartNew();
@ -98,6 +100,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
try
{
var site = _serverManager.Sites.FirstOrDefault(s => s.Name.Equals(WebSiteName));
if (site.State == ObjectState.Started)
{
_logger.LogInformation($"Site {WebSiteName} has started.");

View File

@ -75,13 +75,13 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
// Warm up time for IIS setup.
Logger.LogInformation("Successfully finished IIS application directory setup.");
return new DeploymentResult(
return new IISDeploymentResult(
LoggerFactory,
IISDeploymentParameters,
applicationBaseUri: uri.ToString(),
contentRoot: contentRoot,
hostShutdownToken: _hostShutdownToken.Token
hostShutdownToken: _hostShutdownToken.Token,
hostProcess: _application.HostProcess
);
}
}

View File

@ -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.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
{
@ -43,6 +44,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
ServerConfigActionList = tempParameters.ServerConfigActionList;
WebConfigBasedEnvironmentVariables = tempParameters.WebConfigBasedEnvironmentVariables;
HandlerSettings = tempParameters.HandlerSettings;
GracefulShutdown = tempParameters.GracefulShutdown;
}
}
@ -59,6 +61,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
public IDictionary<string, string> HandlerSettings { get; set; } = new Dictionary<string, string>();
public bool GracefulShutdown { get; set; }
private Action<XElement> AddWebConfigEnvironmentVariables()
{
return xElement =>

View File

@ -0,0 +1,57 @@
// 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.Diagnostics;
using System.Net.Http;
using System.Threading;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
{
public class IISDeploymentResult : DeploymentResult
{
public ILogger Logger { get; set; }
public Process HostProcess { get; }
public IISDeploymentResult(ILoggerFactory loggerFactory,
IISDeploymentParameters deploymentParameters,
string applicationBaseUri,
string contentRoot,
CancellationToken hostShutdownToken,
Process hostProcess)
: base(loggerFactory,
deploymentParameters,
applicationBaseUri,
contentRoot,
hostShutdownToken)
{
HostProcess = hostProcess;
Logger = loggerFactory.CreateLogger(deploymentParameters.SiteName);
// SocketsHttpHandler isn't available in netstandard2.0
RetryingHttpClient = CreateRetryClient(new HttpClientHandler());
HttpClient = CreateClient(new HttpClientHandler());
}
public HttpClient CreateRetryClient(HttpMessageHandler messageHandler)
{
var loggingHandler = new LoggingHandler(messageHandler, Logger);
var retryHandler = new RetryHandler(loggingHandler, Logger);
return new HttpClient(retryHandler)
{
BaseAddress = base.HttpClient.BaseAddress
};
}
public HttpClient CreateClient(HttpMessageHandler messageHandler)
{
return new HttpClient(new LoggingHandler(messageHandler, Logger))
{
BaseAddress = base.HttpClient.BaseAddress
};
}
public HttpClient RetryingHttpClient { get; set; }
public new HttpClient HttpClient { get; set; }
}
}

View File

@ -6,6 +6,7 @@ using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
private const string FailedToInitializeBindingsMessage = "Failed to initialize site bindings";
private const string UnableToStartIISExpressMessage = "Unable to start iisexpress.";
private const int MaximumAttempts = 5;
private readonly TimeSpan ShutdownTimeSpan = TimeSpan.FromSeconds(60);
private static readonly Regex UrlDetectorRegex = new Regex(@"^\s*Successfully registered URL ""(?<url>[^""]+)"" for site.*$");
private Process _hostProcess;
@ -97,12 +98,14 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
Logger.LogInformation("Application ready at URL: {appUrl}", actualUri);
// Right now this works only for urls like http://localhost:5001/. Does not work for http://localhost:5001/subpath.
return new DeploymentResult(
return new IISDeploymentResult(
LoggerFactory,
IISDeploymentParameters,
applicationBaseUri: actualUri.ToString(),
contentRoot: contentRoot,
hostShutdownToken: hostExitToken);
hostShutdownToken: hostExitToken,
hostProcess: _hostProcess);
}
}
@ -352,7 +355,14 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
{
using (Logger.BeginScope("Dispose"))
{
ShutDownIfAnyHostProcess(_hostProcess);
if (IISDeploymentParameters.GracefulShutdown)
{
GracefullyShutdownProcess(_hostProcess);
}
else
{
ShutDownIfAnyHostProcess(_hostProcess);
}
if (!string.IsNullOrEmpty(DeploymentParameters.ServerConfigLocation)
&& File.Exists(DeploymentParameters.ServerConfigLocation))
@ -388,6 +398,59 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
}
}
private class WindowsNativeMethods
{
[DllImport("user32.dll")]
internal static extern IntPtr GetTopWindow(IntPtr hWnd);
[DllImport("user32.dll")]
internal static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll")]
internal static extern uint GetWindowThreadProcessId(IntPtr hwnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
internal static extern bool PostMessage(HandleRef hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
}
private static void SendStopMessageToProcess(int pid)
{
for (var ptr = WindowsNativeMethods.GetTopWindow(IntPtr.Zero); ptr != IntPtr.Zero; ptr = WindowsNativeMethods.GetWindow(ptr, 2))
{
uint num;
WindowsNativeMethods.GetWindowThreadProcessId(ptr, out num);
if (pid == num)
{
var hWnd = new HandleRef(null, ptr);
WindowsNativeMethods.PostMessage(hWnd, 0x12, IntPtr.Zero, IntPtr.Zero);
return;
}
}
}
private void GracefullyShutdownProcess(Process hostProcess)
{
if (hostProcess != null && !hostProcess.HasExited)
{
// Calling hostProcess.StandardInput.WriteLine("q") with StandardInput redirected
// for the process does not work when stopping IISExpress
// Also, hostProcess.CloseMainWindow() doesn't work either.
// Instead we have to send WM_QUIT to the iisexpress process via pInvokes.
// See: https://stackoverflow.com/questions/4772092/starting-and-stopping-iis-express-programmatically
SendStopMessageToProcess(hostProcess.Id);
if (!hostProcess.WaitForExit((int)ShutdownTimeSpan.TotalMilliseconds))
{
throw new InvalidOperationException($"iisexpress Process {hostProcess.Id} failed to gracefully shutdown.");
}
if (hostProcess.ExitCode != 0)
{
Logger.LogWarning($"IISExpress exit code is non-zero after graceful shutdown. Exit code: {hostProcess.ExitCode}");
}
}
else
{
throw new InvalidOperationException($"iisexpress Process {hostProcess.Id} crashed before shutdown was triggered.");
}
}
private void ModifyDotNetExePathInWebConfig()
{
// We assume the x64 dotnet.exe is on the path so we need to provide an absolute path for x86 scenarios.

View File

@ -4,7 +4,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.IntegrationTesting
namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
{
public class LoggingHandler: DelegatingHandler
{

View File

@ -8,7 +8,7 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.IntegrationTesting
namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS
{
public class RetryHandler : DelegatingHandler
{

View File

@ -20,5 +20,18 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
EventLogHelpers.VerifyEventLogEvent(TestSink, "Application '.+' started the coreclr in-process successfully.");
}
[ConditionalFact]
public async Task CheckShutdownEventLogMessage()
{
var deploymentParameters = Helpers.GetBaseDeploymentParameters(publish: true);
deploymentParameters.GracefulShutdown = true;
var deploymentResult = await DeployAsync(deploymentParameters);
await Helpers.AssertStarts(deploymentResult);
StopServer();
EventLogHelpers.VerifyEventLogEvent(TestSink, "Application '.+' has shutdown.");
}
}
}

View File

@ -68,7 +68,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
var deploymentResult = await DeployAsync(deploymentParameters);
await Helpers.AssertStarts(deploymentResult, "HelloWorld");
}

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
{
}
private ApplicationDeployer _deployer;
protected ApplicationDeployer _deployer;
protected virtual async Task<IISDeploymentResult> DeployAsync(IISDeploymentParameters parameters)
{
@ -31,11 +31,10 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
parameters.ServerConfigTemplateContent = parameters.ServerConfigTemplateContent ?? File.ReadAllText("IISExpress.config");
}
_deployer = IISApplicationDeployerFactory.Create(parameters, LoggerFactory);
var result = await _deployer.DeployAsync();
return new IISDeploymentResult(result, Logger);
return (IISDeploymentResult)await _deployer.DeployAsync();
}
public override void Dispose()

View File

@ -1,42 +0,0 @@
// 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.Net.Http;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Server.IntegrationTesting
{
public class IISDeploymentResult
{
public DeploymentResult DeploymentResult { get; }
public ILogger Logger { get; }
public IISDeploymentResult(DeploymentResult deploymentResult, ILogger logger)
{
DeploymentResult = deploymentResult;
Logger = logger;
RetryingHttpClient = CreateRetryClient(new SocketsHttpHandler());
HttpClient = CreateClient(new SocketsHttpHandler());
}
public HttpClient CreateRetryClient(HttpMessageHandler messageHandler)
{
return new HttpClient(new RetryHandler(new LoggingHandler(messageHandler, Logger), Logger))
{
BaseAddress = DeploymentResult.HttpClient.BaseAddress
};
}
public HttpClient CreateClient(HttpMessageHandler messageHandler)
{
return new HttpClient(new LoggingHandler(messageHandler, Logger))
{
BaseAddress = DeploymentResult.HttpClient.BaseAddress
};
}
public HttpClient HttpClient { get; set; }
public HttpClient RetryingHttpClient { get; set; }
}
}

View File

@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

View File

@ -29,7 +29,6 @@
<None Include="$(MSBuildThisFileDirectory)..\..\src\AspNetCoreModuleV2\AspNetCore\bin\$(Configuration)\x64\aspnetcorev2.pdb" CopyToOutputDirectory="PreserveNewest" Visible="true" Link="%(FileName)%(Extension)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Server.IntegrationTesting" Version="$(MicrosoftAspNetCoreServerIntegrationTestingPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(MicrosoftAspNetCoreHostingPackageVersion)" />

View File

@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging;
using Xunit;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
{
@ -26,7 +27,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
{
var deploymentResult = await DeployApp(hostingModel);
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
AddAppOffline(deploymentResult.ContentRoot);
await AssertAppOffline(deploymentResult);
}
@ -39,7 +40,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
var expectedResponse = "The app is offline.";
var deploymentResult = await DeployApp(hostingModel);
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot, expectedResponse);
AddAppOffline(deploymentResult.ContentRoot, expectedResponse);
await AssertAppOffline(deploymentResult, expectedResponse);
}
@ -49,7 +50,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
{
var deploymentResult = await AssertStarts(HostingModel.InProcess);
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
AddAppOffline(deploymentResult.ContentRoot);
await AssertStopsProcess(deploymentResult);
}
@ -62,9 +63,9 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
// Repeat dropping file and restarting multiple times
for (int i = 0; i < 5; i++)
{
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
AddAppOffline(deploymentResult.ContentRoot);
await AssertAppOffline(deploymentResult);
RemoveAppOffline(deploymentResult.DeploymentResult.ContentRoot);
RemoveAppOffline(deploymentResult.ContentRoot);
await AssertRunning(deploymentResult);
}
}
@ -76,11 +77,11 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
{
var deploymentResult = await DeployApp(hostingModel);
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
AddAppOffline(deploymentResult.ContentRoot);
await AssertAppOffline(deploymentResult);
RemoveAppOffline(deploymentResult.DeploymentResult.ContentRoot);
RemoveAppOffline(deploymentResult.ContentRoot);
await AssertRunning(deploymentResult);
}
@ -132,7 +133,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
// dropping app_offline will kill the process
}
var hostShutdownToken = deploymentResult.DeploymentResult.HostShutdownToken;
var hostShutdownToken = deploymentResult.HostShutdownToken;
Assert.True(hostShutdownToken.WaitHandle.WaitOne(TimeoutExtensions.DefaultTimeout));
Assert.True(hostShutdownToken.IsCancellationRequested);

View File

@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
var httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true };
var httpClient = deploymentResult.DeploymentResult.CreateHttpClient(httpClientHandler);
var httpClient = deploymentResult.CreateHttpClient(httpClientHandler);
response = await httpClient.GetAsync("/AuthenticationAnonymous");
responseText = await response.Content.ReadAsStringAsync();

View File

@ -24,7 +24,30 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
var result = await DeployAsync(parameters);
var response = await result.RetryingHttpClient.GetAsync("/Shutdown");
Assert.True(result.DeploymentResult.HostShutdownToken.WaitHandle.WaitOne(TimeoutExtensions.DefaultTimeout));
Assert.True(result.HostShutdownToken.WaitHandle.WaitOne(TimeoutExtensions.DefaultTimeout));
}
[ConditionalFact]
public async Task GracefulShutdown_DoesNotCrashProcess()
{
var parameters = Helpers.GetBaseDeploymentParameters(publish: true);
parameters.GracefulShutdown = true;
var result = await DeployAsync(parameters);
var response = await result.RetryingHttpClient.GetAsync("/HelloWorld");
StopServer();
Assert.True(result.HostProcess.ExitCode == 0);
}
[ConditionalFact]
public async Task ForcefulShutdown_DoesrashProcess()
{
var parameters = Helpers.GetBaseDeploymentParameters(publish: true);
var result = await DeployAsync(parameters);
var response = await result.RetryingHttpClient.GetAsync("/HelloWorld");
StopServer();
Assert.True(result.HostProcess.ExitCode == 1);
}
}
}

View File

@ -152,8 +152,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
private string GetANCMRequestHandlerPath(IISDeploymentResult deploymentResult, string version)
{
return Path.Combine(deploymentResult.DeploymentResult.ContentRoot,
deploymentResult.DeploymentResult.DeploymentParameters.RuntimeArchitecture.ToString(),
return Path.Combine(deploymentResult.ContentRoot,
deploymentResult.DeploymentParameters.RuntimeArchitecture.ToString(),
version,
_aspNetCoreDll);
}