Fix App_offline and start adding tests (#953)
This commit is contained in:
parent
cd81cfc243
commit
306ab78b88
|
|
@ -118,6 +118,8 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo(
|
||||||
}
|
}
|
||||||
|
|
||||||
*ppApplicationInfo = pApplicationInfo;
|
*ppApplicationInfo = pApplicationInfo;
|
||||||
|
pApplicationInfo->StartMonitoringAppOffline();
|
||||||
|
|
||||||
pApplicationInfo = NULL;
|
pApplicationInfo = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -232,14 +232,18 @@ IN_PROCESS_APPLICATION::Recycle(
|
||||||
if (!m_pHttpServer->IsCommandLineLaunch())
|
if (!m_pHttpServer->IsCommandLineLaunch())
|
||||||
{
|
{
|
||||||
// IIS scenario.
|
// IIS scenario.
|
||||||
// notify IIS first so that new request will be routed to new worker process
|
// We don't actually handle any shutdown logic here.
|
||||||
|
// Instead, we notify IIS that the process needs to be recycled, which will call
|
||||||
|
// ApplicationManager->Shutdown(). This will call shutdown on the application.
|
||||||
m_pHttpServer->RecycleProcess(L"AspNetCore InProcess Recycle Process on Demand");
|
m_pHttpServer->RecycleProcess(L"AspNetCore InProcess Recycle Process on Demand");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// IISExpress scenario
|
// IISExpress scenario
|
||||||
// Shutdown the managed application and call exit to terminate current process
|
// Try to graceful shutdown the managed application
|
||||||
|
// and call exit to terminate current process
|
||||||
ShutDown();
|
ShutDown();
|
||||||
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,8 +118,8 @@ OUT_OF_PROCESS_APPLICATION::SetWebsocketStatus(
|
||||||
IHttpContext* pHttpContext
|
IHttpContext* pHttpContext
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
// Even though the applicationhost.config file contains the websocket element,
|
// Even though the applicationhost.config file contains the websocket element,
|
||||||
// the websocket module may still not be enabled.
|
// the websocket module may still not be enabled.
|
||||||
PCWSTR pszTempWebsocketValue;
|
PCWSTR pszTempWebsocketValue;
|
||||||
DWORD cbLength;
|
DWORD cbLength;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
// 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.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using IISIntegration.FunctionalTests.Utilities;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests;
|
||||||
|
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||||
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace IISIntegration.FunctionalTests.Inprocess
|
||||||
|
{
|
||||||
|
[SkipIfIISExpressSchemaMissingInProcess]
|
||||||
|
public class AppOfflineTests : IISFunctionalTestBase
|
||||||
|
{
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task AppOfflineDroppedWhileSiteIsDown_SiteReturns503()
|
||||||
|
{
|
||||||
|
var deploymentResult = await DeployApp();
|
||||||
|
|
||||||
|
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
|
||||||
|
|
||||||
|
await AssertAppOffline(deploymentResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task AppOfflineDroppedWhileSiteIsDown_CustomResponse()
|
||||||
|
{
|
||||||
|
var expectedResponse = "The app is offline.";
|
||||||
|
var deploymentResult = await DeployApp();
|
||||||
|
|
||||||
|
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot, expectedResponse);
|
||||||
|
|
||||||
|
await AssertAppOffline(deploymentResult, expectedResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown()
|
||||||
|
{
|
||||||
|
var deploymentResult = await AssertStarts();
|
||||||
|
|
||||||
|
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
|
||||||
|
|
||||||
|
await AssertStopsProcess(deploymentResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
public async Task AppOfflineDropped_CanRemoveAppOfflineAfterAddingAndSiteWorks()
|
||||||
|
{
|
||||||
|
var deploymentResult = await DeployApp();
|
||||||
|
|
||||||
|
AddAppOffline(deploymentResult.DeploymentResult.ContentRoot);
|
||||||
|
|
||||||
|
await AssertAppOffline(deploymentResult);
|
||||||
|
|
||||||
|
RemoveAppOffline(deploymentResult.DeploymentResult.ContentRoot);
|
||||||
|
|
||||||
|
var response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IISDeploymentResult> DeployApp()
|
||||||
|
{
|
||||||
|
var deploymentParameters = Helpers.GetBaseDeploymentParameters();
|
||||||
|
|
||||||
|
return await DeployAsync(deploymentParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddAppOffline(string appPath, string content = "")
|
||||||
|
{
|
||||||
|
File.WriteAllText(Path.Combine(appPath, "app_offline.htm"), content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveAppOffline(string appPath)
|
||||||
|
{
|
||||||
|
RetryHelper.RetryOperation(
|
||||||
|
() => File.Delete(Path.Combine(appPath, "app_offline.htm")),
|
||||||
|
e => Logger.LogError($"Failed to remove app_offline : {e.Message}"),
|
||||||
|
retryCount: 3,
|
||||||
|
retryDelayMilliseconds: 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AssertAppOffline(IISDeploymentResult deploymentResult, string expectedResponse = "")
|
||||||
|
{
|
||||||
|
var response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
|
||||||
|
|
||||||
|
for (var i = 0; response.IsSuccessStatusCode && i < 5; i++)
|
||||||
|
{
|
||||||
|
// Keep retrying until app_offline is present.
|
||||||
|
response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
|
||||||
|
|
||||||
|
Assert.Equal(expectedResponse, await response.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AssertStopsProcess(IISDeploymentResult deploymentResult)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await deploymentResult.RetryingHttpClient.GetAsync("HelloWorld");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException)
|
||||||
|
{
|
||||||
|
// dropping app_offline will kill the process
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostShutdownToken = deploymentResult.DeploymentResult.HostShutdownToken;
|
||||||
|
|
||||||
|
Assert.True(hostShutdownToken.WaitHandle.WaitOne(millisecondsTimeout: 1000));
|
||||||
|
Assert.True(hostShutdownToken.IsCancellationRequested);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<IISDeploymentResult> AssertStarts()
|
||||||
|
{
|
||||||
|
var deploymentParameters = Helpers.GetBaseDeploymentParameters();
|
||||||
|
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
|
||||||
|
var response = await deploymentResult.RetryingHttpClient.GetAsync("HelloWorld");
|
||||||
|
|
||||||
|
var responseText = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal("Hello World", responseText);
|
||||||
|
|
||||||
|
return deploymentResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -95,7 +95,6 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
Assert.Equal("Hello World", responseText);
|
Assert.Equal("Hello World", responseText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static TestMatrix TestVariants
|
public static TestMatrix TestVariants
|
||||||
=> TestMatrix.ForServers(ServerType.IISExpress)
|
=> TestMatrix.ForServers(ServerType.IISExpress)
|
||||||
.WithTfms(Tfm.NetCoreApp22)
|
.WithTfms(Tfm.NetCoreApp22)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace IISIntegration.FunctionalTests.Inprocess
|
||||||
|
|
||||||
[ConditionalFact]
|
[ConditionalFact]
|
||||||
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")]
|
[OSSkipCondition(OperatingSystems.Windows, WindowsVersions.Win7, "https://github.com/aspnet/IISIntegration/issues/866")]
|
||||||
public async Task Test()
|
public async Task SingleProcessTestServer_HelloWorld()
|
||||||
{
|
{
|
||||||
var helloWorld = "Hello World";
|
var helloWorld = "Hello World";
|
||||||
var expectedPath = "/Path";
|
var expectedPath = "/Path";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue