Display startup errors in ANCM (#8518)
This commit is contained in:
parent
eb41de88a9
commit
56c064bd53
|
|
@ -186,6 +186,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = 500;
|
context.Response.StatusCode = 500;
|
||||||
context.Response.Headers[HeaderNames.CacheControl] = "no-cache";
|
context.Response.Headers[HeaderNames.CacheControl] = "no-cache";
|
||||||
|
context.Response.ContentType = "text/html; charset=utf-8";
|
||||||
return errorPage.ExecuteAsync(context);
|
return errorPage.ExecuteAsync(context);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="$(SharedSourceRoot)RazorViews\*.cs" />
|
<Compile Include="$(SharedSourceRoot)RazorViews\*.cs" />
|
||||||
<Compile Include="$(SharedSourceRoot)StackTrace\**\*.cs" />
|
<Compile Include="$(SharedSourceRoot)StackTrace\**\*.cs" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)ErrorPage\**\*.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -117,15 +117,6 @@
|
||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="ErrorPageHtml_Title" xml:space="preserve">
|
|
||||||
<value>Internal Server Error</value>
|
|
||||||
</data>
|
|
||||||
<data name="ErrorPageHtml_UnhandledException" xml:space="preserve">
|
|
||||||
<value>An error occurred while starting the application.</value>
|
|
||||||
</data>
|
|
||||||
<data name="ErrorPageHtml_UnknownLocation" xml:space="preserve">
|
|
||||||
<value>Unknown location</value>
|
|
||||||
</data>
|
|
||||||
<data name="WebHostBuilder_SingleInstance" xml:space="preserve">
|
<data name="WebHostBuilder_SingleInstance" xml:space="preserve">
|
||||||
<value>WebHostBuilder allows creation only of a single instance of WebHost</value>
|
<value>WebHostBuilder allows creation only of a single instance of WebHost</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#define CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY L"setCurrentDirectory"
|
#define CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY L"setCurrentDirectory"
|
||||||
#define CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE L"disableStartUpErrorPage"
|
#define CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE L"disableStartUpErrorPage"
|
||||||
#define CS_ENABLED L"enabled"
|
#define CS_ENABLED L"enabled"
|
||||||
|
#define CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK L"callStartupHook"
|
||||||
|
|
||||||
class ConfigurationSection: NonCopyable
|
class ConfigurationSection: NonCopyable
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -9,31 +9,34 @@
|
||||||
class ServerErrorHandler : public REQUEST_HANDLER
|
class ServerErrorHandler : public REQUEST_HANDLER
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ServerErrorHandler(IHttpContext& pContext, USHORT statusCode, USHORT subStatusCode, const std::string& statusText, HRESULT hr, HINSTANCE module, bool disableStartupPage, int page) noexcept
|
||||||
|
: ServerErrorHandler(pContext, statusCode, subStatusCode, statusText, hr, module, disableStartupPage, page, std::vector<byte>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
ServerErrorHandler(IHttpContext &pContext, USHORT statusCode, USHORT subStatusCode, std::string statusText, HRESULT hr, HINSTANCE moduleInstance, bool disableStartupPage, int page) noexcept
|
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
|
||||||
: REQUEST_HANDLER(pContext),
|
: REQUEST_HANDLER(pContext),
|
||||||
m_pContext(pContext),
|
m_pContext(pContext),
|
||||||
m_HR(hr),
|
m_HR(hr),
|
||||||
m_disableStartupPage(disableStartupPage),
|
m_disableStartupPage(disableStartupPage),
|
||||||
m_page(page),
|
m_statusCode(statusCode),
|
||||||
m_moduleInstance(moduleInstance),
|
m_subStatusCode(subStatusCode),
|
||||||
m_statusCode(statusCode),
|
m_statusText(std::move(statusText)),
|
||||||
m_subStatusCode(subStatusCode),
|
m_page(page),
|
||||||
m_statusText(std::move(statusText))
|
m_ExceptionInfoContent(content),
|
||||||
|
m_moduleInstance(module)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
REQUEST_NOTIFICATION_STATUS ExecuteRequestHandler() override
|
REQUEST_NOTIFICATION_STATUS ExecuteRequestHandler() override
|
||||||
{
|
{
|
||||||
static std::string s_html500Page = GetHtml(m_moduleInstance, m_page);
|
WriteStaticResponse(m_pContext, m_HR, m_disableStartupPage);
|
||||||
|
|
||||||
WriteStaticResponse(m_pContext, s_html500Page, m_HR, m_disableStartupPage);
|
|
||||||
|
|
||||||
return RQ_NOTIFICATION_FINISH_REQUEST;
|
return RQ_NOTIFICATION_FINISH_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void WriteStaticResponse(IHttpContext& pContext, std::string &page, HRESULT hr, bool disableStartupErrorPage) const
|
void WriteStaticResponse(IHttpContext& pContext, HRESULT hr, bool disableStartupErrorPage)
|
||||||
{
|
{
|
||||||
if (disableStartupErrorPage)
|
if (disableStartupErrorPage)
|
||||||
{
|
{
|
||||||
|
|
@ -49,14 +52,23 @@ private:
|
||||||
(USHORT)strlen("text/html"),
|
(USHORT)strlen("text/html"),
|
||||||
FALSE
|
FALSE
|
||||||
);
|
);
|
||||||
dataChunk.DataChunkType = HttpDataChunkFromMemory;
|
|
||||||
|
|
||||||
dataChunk.FromMemory.pBuffer = page.data();
|
dataChunk.DataChunkType = HttpDataChunkFromMemory;
|
||||||
dataChunk.FromMemory.BufferLength = static_cast<ULONG>(page.size());
|
if (m_ExceptionInfoContent.size() > 0)
|
||||||
|
{
|
||||||
|
dataChunk.FromMemory.pBuffer = &m_ExceptionInfoContent[0];
|
||||||
|
dataChunk.FromMemory.BufferLength = static_cast<ULONG>(m_ExceptionInfoContent.size());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static std::string s_html500Page = GetHtml(m_moduleInstance, m_page);
|
||||||
|
dataChunk.FromMemory.pBuffer = s_html500Page.data();
|
||||||
|
dataChunk.FromMemory.BufferLength = static_cast<ULONG>(s_html500Page.size());
|
||||||
|
}
|
||||||
|
|
||||||
pResponse->WriteEntityChunkByReference(&dataChunk);
|
pResponse->WriteEntityChunkByReference(&dataChunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
|
||||||
std::string
|
std::string
|
||||||
GetHtml(HMODULE module, int page)
|
GetHtml(HMODULE module, int page)
|
||||||
{
|
{
|
||||||
|
|
@ -91,7 +103,7 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IHttpContext &m_pContext;
|
IHttpContext& m_pContext;
|
||||||
HRESULT m_HR;
|
HRESULT m_HR;
|
||||||
bool m_disableStartupPage;
|
bool m_disableStartupPage;
|
||||||
int m_page;
|
int m_page;
|
||||||
|
|
@ -99,4 +111,5 @@ private:
|
||||||
USHORT m_statusCode;
|
USHORT m_statusCode;
|
||||||
USHORT m_subStatusCode;
|
USHORT m_subStatusCode;
|
||||||
std::string m_statusText;
|
std::string m_statusText;
|
||||||
|
std::vector<byte> m_ExceptionInfoContent;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ using ::testing::NiceMock;
|
||||||
// Externals defined in inprocess
|
// Externals defined in inprocess
|
||||||
BOOL g_fProcessDetach;
|
BOOL g_fProcessDetach;
|
||||||
HANDLE g_hEventLog;
|
HANDLE g_hEventLog;
|
||||||
|
std::wstring g_exceptionEventLog;
|
||||||
|
|
||||||
namespace InprocessTests
|
namespace InprocessTests
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -57,6 +57,7 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc
|
||||||
|
|
||||||
const auto handlerSettings = aspNetCoreSection->GetKeyValuePairs(CS_ASPNETCORE_HANDLER_SETTINGS);
|
const auto handlerSettings = aspNetCoreSection->GetKeyValuePairs(CS_ASPNETCORE_HANDLER_SETTINGS);
|
||||||
m_fSetCurrentDirectory = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY).value_or(L"true"), L"true");
|
m_fSetCurrentDirectory = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY).value_or(L"true"), L"true");
|
||||||
|
m_fCallStartupHook = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK).value_or(L"true"), L"true");
|
||||||
|
|
||||||
m_dwStartupTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_STARTUP_TIME_LIMIT) * 1000;
|
m_dwStartupTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_STARTUP_TIME_LIMIT) * 1000;
|
||||||
m_dwShutdownTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_SHUTDOWN_TIME_LIMIT) * 1000;
|
m_dwShutdownTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_SHUTDOWN_TIME_LIMIT) * 1000;
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,12 @@ public:
|
||||||
return m_fSetCurrentDirectory;
|
return m_fSetCurrentDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
QueryCallStartupHook() const
|
||||||
|
{
|
||||||
|
return m_fCallStartupHook;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
QueryWindowsAuthEnabled() const
|
QueryWindowsAuthEnabled() const
|
||||||
{
|
{
|
||||||
|
|
@ -116,6 +122,7 @@ private:
|
||||||
bool m_fStdoutLogEnabled;
|
bool m_fStdoutLogEnabled;
|
||||||
bool m_fDisableStartUpErrorPage;
|
bool m_fDisableStartUpErrorPage;
|
||||||
bool m_fSetCurrentDirectory;
|
bool m_fSetCurrentDirectory;
|
||||||
|
bool m_fCallStartupHook;
|
||||||
bool m_fWindowsAuthEnabled;
|
bool m_fWindowsAuthEnabled;
|
||||||
bool m_fBasicAuthEnabled;
|
bool m_fBasicAuthEnabled;
|
||||||
bool m_fAnonymousAuthEnabled;
|
bool m_fAnonymousAuthEnabled;
|
||||||
|
|
|
||||||
|
|
@ -15,30 +15,28 @@ public:
|
||||||
IHttpApplication& pApplication,
|
IHttpApplication& pApplication,
|
||||||
HINSTANCE moduleInstance,
|
HINSTANCE moduleInstance,
|
||||||
BOOL disableLogs,
|
BOOL disableLogs,
|
||||||
HRESULT hr)
|
HRESULT hr,
|
||||||
|
std::vector<byte>&& errorPageContent
|
||||||
|
)
|
||||||
: m_disableLogs(disableLogs),
|
: m_disableLogs(disableLogs),
|
||||||
m_HR(hr),
|
m_HR(hr),
|
||||||
m_moduleInstance(moduleInstance),
|
m_moduleInstance(moduleInstance),
|
||||||
|
m_errorPageContent(std::move(errorPageContent)),
|
||||||
InProcessApplicationBase(pServer, pApplication)
|
InProcessApplicationBase(pServer, pApplication)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~StartupExceptionApplication() = default;
|
~StartupExceptionApplication() = default;
|
||||||
|
|
||||||
HRESULT CreateHandler(IHttpContext *pHttpContext, IREQUEST_HANDLER ** pRequestHandler)
|
HRESULT CreateHandler(IHttpContext* pHttpContext, IREQUEST_HANDLER** pRequestHandler)
|
||||||
{
|
{
|
||||||
*pRequestHandler = new ServerErrorHandler(*pHttpContext, 500, 30, "Internal Server Error", m_HR, m_moduleInstance, m_disableLogs, IN_PROCESS_RH_STATIC_HTML);
|
*pRequestHandler = new ServerErrorHandler(*pHttpContext, 500, 30, "Internal Server Error", m_HR, m_moduleInstance, m_disableLogs, IN_PROCESS_RH_STATIC_HTML, m_errorPageContent);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string&
|
|
||||||
GetStaticHtml500Content()
|
|
||||||
{
|
|
||||||
return html500Page;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string html500Page;
|
std::vector<byte> m_errorPageContent;
|
||||||
BOOL m_disableLogs;
|
BOOL m_disableLogs;
|
||||||
HRESULT m_HR;
|
HRESULT m_HR;
|
||||||
HINSTANCE m_moduleInstance;
|
HINSTANCE m_moduleInstance;
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ HINSTANCE g_hWinHttpModule;
|
||||||
HINSTANCE g_hAspNetCoreModule;
|
HINSTANCE g_hAspNetCoreModule;
|
||||||
HANDLE g_hEventLog = NULL;
|
HANDLE g_hEventLog = NULL;
|
||||||
bool g_fInProcessApplicationCreated = false;
|
bool g_fInProcessApplicationCreated = false;
|
||||||
|
std::vector<byte> g_errorPageContent;
|
||||||
HINSTANCE g_hServerModule;
|
HINSTANCE g_hServerModule;
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
|
|
@ -128,7 +129,7 @@ CreateApplication(
|
||||||
std::unique_ptr<InProcessOptions> options;
|
std::unique_ptr<InProcessOptions> options;
|
||||||
THROW_IF_FAILED(InProcessOptions::Create(*pServer, pSite, *pHttpApplication, options));
|
THROW_IF_FAILED(InProcessOptions::Create(*pServer, pSite, *pHttpApplication, options));
|
||||||
// Set the currently running application to a fake application that returns startup exceptions.
|
// Set the currently running application to a fake application that returns startup exceptions.
|
||||||
auto pErrorApplication = std::make_unique<StartupExceptionApplication>(*pServer, *pHttpApplication, g_hServerModule, options->QueryDisableStartUpErrorPage(), hr);
|
auto pErrorApplication = std::make_unique<StartupExceptionApplication>(*pServer, *pHttpApplication, g_hServerModule, options->QueryDisableStartUpErrorPage(), hr, std::move(g_errorPageContent));
|
||||||
|
|
||||||
RETURN_IF_FAILED(pErrorApplication->StartMonitoringAppOffline());
|
RETURN_IF_FAILED(pErrorApplication->StartMonitoringAppOffline());
|
||||||
*ppApplication = pErrorApplication.release();
|
*ppApplication = pErrorApplication.release();
|
||||||
|
|
|
||||||
|
|
@ -235,8 +235,26 @@ IN_PROCESS_APPLICATION::ExecuteApplication()
|
||||||
LOG_INFOF(L"Setting current directory to %s", this->QueryApplicationPhysicalPath().c_str());
|
LOG_INFOF(L"Setting current directory to %s", this->QueryApplicationPhysicalPath().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool clrThreadExited;
|
if (m_pConfig->QueryCallStartupHook())
|
||||||
|
{
|
||||||
|
// Used to display developer exception page when there is an exception in main.
|
||||||
|
auto currentStartupHookEnv = Environment::GetEnvironmentVariableValue(DOTNETCORE_STARTUP_HOOK);
|
||||||
|
|
||||||
|
if (currentStartupHookEnv.has_value())
|
||||||
|
{
|
||||||
|
currentStartupHookEnv = currentStartupHookEnv.value() + L";" + ASPNETCORE_STARTUP_ASSEMBLY;
|
||||||
|
LOG_LAST_ERROR_IF(!SetEnvironmentVariable(DOTNETCORE_STARTUP_HOOK, currentStartupHookEnv.value().c_str()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_LAST_ERROR_IF(!SetEnvironmentVariable(DOTNETCORE_STARTUP_HOOK, ASPNETCORE_STARTUP_ASSEMBLY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to make .NET Runtime always log to event log when there is an unhandled exception.
|
||||||
|
LOG_LAST_ERROR_IF(SetEnvironmentVariable(L"COMPlus_UseEntryPointFilter", L"1"));
|
||||||
|
|
||||||
|
bool clrThreadExited;
|
||||||
{
|
{
|
||||||
auto redirectionOutput = LoggingHelpers::CreateOutputs(
|
auto redirectionOutput = LoggingHelpers::CreateOutputs(
|
||||||
m_pConfig->QueryStdoutLogEnabled(),
|
m_pConfig->QueryStdoutLogEnabled(),
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ typedef BOOL(WINAPI * PFN_SHUTDOWN_HANDLER) (void* pvShutdownHandlerContext);
|
||||||
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_ASYNC_COMPLETION_HANDLER)(void *pvManagedHttpContext, HRESULT hrCompletionStatus, DWORD cbCompletion);
|
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_ASYNC_COMPLETION_HANDLER)(void *pvManagedHttpContext, HRESULT hrCompletionStatus, DWORD cbCompletion);
|
||||||
typedef void(WINAPI * PFN_REQUESTS_DRAINED_HANDLER) (void* pvShutdownHandlerContext);
|
typedef void(WINAPI * PFN_REQUESTS_DRAINED_HANDLER) (void* pvShutdownHandlerContext);
|
||||||
|
|
||||||
|
#define DOTNETCORE_STARTUP_HOOK L"DOTNET_STARTUP_HOOKS"
|
||||||
|
#define ASPNETCORE_STARTUP_ASSEMBLY L"Microsoft.AspNetCore.Server.IIS"
|
||||||
class IN_PROCESS_APPLICATION : public InProcessApplicationBase
|
class IN_PROCESS_APPLICATION : public InProcessApplicationBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -58,7 +60,6 @@ public:
|
||||||
HRESULT
|
HRESULT
|
||||||
LoadManagedApplication();
|
LoadManagedApplication();
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
QueueStop();
|
QueueStop();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,11 @@
|
||||||
#include "inprocessapplication.h"
|
#include "inprocessapplication.h"
|
||||||
#include "inprocesshandler.h"
|
#include "inprocesshandler.h"
|
||||||
#include "requesthandler_config.h"
|
#include "requesthandler_config.h"
|
||||||
|
#include "EventLog.h"
|
||||||
|
|
||||||
extern bool g_fInProcessApplicationCreated;
|
extern bool g_fInProcessApplicationCreated;
|
||||||
|
extern std::vector<byte> g_errorPageContent;
|
||||||
|
extern IHttpServer* g_pHttpServer;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Initialization export
|
// Initialization export
|
||||||
|
|
@ -236,7 +239,7 @@ http_read_request_bytes(
|
||||||
{
|
{
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
IHttpRequest *pHttpRequest = (IHttpRequest*)pInProcessHandler->QueryHttpContext()->GetRequest();
|
IHttpRequest* pHttpRequest = (IHttpRequest*)pInProcessHandler->QueryHttpContext()->GetRequest();
|
||||||
|
|
||||||
// Check if there is anything to read
|
// Check if there is anything to read
|
||||||
if (pHttpRequest->GetRemainingEntityBytes() > 0)
|
if (pHttpRequest->GetRemainingEntityBytes() > 0)
|
||||||
|
|
@ -267,7 +270,7 @@ http_write_response_bytes(
|
||||||
_In_ BOOL* pfCompletionExpected
|
_In_ BOOL* pfCompletionExpected
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
IHttpResponse *pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
|
IHttpResponse* pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
|
||||||
BOOL fAsync = TRUE;
|
BOOL fAsync = TRUE;
|
||||||
BOOL fMoreData = TRUE;
|
BOOL fMoreData = TRUE;
|
||||||
DWORD dwBytesSent = 0;
|
DWORD dwBytesSent = 0;
|
||||||
|
|
@ -291,7 +294,7 @@ http_flush_response_bytes(
|
||||||
_Out_ BOOL* pfCompletionExpected
|
_Out_ BOOL* pfCompletionExpected
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
IHttpResponse *pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
|
IHttpResponse* pHttpResponse = (IHttpResponse*)pInProcessHandler->QueryHttpContext()->GetResponse();
|
||||||
|
|
||||||
BOOL fAsync = TRUE;
|
BOOL fAsync = TRUE;
|
||||||
DWORD dwBytesSent = 0;
|
DWORD dwBytesSent = 0;
|
||||||
|
|
@ -316,7 +319,7 @@ http_websockets_read_bytes(
|
||||||
_In_ BOOL* pfCompletionPending
|
_In_ BOOL* pfCompletionPending
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
IHttpRequest3 *pHttpRequest = (IHttpRequest3*)pInProcessHandler->QueryHttpContext()->GetRequest();
|
IHttpRequest3* pHttpRequest = (IHttpRequest3*)pInProcessHandler->QueryHttpContext()->GetRequest();
|
||||||
|
|
||||||
BOOL fAsync = TRUE;
|
BOOL fAsync = TRUE;
|
||||||
|
|
||||||
|
|
@ -343,7 +346,7 @@ http_websockets_write_bytes(
|
||||||
_In_ BOOL* pfCompletionExpected
|
_In_ BOOL* pfCompletionExpected
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
IHttpResponse2 *pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
|
IHttpResponse2* pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
|
||||||
|
|
||||||
BOOL fAsync = TRUE;
|
BOOL fAsync = TRUE;
|
||||||
BOOL fMoreData = TRUE;
|
BOOL fMoreData = TRUE;
|
||||||
|
|
@ -371,7 +374,7 @@ http_websockets_flush_bytes(
|
||||||
_In_ BOOL* pfCompletionExpected
|
_In_ BOOL* pfCompletionExpected
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
IHttpResponse2 *pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
|
IHttpResponse2* pHttpResponse = (IHttpResponse2*)pInProcessHandler->QueryHttpContext()->GetResponse();
|
||||||
|
|
||||||
BOOL fAsync = TRUE;
|
BOOL fAsync = TRUE;
|
||||||
BOOL fMoreData = TRUE;
|
BOOL fMoreData = TRUE;
|
||||||
|
|
@ -504,4 +507,12 @@ set_main_handler(_In_ hostfxr_main_fn main)
|
||||||
IN_PROCESS_APPLICATION::SetMainCallback(main);
|
IN_PROCESS_APPLICATION::SetMainCallback(main);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXTERN_C __MIDL_DECLSPEC_DLLEXPORT
|
||||||
|
VOID
|
||||||
|
http_set_startup_error_page_content(_In_ byte* errorPageContent, int length)
|
||||||
|
{
|
||||||
|
g_errorPageContent.resize(length);
|
||||||
|
memcpy(&g_errorPageContent[0], errorPageContent, length);
|
||||||
|
}
|
||||||
|
|
||||||
// End of export
|
// End of export
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
<Reference Include="Microsoft.AspNetCore.Authentication.Core" />
|
<Reference Include="Microsoft.AspNetCore.Authentication.Core" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
|
||||||
|
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
|
||||||
|
<Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" />
|
||||||
<Reference Include="System.IO.Pipelines" />
|
<Reference Include="System.IO.Pipelines" />
|
||||||
<Reference Include="System.Security.Principal.Windows" />
|
<Reference Include="System.Security.Principal.Windows" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
|
|
@ -14,8 +14,11 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="$(SharedSourceRoot)Buffers.MemoryPool\**\*.cs" />
|
<Compile Include="$(SharedSourceRoot)Buffers.MemoryPool\**\*.cs" LinkBase="Shared\" />
|
||||||
<Compile Include="$(SharedSourceRoot)HttpSys\**\*.cs" />
|
<Compile Include="$(SharedSourceRoot)HttpSys\**\*.cs" LinkBase="Shared\"/>
|
||||||
|
<Compile Include="$(SharedSourceRoot)StackTrace\**\*.cs" LinkBase="Shared\" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)RazorViews\*.cs" LinkBase="Shared\" />
|
||||||
|
<Compile Include="$(SharedSourceRoot)ErrorPage\*.cs" LinkBase="Shared\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="ValidateNativeComponentsBuilt" AfterTargets="Build" >
|
<Target Name="ValidateNativeComponentsBuilt" AfterTargets="Build" >
|
||||||
|
|
@ -32,6 +35,8 @@
|
||||||
<Reference Include="Microsoft.AspNetCore.Authentication.Core" />
|
<Reference Include="Microsoft.AspNetCore.Authentication.Core" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Connections.Abstractions" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
|
<Reference Include="Microsoft.AspNetCore.Hosting.Abstractions" />
|
||||||
|
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
|
||||||
|
<Reference Include="Microsoft.Extensions.TypeNameHelper.Sources" />
|
||||||
<Reference Include="System.IO.Pipelines" />
|
<Reference Include="System.IO.Pipelines" />
|
||||||
<Reference Include="System.Security.Principal.Windows" />
|
<Reference Include="System.Security.Principal.Windows" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,9 @@ namespace Microsoft.AspNetCore.Server.IIS
|
||||||
[DllImport(AspNetCoreModuleDll)]
|
[DllImport(AspNetCoreModuleDll)]
|
||||||
private static extern int http_get_authentication_information(IntPtr pInProcessHandler, [MarshalAs(UnmanagedType.BStr)] out string authType, out IntPtr token);
|
private static extern int http_get_authentication_information(IntPtr pInProcessHandler, [MarshalAs(UnmanagedType.BStr)] out string authType, out IntPtr token);
|
||||||
|
|
||||||
|
[DllImport(AspNetCoreModuleDll)]
|
||||||
|
private static extern unsafe int http_set_startup_error_page_content(byte* content, int contentLength);
|
||||||
|
|
||||||
public static void HttpPostCompletion(IntPtr pInProcessHandler, int cbBytes)
|
public static void HttpPostCompletion(IntPtr pInProcessHandler, int cbBytes)
|
||||||
{
|
{
|
||||||
Validate(http_post_completion(pInProcessHandler, cbBytes));
|
Validate(http_post_completion(pInProcessHandler, cbBytes));
|
||||||
|
|
@ -169,7 +172,7 @@ namespace Microsoft.AspNetCore.Server.IIS
|
||||||
Validate(register_callbacks(pInProcessApplication, requestCallback, shutdownCallback, disconnectCallback, asyncCallback, requestsDrainedHandler, pvRequestContext, pvShutdownContext));
|
Validate(register_callbacks(pInProcessApplication, requestCallback, shutdownCallback, disconnectCallback, asyncCallback, requestsDrainedHandler, pvRequestContext, pvShutdownContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe int HttpWriteResponseBytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected)
|
internal static unsafe int HttpWriteResponseBytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected)
|
||||||
{
|
{
|
||||||
return http_write_response_bytes(pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected);
|
return http_write_response_bytes(pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected);
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +182,7 @@ namespace Microsoft.AspNetCore.Server.IIS
|
||||||
return http_flush_response_bytes(pInProcessHandler, fMoreData, out fCompletionExpected);
|
return http_flush_response_bytes(pInProcessHandler, fMoreData, out fCompletionExpected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe HttpApiTypes.HTTP_REQUEST_V2* HttpGetRawRequest(IntPtr pInProcessHandler)
|
internal static unsafe HttpApiTypes.HTTP_REQUEST_V2* HttpGetRawRequest(IntPtr pInProcessHandler)
|
||||||
{
|
{
|
||||||
return http_get_raw_request(pInProcessHandler);
|
return http_get_raw_request(pInProcessHandler);
|
||||||
}
|
}
|
||||||
|
|
@ -219,7 +222,7 @@ namespace Microsoft.AspNetCore.Server.IIS
|
||||||
Validate(http_set_managed_context(pInProcessHandler, pvManagedContext));
|
Validate(http_set_managed_context(pInProcessHandler, pvManagedContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IISConfigurationData HttpGetApplicationProperties()
|
internal static IISConfigurationData HttpGetApplicationProperties()
|
||||||
{
|
{
|
||||||
var iisConfigurationData = new IISConfigurationData();
|
var iisConfigurationData = new IISConfigurationData();
|
||||||
Validate(http_get_application_properties(ref iisConfigurationData));
|
Validate(http_get_application_properties(ref iisConfigurationData));
|
||||||
|
|
@ -247,7 +250,7 @@ namespace Microsoft.AspNetCore.Server.IIS
|
||||||
return http_websockets_read_bytes(pInProcessHandler, pvBuffer, cbBuffer, pfnCompletionCallback, pvCompletionContext, out dwBytesReceived, out fCompletionExpected);
|
return http_websockets_read_bytes(pInProcessHandler, pvBuffer, cbBuffer, pfnCompletionCallback, pvCompletionContext, out dwBytesReceived, out fCompletionExpected);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static unsafe int HttpWebsocketsWriteBytes(
|
internal static unsafe int HttpWebsocketsWriteBytes(
|
||||||
IntPtr pInProcessHandler,
|
IntPtr pInProcessHandler,
|
||||||
HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks,
|
HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks,
|
||||||
int nChunks,
|
int nChunks,
|
||||||
|
|
@ -297,6 +300,14 @@ namespace Microsoft.AspNetCore.Server.IIS
|
||||||
Validate(http_get_authentication_information(pInProcessHandler, out authType, out token));
|
Validate(http_get_authentication_information(pInProcessHandler, out authType, out token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static unsafe void HttpSetStartupErrorPageContent(byte[] content)
|
||||||
|
{
|
||||||
|
fixed(byte* bytePtr = content)
|
||||||
|
{
|
||||||
|
http_set_startup_error_page_content(bytePtr, content.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void Validate(int hr)
|
private static void Validate(int hr)
|
||||||
{
|
{
|
||||||
if (hr != HR_OK)
|
if (hr != HR_OK)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Pipelines;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Views;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.Server.IIS;
|
||||||
|
using Microsoft.Extensions.FileProviders;
|
||||||
|
using Microsoft.Extensions.StackTrace.Sources;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Startup hooks are pieces of code that will run before a users program main executes
|
||||||
|
/// See: https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/host-startup-hook.md
|
||||||
|
/// The type must be named StartupHook without any namespace, and should be internal.
|
||||||
|
/// </summary>
|
||||||
|
internal class StartupHook
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Startup hooks are pieces of code that will run before a users program main executes
|
||||||
|
/// See: https://github.com/dotnet/core-setup/blob/master/Documentation/design-docs/host-startup-hook.md
|
||||||
|
/// </summary>
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
if (!NativeMethods.IsAspNetCoreModuleLoaded())
|
||||||
|
{
|
||||||
|
// This means someone set the startup hook for Microsoft.AspNetCore.Server.IIS
|
||||||
|
// but are not running inprocess. Return at this point.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var detailedErrors = Environment.GetEnvironmentVariable("ASPNETCORE_DETAILEDERRORS");
|
||||||
|
var enableStartupErrorPage = detailedErrors?.Equals("1", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||||
|
enableStartupErrorPage |= detailedErrors?.Equals("true", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||||
|
|
||||||
|
var aspnetCoreEnvironment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
|
||||||
|
enableStartupErrorPage |= aspnetCoreEnvironment?.Equals("Development", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||||
|
|
||||||
|
var dotnetEnvironment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
|
||||||
|
enableStartupErrorPage |= dotnetEnvironment?.Equals("Development", StringComparison.OrdinalIgnoreCase) ?? false;
|
||||||
|
|
||||||
|
if (!enableStartupErrorPage)
|
||||||
|
{
|
||||||
|
// Not running in development or detailed errors aren't enabled
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
|
||||||
|
{
|
||||||
|
var exception = (Exception)eventArgs.ExceptionObject;
|
||||||
|
|
||||||
|
// Get the content root from IIS.
|
||||||
|
var iisConfigData = NativeMethods.HttpGetApplicationProperties();
|
||||||
|
var contentRoot = iisConfigData.pwzFullApplicationPath.TrimEnd(Path.DirectorySeparatorChar);
|
||||||
|
|
||||||
|
var model = new ErrorPageModel
|
||||||
|
{
|
||||||
|
RuntimeDisplayName = RuntimeInformation.FrameworkDescription
|
||||||
|
};
|
||||||
|
|
||||||
|
var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).GetTypeInfo().Assembly;
|
||||||
|
var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName).Version.ToString();
|
||||||
|
var clrVersion = assemblyVersion;
|
||||||
|
model.RuntimeArchitecture = RuntimeInformation.ProcessArchitecture.ToString();
|
||||||
|
var currentAssembly = typeof(ErrorPage).GetTypeInfo().Assembly;
|
||||||
|
model.CurrentAssemblyVesion = currentAssembly
|
||||||
|
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||||
|
.InformationalVersion;
|
||||||
|
model.ClrVersion = clrVersion;
|
||||||
|
model.OperatingSystemDescription = RuntimeInformation.OSDescription;
|
||||||
|
|
||||||
|
var exceptionDetailProvider = new ExceptionDetailsProvider(
|
||||||
|
new PhysicalFileProvider(contentRoot),
|
||||||
|
sourceCodeLineCount: 6);
|
||||||
|
|
||||||
|
model.ErrorDetails = exceptionDetailProvider.GetDetails(exception);
|
||||||
|
|
||||||
|
var errorPage = new ErrorPage(model);
|
||||||
|
|
||||||
|
var stream = new MemoryStream();
|
||||||
|
|
||||||
|
// Never will go async because we are writing to a memory stream.
|
||||||
|
errorPage.ExecuteAsync(stream).GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
// Get the raw content and set the error page.
|
||||||
|
stream.Position = 0;
|
||||||
|
var content = stream.ToArray();
|
||||||
|
|
||||||
|
NativeMethods.HttpSetStartupErrorPageContent(content);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,9 +3,11 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
@ -481,6 +483,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
|
|
||||||
|
|
||||||
private static Dictionary<string, Func<IISDeploymentParameters, string>> StandaloneConfigTransformations = InitStandaloneConfigTransformations();
|
private static Dictionary<string, Func<IISDeploymentParameters, string>> StandaloneConfigTransformations = InitStandaloneConfigTransformations();
|
||||||
|
|
||||||
public static IEnumerable<object[]> StandaloneConfigTransformationsScenarios => StandaloneConfigTransformations.ToTheoryData();
|
public static IEnumerable<object[]> StandaloneConfigTransformationsScenarios => StandaloneConfigTransformations.ToTheoryData();
|
||||||
|
|
||||||
[ConditionalTheory]
|
[ConditionalTheory]
|
||||||
|
|
@ -572,6 +575,116 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
await request;
|
await request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ConditionalTheory]
|
||||||
|
[RequiresIIS(IISCapability.PoolEnvironmentVariables)]
|
||||||
|
[RequiresNewHandler]
|
||||||
|
[InlineData("ASPNETCORE_ENVIRONMENT", "Development")]
|
||||||
|
[InlineData("DOTNET_ENVIRONMENT", "deVelopment")]
|
||||||
|
[InlineData("ASPNETCORE_DETAILEDERRORS", "1")]
|
||||||
|
[InlineData("ASPNETCORE_DETAILEDERRORS", "TRUE")]
|
||||||
|
public async Task ExceptionIsLoggedToEventLogAndPutInResponseWhenDeveloperExceptionPageIsEnabled(string environmentVariable, string value)
|
||||||
|
{
|
||||||
|
var deploymentParameters = Fixture.GetBaseDeploymentParameters();
|
||||||
|
deploymentParameters.TransformArguments((a, _) => $"{a} Throw");
|
||||||
|
|
||||||
|
// Deployment parameters by default set ASPNETCORE_DETAILEDERRORS to true
|
||||||
|
deploymentParameters.EnvironmentVariables["ASPNETCORE_DETAILEDERRORS"] = "";
|
||||||
|
deploymentParameters.EnvironmentVariables[environmentVariable] = value;
|
||||||
|
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
var result = await deploymentResult.HttpClient.GetAsync("/");
|
||||||
|
Assert.False(result.IsSuccessStatusCode);
|
||||||
|
|
||||||
|
var content = await result.Content.ReadAsStringAsync();
|
||||||
|
Assert.Contains("InvalidOperationException", content);
|
||||||
|
Assert.Contains("TestSite.Program.Main", content);
|
||||||
|
|
||||||
|
StopServer();
|
||||||
|
|
||||||
|
VerifyDotnetRuntimeEventLog(deploymentResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[RequiresIIS(IISCapability.PoolEnvironmentVariables)]
|
||||||
|
[RequiresNewHandler]
|
||||||
|
public async Task ExceptionIsNotLoggedToResponseWhenStartupHookIsDisabled()
|
||||||
|
{
|
||||||
|
var deploymentParameters = Fixture.GetBaseDeploymentParameters();
|
||||||
|
deploymentParameters.TransformArguments((a, _) => $"{a} Throw");
|
||||||
|
deploymentParameters.EnvironmentVariables["ASPNETCORE_DETAILEDERRORS"] = "";
|
||||||
|
deploymentParameters.EnvironmentVariables["ASPNETCORE_ENVIRONMENT"] = "Development";
|
||||||
|
deploymentParameters.HandlerSettings["callStartupHook"] = "false";
|
||||||
|
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
var result = await deploymentResult.HttpClient.GetAsync("/");
|
||||||
|
Assert.False(result.IsSuccessStatusCode);
|
||||||
|
|
||||||
|
var content = await result.Content.ReadAsStringAsync();
|
||||||
|
Assert.DoesNotContain("InvalidOperationException", content);
|
||||||
|
|
||||||
|
StopServer();
|
||||||
|
|
||||||
|
VerifyDotnetRuntimeEventLog(deploymentResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ConditionalFact]
|
||||||
|
[RequiresNewHandler]
|
||||||
|
public async Task ExceptionIsLoggedToEventLogDoesNotWriteToResponse()
|
||||||
|
{
|
||||||
|
var deploymentParameters = Fixture.GetBaseDeploymentParameters();
|
||||||
|
deploymentParameters.TransformArguments((a, _) => $"{a} Throw");
|
||||||
|
|
||||||
|
// Deployment parameters by default set ASPNETCORE_DETAILEDERRORS to true
|
||||||
|
deploymentParameters.EnvironmentVariables["ASPNETCORE_DETAILEDERRORS"] = "";
|
||||||
|
|
||||||
|
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||||
|
var result = await deploymentResult.HttpClient.GetAsync("/");
|
||||||
|
Assert.False(result.IsSuccessStatusCode);
|
||||||
|
|
||||||
|
var content = await result.Content.ReadAsStringAsync();
|
||||||
|
Assert.DoesNotContain("InvalidOperationException", content);
|
||||||
|
|
||||||
|
StopServer();
|
||||||
|
|
||||||
|
VerifyDotnetRuntimeEventLog(deploymentResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void VerifyDotnetRuntimeEventLog(IISDeploymentResult deploymentResult)
|
||||||
|
{
|
||||||
|
var entries = GetEventLogsFromDotnetRuntime(deploymentResult);
|
||||||
|
|
||||||
|
var expectedRegex = new Regex("Exception Info: System\\.InvalidOperationException:", RegexOptions.Singleline);
|
||||||
|
var matchedEntries = entries.Where(entry => expectedRegex.IsMatch(entry.Message)).ToArray();
|
||||||
|
// There isn't a process ID to filter on here, so there can be duplicate entries from other tests.
|
||||||
|
Assert.True(matchedEntries.Length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<EventLogEntry> GetEventLogsFromDotnetRuntime(IISDeploymentResult deploymentResult)
|
||||||
|
{
|
||||||
|
var processStartTime = deploymentResult.HostProcess.StartTime.AddSeconds(-5);
|
||||||
|
var eventLog = new EventLog("Application");
|
||||||
|
|
||||||
|
for (var i = eventLog.Entries.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var eventLogEntry = eventLog.Entries[i];
|
||||||
|
if (eventLogEntry.TimeGenerated < processStartTime)
|
||||||
|
{
|
||||||
|
// If event logs is older than the process start time, we didn't find a match.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventLogEntry.ReplacementStrings == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventLogEntry.Source == ".NET Runtime")
|
||||||
|
{
|
||||||
|
yield return eventLogEntry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void MoveApplication(
|
private static void MoveApplication(
|
||||||
IISDeploymentParameters parameters,
|
IISDeploymentParameters parameters,
|
||||||
string subdirectory)
|
string subdirectory)
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
||||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||||
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
|
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
|
||||||
using Microsoft.AspNetCore.Testing.xunit;
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,4 +1,4 @@
|
||||||
@using System
|
@using System
|
||||||
@using System.Globalization
|
@using System.Globalization
|
||||||
@using System.Linq
|
@using System.Linq
|
||||||
@using System.Net
|
@using System.Net
|
||||||
|
|
@ -15,20 +15,19 @@
|
||||||
public ErrorPageModel Model { get; set; }
|
public ErrorPageModel Model { get; set; }
|
||||||
}
|
}
|
||||||
@{
|
@{
|
||||||
Response.ContentType = "text/html; charset=utf-8";
|
|
||||||
var location = string.Empty;
|
var location = string.Empty;
|
||||||
}
|
}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName" xmlns="http://www.w3.org/1999/xhtml">
|
<html lang="@CultureInfo.CurrentUICulture.TwoLetterISOLanguageName" xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>@Resources.ErrorPageHtml_Title</title>
|
<title>Internal Server Error</title>
|
||||||
<style>
|
<style>
|
||||||
<%$ include: ErrorPage.css %>
|
<%$ include: ErrorPage.css %>
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>@Resources.ErrorPageHtml_UnhandledException</h1>
|
<h1>An error occurred while starting the application.</h1>
|
||||||
@foreach (var errorDetail in Model.ErrorDetails)
|
@foreach (var errorDetail in Model.ErrorDetails)
|
||||||
{
|
{
|
||||||
<div class="titleerror">@errorDetail.Error.GetType().Name: @{ Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message)); }</div>
|
<div class="titleerror">@errorDetail.Error.GetType().Name: @{ Output.Write(HtmlEncodeAndReplaceLineBreaks(errorDetail.Error.Message)); }</div>
|
||||||
|
|
@ -49,7 +48,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<p class="location">@Resources.ErrorPageHtml_UnknownLocation</p>
|
<p class="location">Unknown location</p>
|
||||||
}
|
}
|
||||||
|
|
||||||
var reflectionTypeLoadException = errorDetail.Error as ReflectionTypeLoadException;
|
var reflectionTypeLoadException = errorDetail.Error as ReflectionTypeLoadException;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
@ -57,6 +57,23 @@ namespace Microsoft.Extensions.RazorViews
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected JavaScriptEncoder JavaScriptEncoder { get; set; } = JavaScriptEncoder.Default;
|
protected JavaScriptEncoder JavaScriptEncoder { get; set; } = JavaScriptEncoder.Default;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute an individual request
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream to write to</param>
|
||||||
|
public async Task ExecuteAsync(Stream stream)
|
||||||
|
{
|
||||||
|
// We technically don't need this intermediate buffer if this method accepts a memory stream.
|
||||||
|
var buffer = new MemoryStream();
|
||||||
|
Output = new StreamWriter(buffer, UTF8NoBOM, 4096, leaveOpen: true);
|
||||||
|
await ExecuteAsync();
|
||||||
|
await Output.FlushAsync();
|
||||||
|
Output.Dispose();
|
||||||
|
buffer.Seek(0, SeekOrigin.Begin);
|
||||||
|
await buffer.CopyToAsync(stream);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Execute an individual request
|
/// Execute an individual request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue