Skip to content

Commit 9fded4c

Browse files
jkotalikanalogrelay
authored andcommitted
[3.1.x] Fix dotnet.exe process recovery after abnormal exit in… (#17103)
1 parent 6902b14 commit 9fded4c

File tree

8 files changed

+117
-4
lines changed

8 files changed

+117
-4
lines changed

src/Servers/IIS/AspNetCoreModuleV2/CommonLib/config_utility.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class ConfigUtility
1414
#define CS_ASPNETCORE_HANDLER_SETTINGS L"handlerSettings"
1515
#define CS_ASPNETCORE_HANDLER_VERSION L"handlerVersion"
1616
#define CS_ASPNETCORE_DEBUG_FILE L"debugFile"
17+
#define CS_ASPNETCORE_ENABLE_OUT_OF_PROCESS_CONSOLE_REDIRECTION L"enableOutOfProcessConsoleRedirection"
1718
#define CS_ASPNETCORE_DEBUG_LEVEL L"debugLevel"
1819
#define CS_ASPNETCORE_HANDLER_SETTINGS_NAME L"name"
1920
#define CS_ASPNETCORE_HANDLER_SETTINGS_VALUE L"value"
@@ -40,6 +41,13 @@ class ConfigUtility
4041
return FindKeyValuePair(pElement, CS_ASPNETCORE_DEBUG_LEVEL, strDebugFile);
4142
}
4243

44+
static
45+
HRESULT
46+
FindEnableOutOfProcessConsoleRedirection(IAppHostElement* pElement, STRU& strEnableOutOfProcessConsoleRedirection)
47+
{
48+
return FindKeyValuePair(pElement, CS_ASPNETCORE_ENABLE_OUT_OF_PROCESS_CONSOLE_REDIRECTION, strEnableOutOfProcessConsoleRedirection);
49+
}
50+
4351
private:
4452
static
4553
HRESULT

src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ PROCESS_MANAGER::GetProcess(
158158
pConfig->QueryAnonymousAuthEnabled(),
159159
pConfig->QueryEnvironmentVariables(),
160160
pConfig->QueryStdoutLogEnabled(),
161+
pConfig->QueryEnableOutOfProcessConsoleRedirection(),
161162
fWebsocketSupported,
162163
pConfig->QueryStdoutLogFile(),
163164
pConfig->QueryApplicationPhysicalPath(), // physical path

src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ SERVER_PROCESS::Initialize(
2525
BOOL fAnonymousAuthEnabled,
2626
std::map<std::wstring, std::wstring, ignore_case_comparer>& pEnvironmentVariables,
2727
BOOL fStdoutLogEnabled,
28+
BOOL fEnableOutOfProcessConsoleRedirection,
2829
BOOL fWebSocketSupported,
2930
STRU *pstruStdoutLogFile,
3031
STRU *pszAppPhysicalPath,
@@ -43,6 +44,7 @@ SERVER_PROCESS::Initialize(
4344
m_fWindowsAuthEnabled = fWindowsAuthEnabled;
4445
m_fBasicAuthEnabled = fBasicAuthEnabled;
4546
m_fAnonymousAuthEnabled = fAnonymousAuthEnabled;
47+
m_fEnableOutOfProcessConsoleRedirection = fEnableOutOfProcessConsoleRedirection;
4648
m_pProcessManager->ReferenceProcessManager();
4749
m_fDebuggerAttached = FALSE;
4850

@@ -1030,6 +1032,15 @@ SERVER_PROCESS::SetupStdHandles(
10301032
saAttr.bInheritHandle = TRUE;
10311033
saAttr.lpSecurityDescriptor = NULL;
10321034

1035+
if (!m_fEnableOutOfProcessConsoleRedirection)
1036+
{
1037+
pStartupInfo->dwFlags = STARTF_USESTDHANDLES;
1038+
pStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
1039+
pStartupInfo->hStdError = INVALID_HANDLE_VALUE;
1040+
pStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
1041+
return hr;
1042+
}
1043+
10331044
if (!m_fStdoutLogEnabled)
10341045
{
10351046
CreatePipe(&m_hStdoutHandle, &m_hStdErrWritePipe, &saAttr, 0 /*nSize*/);
@@ -1770,6 +1781,8 @@ SERVER_PROCESS::SERVER_PROCESS() :
17701781
m_dwListeningProcessId(0),
17711782
m_hListeningProcessHandle(NULL),
17721783
m_hShutdownHandle(NULL),
1784+
m_hStdErrWritePipe(NULL),
1785+
m_hReadThread(nullptr),
17731786
m_randomGenerator(std::random_device()())
17741787
{
17751788
//InterlockedIncrement(&g_dwActiveServerProcesses);
@@ -1866,13 +1879,15 @@ SERVER_PROCESS::~SERVER_PROCESS()
18661879
m_pProcessManager = NULL;
18671880
}
18681881

1869-
if (m_hStdoutHandle != NULL)
1882+
if (m_hStdErrWritePipe != NULL)
18701883
{
1871-
if (m_hStdoutHandle != INVALID_HANDLE_VALUE)
1884+
if (m_hStdErrWritePipe != INVALID_HANDLE_VALUE)
18721885
{
1873-
CloseHandle(m_hStdoutHandle);
1886+
FlushFileBuffers(m_hStdErrWritePipe);
1887+
CloseHandle(m_hStdErrWritePipe);
18741888
}
1875-
m_hStdoutHandle = NULL;
1889+
1890+
m_hStdErrWritePipe = NULL;
18761891
}
18771892

18781893
// Forces ReadFile to cancel, causing the read loop to complete.
@@ -1907,6 +1922,15 @@ SERVER_PROCESS::~SERVER_PROCESS()
19071922
m_hReadThread = nullptr;
19081923
}
19091924

1925+
if (m_hStdoutHandle != NULL)
1926+
{
1927+
if (m_hStdoutHandle != INVALID_HANDLE_VALUE)
1928+
{
1929+
CloseHandle(m_hStdoutHandle);
1930+
}
1931+
m_hStdoutHandle = NULL;
1932+
}
1933+
19101934
if (m_fStdoutLogEnabled)
19111935
{
19121936
m_Timer.CancelTimer();

src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class SERVER_PROCESS
3737
_In_ BOOL fAnonymousAuthEnabled,
3838
_In_ std::map<std::wstring, std::wstring, ignore_case_comparer>& pEnvironmentVariables,
3939
_In_ BOOL fStdoutLogEnabled,
40+
_In_ BOOL fDisableRedirection,
4041
_In_ BOOL fWebSocketSupported,
4142
_In_ STRU *pstruStdoutLogFile,
4243
_In_ STRU *pszAppPhysicalPath,
@@ -253,6 +254,7 @@ class SERVER_PROCESS
253254
BOOL m_fBasicAuthEnabled;
254255
BOOL m_fAnonymousAuthEnabled;
255256
BOOL m_fDebuggerAttached;
257+
BOOL m_fEnableOutOfProcessConsoleRedirection;
256258

257259
STTIMER m_Timer;
258260
SOCKET m_socket;

src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,12 @@ REQUESTHANDLER_CONFIG::Populate(
379379
goto Finished;
380380
}
381381

382+
hr = ConfigUtility::FindEnableOutOfProcessConsoleRedirection(pAspNetCoreElement, m_fEnableOutOfProcessConsoleRedirection);
383+
if (FAILED(hr))
384+
{
385+
goto Finished;
386+
}
387+
382388
Finished:
383389

384390
if (pAspNetCoreElement != NULL)

src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,12 @@ class REQUESTHANDLER_CONFIG
218218
return &m_struConfigPath;
219219
}
220220

221+
BOOL
222+
QueryEnableOutOfProcessConsoleRedirection()
223+
{
224+
return !m_fEnableOutOfProcessConsoleRedirection.Equals(L"false", 1);
225+
}
226+
221227
protected:
222228

223229
//
@@ -255,6 +261,7 @@ class REQUESTHANDLER_CONFIG
255261
BOOL m_fWindowsAuthEnabled;
256262
BOOL m_fBasicAuthEnabled;
257263
BOOL m_fAnonymousAuthEnabled;
264+
STRU m_fEnableOutOfProcessConsoleRedirection;
258265
APP_HOSTING_MODEL m_hostingModel;
259266
std::map<std::wstring, std::wstring, ignore_case_comparer> m_pEnvironmentVariables;
260267
STRU m_struHostFxrLocation;

src/Servers/IIS/IIS/test/Common.FunctionalTests/LogFileTests.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,22 @@ public async Task CaptureLogsForOutOfProcessWhenProcessFailsToStart()
233233
EventLogHelpers.VerifyEventLogEvent(deploymentResult, EventLogHelpers.OutOfProcessFailedToStart(deploymentResult, "Wow!"), Logger);
234234
}
235235

236+
[ConditionalFact]
237+
[RequiresNewShim]
238+
public async Task DisableRedirectionNoLogs()
239+
{
240+
var deploymentParameters = Fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess);
241+
deploymentParameters.HandlerSettings["enableOutOfProcessConsoleRedirection"] = "false";
242+
deploymentParameters.TransformArguments((a, _) => $"{a} ConsoleWriteSingle");
243+
var deploymentResult = await DeployAsync(deploymentParameters);
244+
245+
var response = await deploymentResult.HttpClient.GetAsync("Test");
246+
247+
StopServer();
248+
249+
EventLogHelpers.VerifyEventLogEvent(deploymentResult, EventLogHelpers.OutOfProcessFailedToStart(deploymentResult, ""), Logger);
250+
}
251+
236252
[ConditionalFact]
237253
public async Task CaptureLogsForOutOfProcessWhenProcessFailsToStart30KbMax()
238254
{

src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/AspNetCorePortTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,55 @@ public async Task EnvVarInWebConfig_Invalid(TestVariant variant, string port)
8484
Assert.Equal(HttpStatusCode.BadGateway, response.StatusCode);
8585
}
8686

87+
[ConditionalTheory]
88+
[MemberData(nameof(TestVariants))]
89+
[RequiresNewShim]
90+
public async Task ShutdownMultipleTimesWorks(TestVariant variant)
91+
{
92+
// Must publish to set env vars in web.config
93+
var deploymentParameters = Fixture.GetBaseDeploymentParameters(variant);
94+
95+
var deploymentResult = await DeployAsync(deploymentParameters);
96+
97+
// Shutdown once
98+
var response = await deploymentResult.HttpClient.GetAsync("/Shutdown");
99+
100+
// Wait for server to start again.
101+
int i;
102+
for (i = 0; i < 10; i++)
103+
{
104+
// ANCM should eventually recover from being shutdown multiple times.
105+
response = await deploymentResult.HttpClient.GetAsync("/HelloWorld");
106+
if (response.IsSuccessStatusCode)
107+
{
108+
break;
109+
}
110+
}
111+
112+
if (i == 10)
113+
{
114+
// Didn't restart after 10 retries
115+
Assert.False(true);
116+
}
117+
118+
// Shutdown again
119+
response = await deploymentResult.HttpClient.GetAsync("/Shutdown");
120+
121+
// return if server starts again.
122+
for (i = 0; i < 10; i++)
123+
{
124+
// ANCM should eventually recover from being shutdown multiple times.
125+
response = await deploymentResult.HttpClient.GetAsync("/HelloWorld");
126+
if (response.IsSuccessStatusCode)
127+
{
128+
return;
129+
}
130+
}
131+
132+
// Test failure if this happens.
133+
Assert.False(true);
134+
}
135+
87136
private static int GetUnusedRandomPort()
88137
{
89138
// Large number of retries to prevent test failures due to port collisions, but not infinite

0 commit comments

Comments
 (0)