Skip to content

Commit 56c064b

Browse files
pakrymjkotalik
authored andcommitted
Display startup errors in ANCM (#8518)
1 parent eb41de8 commit 56c064b

27 files changed

+1408
-1115
lines changed

src/Hosting/Hosting/src/GenericHost/GenericWebHostedService.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ private RequestDelegate BuildErrorPageApplication(Exception exception)
186186
{
187187
context.Response.StatusCode = 500;
188188
context.Response.Headers[HeaderNames.CacheControl] = "no-cache";
189+
context.Response.ContentType = "text/html; charset=utf-8";
189190
return errorPage.ExecuteAsync(context);
190191
};
191192
}

src/Hosting/Hosting/src/Microsoft.AspNetCore.Hosting.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<ItemGroup>
1313
<Compile Include="$(SharedSourceRoot)RazorViews\*.cs" />
1414
<Compile Include="$(SharedSourceRoot)StackTrace\**\*.cs" />
15+
<Compile Include="$(SharedSourceRoot)ErrorPage\**\*.cs" />
1516
</ItemGroup>
1617

1718
<ItemGroup>

src/Hosting/Hosting/src/Resources.resx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,6 @@
117117
<resheader name="writer">
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120-
<data name="ErrorPageHtml_Title" xml:space="preserve">
121-
<value>Internal Server Error</value>
122-
</data>
123-
<data name="ErrorPageHtml_UnhandledException" xml:space="preserve">
124-
<value>An error occurred while starting the application.</value>
125-
</data>
126-
<data name="ErrorPageHtml_UnknownLocation" xml:space="preserve">
127-
<value>Unknown location</value>
128-
</data>
129120
<data name="WebHostBuilder_SingleInstance" xml:space="preserve">
130121
<value>WebHostBuilder allows creation only of a single instance of WebHost</value>
131122
</data>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#define CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY L"setCurrentDirectory"
2929
#define CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE L"disableStartUpErrorPage"
3030
#define CS_ENABLED L"enabled"
31+
#define CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK L"callStartupHook"
3132

3233
class ConfigurationSection: NonCopyable
3334
{

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

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,34 @@
99
class ServerErrorHandler : public REQUEST_HANDLER
1010
{
1111
public:
12+
ServerErrorHandler(IHttpContext& pContext, USHORT statusCode, USHORT subStatusCode, const std::string& statusText, HRESULT hr, HINSTANCE module, bool disableStartupPage, int page) noexcept
13+
: ServerErrorHandler(pContext, statusCode, subStatusCode, statusText, hr, module, disableStartupPage, page, std::vector<byte>())
14+
{
15+
}
1216

13-
ServerErrorHandler(IHttpContext &pContext, USHORT statusCode, USHORT subStatusCode, std::string statusText, HRESULT hr, HINSTANCE moduleInstance, bool disableStartupPage, int page) noexcept
17+
ServerErrorHandler(IHttpContext& pContext, USHORT statusCode, USHORT subStatusCode, const std::string& statusText, HRESULT hr, HINSTANCE module, bool disableStartupPage, int page, const std::vector<byte>& content) noexcept
1418
: REQUEST_HANDLER(pContext),
15-
m_pContext(pContext),
16-
m_HR(hr),
17-
m_disableStartupPage(disableStartupPage),
18-
m_page(page),
19-
m_moduleInstance(moduleInstance),
20-
m_statusCode(statusCode),
21-
m_subStatusCode(subStatusCode),
22-
m_statusText(std::move(statusText))
19+
m_pContext(pContext),
20+
m_HR(hr),
21+
m_disableStartupPage(disableStartupPage),
22+
m_statusCode(statusCode),
23+
m_subStatusCode(subStatusCode),
24+
m_statusText(std::move(statusText)),
25+
m_page(page),
26+
m_ExceptionInfoContent(content),
27+
m_moduleInstance(module)
2328
{
2429
}
2530

2631
REQUEST_NOTIFICATION_STATUS ExecuteRequestHandler() override
2732
{
28-
static std::string s_html500Page = GetHtml(m_moduleInstance, m_page);
29-
30-
WriteStaticResponse(m_pContext, s_html500Page, m_HR, m_disableStartupPage);
33+
WriteStaticResponse(m_pContext, m_HR, m_disableStartupPage);
3134

3235
return RQ_NOTIFICATION_FINISH_REQUEST;
3336
}
3437

3538
private:
36-
void WriteStaticResponse(IHttpContext& pContext, std::string &page, HRESULT hr, bool disableStartupErrorPage) const
39+
void WriteStaticResponse(IHttpContext& pContext, HRESULT hr, bool disableStartupErrorPage)
3740
{
3841
if (disableStartupErrorPage)
3942
{
@@ -49,14 +52,23 @@ class ServerErrorHandler : public REQUEST_HANDLER
4952
(USHORT)strlen("text/html"),
5053
FALSE
5154
);
52-
dataChunk.DataChunkType = HttpDataChunkFromMemory;
5355

54-
dataChunk.FromMemory.pBuffer = page.data();
55-
dataChunk.FromMemory.BufferLength = static_cast<ULONG>(page.size());
56+
dataChunk.DataChunkType = HttpDataChunkFromMemory;
57+
if (m_ExceptionInfoContent.size() > 0)
58+
{
59+
dataChunk.FromMemory.pBuffer = &m_ExceptionInfoContent[0];
60+
dataChunk.FromMemory.BufferLength = static_cast<ULONG>(m_ExceptionInfoContent.size());
61+
}
62+
else
63+
{
64+
static std::string s_html500Page = GetHtml(m_moduleInstance, m_page);
65+
dataChunk.FromMemory.pBuffer = s_html500Page.data();
66+
dataChunk.FromMemory.BufferLength = static_cast<ULONG>(s_html500Page.size());
67+
}
68+
5669
pResponse->WriteEntityChunkByReference(&dataChunk);
5770
}
5871

59-
static
6072
std::string
6173
GetHtml(HMODULE module, int page)
6274
{
@@ -91,12 +103,13 @@ class ServerErrorHandler : public REQUEST_HANDLER
91103
}
92104
}
93105

94-
IHttpContext &m_pContext;
106+
IHttpContext& m_pContext;
95107
HRESULT m_HR;
96108
bool m_disableStartupPage;
97109
int m_page;
98110
HINSTANCE m_moduleInstance;
99111
USHORT m_statusCode;
100112
USHORT m_subStatusCode;
101113
std::string m_statusText;
114+
std::vector<byte> m_ExceptionInfoContent;
102115
};

src/Servers/IIS/AspNetCoreModuleV2/CommonLibTests/inprocess_application_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ using ::testing::NiceMock;
1313
// Externals defined in inprocess
1414
BOOL g_fProcessDetach;
1515
HANDLE g_hEventLog;
16+
std::wstring g_exceptionEventLog;
1617

1718
namespace InprocessTests
1819
{
Binary file not shown.

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc
5757

5858
const auto handlerSettings = aspNetCoreSection->GetKeyValuePairs(CS_ASPNETCORE_HANDLER_SETTINGS);
5959
m_fSetCurrentDirectory = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY).value_or(L"true"), L"true");
60+
m_fCallStartupHook = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK).value_or(L"true"), L"true");
6061

6162
m_dwStartupTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_STARTUP_TIME_LIMIT) * 1000;
6263
m_dwShutdownTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_SHUTDOWN_TIME_LIMIT) * 1000;

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ class InProcessOptions: NonCopyable
4848
return m_fSetCurrentDirectory;
4949
}
5050

51+
bool
52+
QueryCallStartupHook() const
53+
{
54+
return m_fCallStartupHook;
55+
}
56+
5157
bool
5258
QueryWindowsAuthEnabled() const
5359
{
@@ -116,6 +122,7 @@ class InProcessOptions: NonCopyable
116122
bool m_fStdoutLogEnabled;
117123
bool m_fDisableStartUpErrorPage;
118124
bool m_fSetCurrentDirectory;
125+
bool m_fCallStartupHook;
119126
bool m_fWindowsAuthEnabled;
120127
bool m_fBasicAuthEnabled;
121128
bool m_fAnonymousAuthEnabled;

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/StartupExceptionApplication.h

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,28 @@ class StartupExceptionApplication : public InProcessApplicationBase
1515
IHttpApplication& pApplication,
1616
HINSTANCE moduleInstance,
1717
BOOL disableLogs,
18-
HRESULT hr)
18+
HRESULT hr,
19+
std::vector<byte>&& errorPageContent
20+
)
1921
: m_disableLogs(disableLogs),
2022
m_HR(hr),
2123
m_moduleInstance(moduleInstance),
24+
m_errorPageContent(std::move(errorPageContent)),
2225
InProcessApplicationBase(pServer, pApplication)
2326
{
2427
}
2528

2629
~StartupExceptionApplication() = default;
2730

28-
HRESULT CreateHandler(IHttpContext *pHttpContext, IREQUEST_HANDLER ** pRequestHandler)
31+
HRESULT CreateHandler(IHttpContext* pHttpContext, IREQUEST_HANDLER** pRequestHandler)
2932
{
30-
*pRequestHandler = new ServerErrorHandler(*pHttpContext, 500, 30, "Internal Server Error", m_HR, m_moduleInstance, m_disableLogs, IN_PROCESS_RH_STATIC_HTML);
31-
return S_OK;
32-
}
33+
*pRequestHandler = new ServerErrorHandler(*pHttpContext, 500, 30, "Internal Server Error", m_HR, m_moduleInstance, m_disableLogs, IN_PROCESS_RH_STATIC_HTML, m_errorPageContent);
3334

34-
std::string&
35-
GetStaticHtml500Content()
36-
{
37-
return html500Page;
35+
return S_OK;
3836
}
3937

4038
private:
41-
std::string html500Page;
39+
std::vector<byte> m_errorPageContent;
4240
BOOL m_disableLogs;
4341
HRESULT m_HR;
4442
HINSTANCE m_moduleInstance;

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/dllmain.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ HINSTANCE g_hWinHttpModule;
2828
HINSTANCE g_hAspNetCoreModule;
2929
HANDLE g_hEventLog = NULL;
3030
bool g_fInProcessApplicationCreated = false;
31+
std::vector<byte> g_errorPageContent;
3132
HINSTANCE g_hServerModule;
3233

3334
HRESULT
@@ -128,7 +129,7 @@ CreateApplication(
128129
std::unique_ptr<InProcessOptions> options;
129130
THROW_IF_FAILED(InProcessOptions::Create(*pServer, pSite, *pHttpApplication, options));
130131
// Set the currently running application to a fake application that returns startup exceptions.
131-
auto pErrorApplication = std::make_unique<StartupExceptionApplication>(*pServer, *pHttpApplication, g_hServerModule, options->QueryDisableStartUpErrorPage(), hr);
132+
auto pErrorApplication = std::make_unique<StartupExceptionApplication>(*pServer, *pHttpApplication, g_hServerModule, options->QueryDisableStartUpErrorPage(), hr, std::move(g_errorPageContent));
132133

133134
RETURN_IF_FAILED(pErrorApplication->StartMonitoringAppOffline());
134135
*ppApplication = pErrorApplication.release();

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,26 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
235235
LOG_INFOF(L"Setting current directory to %s", this->QueryApplicationPhysicalPath().c_str());
236236
}
237237

238-
bool clrThreadExited;
238+
if (m_pConfig->QueryCallStartupHook())
239+
{
240+
// Used to display developer exception page when there is an exception in main.
241+
auto currentStartupHookEnv = Environment::GetEnvironmentVariableValue(DOTNETCORE_STARTUP_HOOK);
239242

243+
if (currentStartupHookEnv.has_value())
244+
{
245+
currentStartupHookEnv = currentStartupHookEnv.value() + L";" + ASPNETCORE_STARTUP_ASSEMBLY;
246+
LOG_LAST_ERROR_IF(!SetEnvironmentVariable(DOTNETCORE_STARTUP_HOOK, currentStartupHookEnv.value().c_str()));
247+
}
248+
else
249+
{
250+
LOG_LAST_ERROR_IF(!SetEnvironmentVariable(DOTNETCORE_STARTUP_HOOK, ASPNETCORE_STARTUP_ASSEMBLY));
251+
}
252+
}
253+
254+
// Used to make .NET Runtime always log to event log when there is an unhandled exception.
255+
LOG_LAST_ERROR_IF(SetEnvironmentVariable(L"COMPlus_UseEntryPointFilter", L"1"));
256+
257+
bool clrThreadExited;
240258
{
241259
auto redirectionOutput = LoggingHelpers::CreateOutputs(
242260
m_pConfig->QueryStdoutLogEnabled(),

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ typedef BOOL(WINAPI * PFN_SHUTDOWN_HANDLER) (void* pvShutdownHandlerContext);
1515
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_ASYNC_COMPLETION_HANDLER)(void *pvManagedHttpContext, HRESULT hrCompletionStatus, DWORD cbCompletion);
1616
typedef void(WINAPI * PFN_REQUESTS_DRAINED_HANDLER) (void* pvShutdownHandlerContext);
1717

18+
#define DOTNETCORE_STARTUP_HOOK L"DOTNET_STARTUP_HOOKS"
19+
#define ASPNETCORE_STARTUP_ASSEMBLY L"Microsoft.AspNetCore.Server.IIS"
1820
class IN_PROCESS_APPLICATION : public InProcessApplicationBase
1921
{
2022
public:
@@ -58,7 +60,6 @@ class IN_PROCESS_APPLICATION : public InProcessApplicationBase
5860
HRESULT
5961
LoadManagedApplication();
6062

61-
6263
void
6364
QueueStop();
6465

src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
#include "inprocessapplication.h"
55
#include "inprocesshandler.h"
66
#include "requesthandler_config.h"
7+
#include "EventLog.h"
78

89
extern bool g_fInProcessApplicationCreated;
10+
extern std::vector<byte> g_errorPageContent;
11+
extern IHttpServer* g_pHttpServer;
912

1013
//
1114
// Initialization export
@@ -236,7 +239,7 @@ http_read_request_bytes(
236239
{
237240
return E_FAIL;
238241
}
239-
IHttpRequest *pHttpRequest = (IHttpRequest*)pInProcessHandler->QueryHttpContext()->GetRequest();
242+
IHttpRequest* pHttpRequest = (IHttpRequest*)pInProcessHandler->QueryHttpContext()->GetRequest();
240243

241244
// Check if there is anything to read
242245
if (pHttpRequest->GetRemainingEntityBytes() > 0)
@@ -267,7 +270,7 @@ http_write_response_bytes(
267270
_In_ BOOL* pfCompletionExpected
268271
)
269272
{
270-
IHttpResponse *pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
273+
IHttpResponse* pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
271274
BOOL fAsync = TRUE;
272275
BOOL fMoreData = TRUE;
273276
DWORD dwBytesSent = 0;
@@ -291,7 +294,7 @@ http_flush_response_bytes(
291294
_Out_ BOOL* pfCompletionExpected
292295
)
293296
{
294-
IHttpResponse *pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
297+
IHttpResponse* pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
295298

296299
BOOL fAsync = TRUE;
297300
DWORD dwBytesSent = 0;
@@ -316,7 +319,7 @@ http_websockets_read_bytes(
316319
_In_ BOOL* pfCompletionPending
317320
)
318321
{
319-
IHttpRequest3 *pHttpRequest = (IHttpRequest3*)pInProcessHandler->QueryHttpContext()->GetRequest();
322+
IHttpRequest3* pHttpRequest = (IHttpRequest3*)pInProcessHandler->QueryHttpContext()->GetRequest();
320323

321324
BOOL fAsync = TRUE;
322325

@@ -343,7 +346,7 @@ http_websockets_write_bytes(
343346
_In_ BOOL* pfCompletionExpected
344347
)
345348
{
346-
IHttpResponse2 *pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
349+
IHttpResponse2* pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
347350

348351
BOOL fAsync = TRUE;
349352
BOOL fMoreData = TRUE;
@@ -371,7 +374,7 @@ http_websockets_flush_bytes(
371374
_In_ BOOL* pfCompletionExpected
372375
)
373376
{
374-
IHttpResponse2 *pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
377+
IHttpResponse2* pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
375378

376379
BOOL fAsync = TRUE;
377380
BOOL fMoreData = TRUE;
@@ -504,4 +507,12 @@ set_main_handler(_In_ hostfxr_main_fn main)
504507
IN_PROCESS_APPLICATION::SetMainCallback(main);
505508
}
506509

510+
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
511+
VOID
512+
http_set_startup_error_page_content(_In_ byte* errorPageContent, int length)
513+
{
514+
g_errorPageContent.resize(length);
515+
memcpy(&g_errorPageContent[0], errorPageContent, length);
516+
}
517+
507518
// End of export

src/Servers/IIS/IIS/ref/Microsoft.AspNetCore.Server.IIS.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
<Reference Include="Microsoft.AspNetCore.Authentication.Core" />
99
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
1010
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
11+
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
12+
<Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" />
1113
<Reference Include="System.IO.Pipelines" />
1214
<Reference Include="System.Security.Principal.Windows" />
1315
</ItemGroup>

src/Servers/IIS/IIS/src/Microsoft.AspNetCore.Server.IIS.csproj

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>netcoreapp3.0</TargetFramework>
@@ -14,8 +14,11 @@
1414
</PropertyGroup>
1515

1616
<ItemGroup>
17-
<Compile Include="$(SharedSourceRoot)Buffers.MemoryPool\**\*.cs" />
18-
<Compile Include="$(SharedSourceRoot)HttpSys\**\*.cs" />
17+
<Compile Include="$(SharedSourceRoot)Buffers.MemoryPool\**\*.cs" LinkBase="Shared\" />
18+
<Compile Include="$(SharedSourceRoot)HttpSys\**\*.cs" LinkBase="Shared\"/>
19+
<Compile Include="$(SharedSourceRoot)StackTrace\**\*.cs" LinkBase="Shared\" />
20+
<Compile Include="$(SharedSourceRoot)RazorViews\*.cs" LinkBase="Shared\" />
21+
<Compile Include="$(SharedSourceRoot)ErrorPage\*.cs" LinkBase="Shared\" />
1922
</ItemGroup>
2023

2124
<Target Name="ValidateNativeComponentsBuilt" AfterTargets="Build" >
@@ -32,6 +35,8 @@
3235
<Reference Include="Microsoft.AspNetCore.Authentication.Core" />
3336
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
3437
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
38+
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
39+
<Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" />
3540
<Reference Include="System.IO.Pipelines" />
3641
<Reference Include="System.Security.Principal.Windows" />
3742
</ItemGroup>

0 commit comments

Comments
 (0)