[3.1.x] Fix dotnet.exe process recovery after abnormal exit in… (#17103)

This commit is contained in:
Justin Kotalik 2019-11-22 15:50:31 -08:00 committed by Andrew Stanton-Nurse
parent 6902b14471
commit 9fded4c4aa
8 changed files with 117 additions and 4 deletions

View File

@ -14,6 +14,7 @@ class ConfigUtility
#define CS_ASPNETCORE_HANDLER_SETTINGS L"handlerSettings"
#define CS_ASPNETCORE_HANDLER_VERSION L"handlerVersion"
#define CS_ASPNETCORE_DEBUG_FILE L"debugFile"
#define CS_ASPNETCORE_ENABLE_OUT_OF_PROCESS_CONSOLE_REDIRECTION L"enableOutOfProcessConsoleRedirection"
#define CS_ASPNETCORE_DEBUG_LEVEL L"debugLevel"
#define CS_ASPNETCORE_HANDLER_SETTINGS_NAME L"name"
#define CS_ASPNETCORE_HANDLER_SETTINGS_VALUE L"value"
@ -40,6 +41,13 @@ public:
return FindKeyValuePair(pElement, CS_ASPNETCORE_DEBUG_LEVEL, strDebugFile);
}
static
HRESULT
FindEnableOutOfProcessConsoleRedirection(IAppHostElement* pElement, STRU& strEnableOutOfProcessConsoleRedirection)
{
return FindKeyValuePair(pElement, CS_ASPNETCORE_ENABLE_OUT_OF_PROCESS_CONSOLE_REDIRECTION, strEnableOutOfProcessConsoleRedirection);
}
private:
static
HRESULT

View File

@ -158,6 +158,7 @@ PROCESS_MANAGER::GetProcess(
pConfig->QueryAnonymousAuthEnabled(),
pConfig->QueryEnvironmentVariables(),
pConfig->QueryStdoutLogEnabled(),
pConfig->QueryEnableOutOfProcessConsoleRedirection(),
fWebsocketSupported,
pConfig->QueryStdoutLogFile(),
pConfig->QueryApplicationPhysicalPath(), // physical path

View File

@ -25,6 +25,7 @@ SERVER_PROCESS::Initialize(
BOOL fAnonymousAuthEnabled,
std::map<std::wstring, std::wstring, ignore_case_comparer>& pEnvironmentVariables,
BOOL fStdoutLogEnabled,
BOOL fEnableOutOfProcessConsoleRedirection,
BOOL fWebSocketSupported,
STRU *pstruStdoutLogFile,
STRU *pszAppPhysicalPath,
@ -43,6 +44,7 @@ SERVER_PROCESS::Initialize(
m_fWindowsAuthEnabled = fWindowsAuthEnabled;
m_fBasicAuthEnabled = fBasicAuthEnabled;
m_fAnonymousAuthEnabled = fAnonymousAuthEnabled;
m_fEnableOutOfProcessConsoleRedirection = fEnableOutOfProcessConsoleRedirection;
m_pProcessManager->ReferenceProcessManager();
m_fDebuggerAttached = FALSE;
@ -1030,6 +1032,15 @@ SERVER_PROCESS::SetupStdHandles(
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
if (!m_fEnableOutOfProcessConsoleRedirection)
{
pStartupInfo->dwFlags = STARTF_USESTDHANDLES;
pStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
pStartupInfo->hStdError = INVALID_HANDLE_VALUE;
pStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
return hr;
}
if (!m_fStdoutLogEnabled)
{
CreatePipe(&m_hStdoutHandle, &m_hStdErrWritePipe, &saAttr, 0 /*nSize*/);
@ -1770,6 +1781,8 @@ SERVER_PROCESS::SERVER_PROCESS() :
m_dwListeningProcessId(0),
m_hListeningProcessHandle(NULL),
m_hShutdownHandle(NULL),
m_hStdErrWritePipe(NULL),
m_hReadThread(nullptr),
m_randomGenerator(std::random_device()())
{
//InterlockedIncrement(&g_dwActiveServerProcesses);
@ -1866,13 +1879,15 @@ SERVER_PROCESS::~SERVER_PROCESS()
m_pProcessManager = NULL;
}
if (m_hStdoutHandle != NULL)
if (m_hStdErrWritePipe != NULL)
{
if (m_hStdoutHandle != INVALID_HANDLE_VALUE)
if (m_hStdErrWritePipe != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hStdoutHandle);
FlushFileBuffers(m_hStdErrWritePipe);
CloseHandle(m_hStdErrWritePipe);
}
m_hStdoutHandle = NULL;
m_hStdErrWritePipe = NULL;
}
// Forces ReadFile to cancel, causing the read loop to complete.
@ -1907,6 +1922,15 @@ SERVER_PROCESS::~SERVER_PROCESS()
m_hReadThread = nullptr;
}
if (m_hStdoutHandle != NULL)
{
if (m_hStdoutHandle != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hStdoutHandle);
}
m_hStdoutHandle = NULL;
}
if (m_fStdoutLogEnabled)
{
m_Timer.CancelTimer();

View File

@ -37,6 +37,7 @@ public:
_In_ BOOL fAnonymousAuthEnabled,
_In_ std::map<std::wstring, std::wstring, ignore_case_comparer>& pEnvironmentVariables,
_In_ BOOL fStdoutLogEnabled,
_In_ BOOL fDisableRedirection,
_In_ BOOL fWebSocketSupported,
_In_ STRU *pstruStdoutLogFile,
_In_ STRU *pszAppPhysicalPath,
@ -253,6 +254,7 @@ private:
BOOL m_fBasicAuthEnabled;
BOOL m_fAnonymousAuthEnabled;
BOOL m_fDebuggerAttached;
BOOL m_fEnableOutOfProcessConsoleRedirection;
STTIMER m_Timer;
SOCKET m_socket;

View File

@ -379,6 +379,12 @@ REQUESTHANDLER_CONFIG::Populate(
goto Finished;
}
hr = ConfigUtility::FindEnableOutOfProcessConsoleRedirection(pAspNetCoreElement, m_fEnableOutOfProcessConsoleRedirection);
if (FAILED(hr))
{
goto Finished;
}
Finished:
if (pAspNetCoreElement != NULL)

View File

@ -218,6 +218,12 @@ public:
return &m_struConfigPath;
}
BOOL
QueryEnableOutOfProcessConsoleRedirection()
{
return !m_fEnableOutOfProcessConsoleRedirection.Equals(L"false", 1);
}
protected:
//
@ -255,6 +261,7 @@ protected:
BOOL m_fWindowsAuthEnabled;
BOOL m_fBasicAuthEnabled;
BOOL m_fAnonymousAuthEnabled;
STRU m_fEnableOutOfProcessConsoleRedirection;
APP_HOSTING_MODEL m_hostingModel;
std::map<std::wstring, std::wstring, ignore_case_comparer> m_pEnvironmentVariables;
STRU m_struHostFxrLocation;

View File

@ -233,6 +233,22 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
EventLogHelpers.VerifyEventLogEvent(deploymentResult, EventLogHelpers.OutOfProcessFailedToStart(deploymentResult, "Wow!"), Logger);
}
[ConditionalFact]
[RequiresNewShim]
public async Task DisableRedirectionNoLogs()
{
var deploymentParameters = Fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess);
deploymentParameters.HandlerSettings["enableOutOfProcessConsoleRedirection"] = "false";
deploymentParameters.TransformArguments((a, _) => $"{a} ConsoleWriteSingle");
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await deploymentResult.HttpClient.GetAsync("Test");
StopServer();
EventLogHelpers.VerifyEventLogEvent(deploymentResult, EventLogHelpers.OutOfProcessFailedToStart(deploymentResult, ""), Logger);
}
[ConditionalFact]
public async Task CaptureLogsForOutOfProcessWhenProcessFailsToStart30KbMax()
{

View File

@ -84,6 +84,55 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.OutOfProcess
Assert.Equal(HttpStatusCode.BadGateway, response.StatusCode);
}
[ConditionalTheory]
[MemberData(nameof(TestVariants))]
[RequiresNewShim]
public async Task ShutdownMultipleTimesWorks(TestVariant variant)
{
// Must publish to set env vars in web.config
var deploymentParameters = Fixture.GetBaseDeploymentParameters(variant);
var deploymentResult = await DeployAsync(deploymentParameters);
// Shutdown once
var response = await deploymentResult.HttpClient.GetAsync("/Shutdown");
// Wait for server to start again.
int i;
for (i = 0; i < 10; i++)
{
// ANCM should eventually recover from being shutdown multiple times.
response = await deploymentResult.HttpClient.GetAsync("/HelloWorld");
if (response.IsSuccessStatusCode)
{
break;
}
}
if (i == 10)
{
// Didn't restart after 10 retries
Assert.False(true);
}
// Shutdown again
response = await deploymentResult.HttpClient.GetAsync("/Shutdown");
// return if server starts again.
for (i = 0; i < 10; i++)
{
// ANCM should eventually recover from being shutdown multiple times.
response = await deploymentResult.HttpClient.GetAsync("/HelloWorld");
if (response.IsSuccessStatusCode)
{
return;
}
}
// Test failure if this happens.
Assert.False(true);
}
private static int GetUnusedRandomPort()
{
// Large number of retries to prevent test failures due to port collisions, but not infinite