diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.cpp index 545791610b..a9d942aabf 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.cpp @@ -47,6 +47,7 @@ void HostFxr::Load(HMODULE moduleHandle) m_corehost_set_error_writer_fn = ModuleHelpers::GetKnownProcAddress(moduleHandle, "hostfxr_set_error_writer", /* optional */ true); m_hostfxr_initialize_for_dotnet_commandline_fn = ModuleHelpers::GetKnownProcAddress(moduleHandle, "hostfxr_initialize_for_dotnet_command_line", /* optional */ true); m_hostfxr_set_runtime_property_value_fn = ModuleHelpers::GetKnownProcAddress(moduleHandle, "hostfxr_set_runtime_property_value", /* optional */ true); + m_hostfxr_get_runtime_property_value_fn = ModuleHelpers::GetKnownProcAddress(moduleHandle, "hostfxr_get_runtime_property_value", /* optional */ true); m_hostfxr_run_app_fn = ModuleHelpers::GetKnownProcAddress(moduleHandle, "hostfxr_run_app", /* optional */ true); m_hostfxr_close_fn = ModuleHelpers::GetKnownProcAddress(moduleHandle, "hostfxr_close", /* optional */ true); } @@ -164,6 +165,15 @@ int HostFxr::SetRuntimePropertyValue(PCWSTR name, PCWSTR value) const noexcept return 0; } +int HostFxr::GetRuntimePropertyValue(PCWSTR name, PWSTR* value) const noexcept +{ + if (m_host_context_handle != nullptr && m_hostfxr_get_runtime_property_value_fn != nullptr) + { + return m_hostfxr_get_runtime_property_value_fn(m_host_context_handle, name, value); + } + return 0; +} + void HostFxr::Close() const noexcept { if (m_host_context_handle != nullptr && m_hostfxr_close_fn != nullptr) diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.h index ced4dd1940..a6cf2bb889 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxr.h @@ -25,6 +25,7 @@ typedef void(*corehost_error_writer_fn) (const WCHAR* message); typedef corehost_error_writer_fn(*corehost_set_error_writer_fn) (corehost_error_writer_fn error_writer); typedef int(*hostfxr_initialize_for_dotnet_runtime_fn)(int argc, const PCWSTR* argv, hostfxr_initialize_parameters* parameters, void* const* host_context_handle); typedef int(*hostfxr_set_runtime_property_value_fn)(void* host_context_handle, PCWSTR name, PCWSTR value); +typedef int(*hostfxr_get_runtime_property_value_fn)(void* host_context_handle, PCWSTR name, PWSTR* value); typedef int(*hostfxr_run_app_fn)(void* host_context_handle); typedef int(*hostfxr_close_fn)(void* hostfxr_context_handle); @@ -73,6 +74,7 @@ public: HostFxrErrorRedirector RedirectOutput(RedirectionOutput* writer) const noexcept; int SetRuntimePropertyValue(PCWSTR name, PCWSTR value) const noexcept; + int GetRuntimePropertyValue(PCWSTR name, PWSTR* value) const noexcept; int InitializeForApp(int argc, PCWSTR* argv, const std::wstring& m_dotnetExeKnownLocation) const noexcept; void Close() const noexcept; @@ -82,6 +84,7 @@ private: hostfxr_get_native_search_directories_fn m_hostfxr_get_native_search_directories_fn; hostfxr_initialize_for_dotnet_runtime_fn m_hostfxr_initialize_for_dotnet_commandline_fn; hostfxr_set_runtime_property_value_fn m_hostfxr_set_runtime_property_value_fn; + hostfxr_get_runtime_property_value_fn m_hostfxr_get_runtime_property_value_fn; hostfxr_run_app_fn m_hostfxr_run_app_fn; corehost_set_error_writer_fn m_corehost_set_error_writer_fn; hostfxr_close_fn m_hostfxr_close_fn; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp index b671e7dba2..be2e9055b7 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp @@ -252,7 +252,20 @@ IN_PROCESS_APPLICATION::ExecuteApplication() if (m_pConfig->QueryCallStartupHook()) { - RETURN_IF_NOT_ZERO(context->m_hostFxr.SetRuntimePropertyValue(DOTNETCORE_STARTUP_HOOK, ASPNETCORE_STARTUP_ASSEMBLY)); + PWSTR startupHookValue = NULL; + // Will get property not found if the enviroment variable isn't set. + context->m_hostFxr.GetRuntimePropertyValue(DOTNETCORE_STARTUP_HOOK, &startupHookValue); + + if (startupHookValue == NULL) + { + RETURN_IF_NOT_ZERO(context->m_hostFxr.SetRuntimePropertyValue(DOTNETCORE_STARTUP_HOOK, ASPNETCORE_STARTUP_ASSEMBLY)); + } + else + { + std::wstring startupHook(startupHookValue); + startupHook.append(L";").append(ASPNETCORE_STARTUP_ASSEMBLY); + RETURN_IF_NOT_ZERO(context->m_hostFxr.SetRuntimePropertyValue(DOTNETCORE_STARTUP_HOOK, startupHook.c_str())); + } } RETURN_IF_NOT_ZERO(context->m_hostFxr.SetRuntimePropertyValue(DOTNETCORE_USE_ENTRYPOINT_FILTER, L"1")); diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs index 3acf1a86e0..1c070242e0 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -817,6 +817,42 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.InProcess VerifyDotnetRuntimeEventLog(deploymentResult); } + + [ConditionalFact] + [RequiresNewHandler] + public async Task CanAddCustomStartupHook() + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(); + + // Deployment parameters by default set ASPNETCORE_DETAILEDERRORS to true + deploymentParameters.WebConfigBasedEnvironmentVariables["DOTNET_STARTUP_HOOKS"] = "InProcessWebSite"; + + var deploymentResult = await DeployAsync(deploymentParameters); + var result = await deploymentResult.HttpClient.GetAsync("/StartupHook"); + var content = await result.Content.ReadAsStringAsync(); + Assert.Equal("True", content); + + StopServer(); + } + + [ConditionalFact] + [RequiresNewHandler] + public async Task CanAddCustomStartupHookWhenIISOneIsDisabled() + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(); + + // Deployment parameters by default set ASPNETCORE_DETAILEDERRORS to true + deploymentParameters.WebConfigBasedEnvironmentVariables["DOTNET_STARTUP_HOOKS"] = "InProcessWebSite"; + deploymentParameters.HandlerSettings["callStartupHook"] = "false"; + + var deploymentResult = await DeployAsync(deploymentParameters); + var result = await deploymentResult.HttpClient.GetAsync("/StartupHook"); + var content = await result.Content.ReadAsStringAsync(); + Assert.Equal("True", content); + + StopServer(); + } + [ConditionalFact] [RequiresNewHandler] public async Task StackOverflowIsAvoidedBySettingLargerStack() diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs index f25dca8633..7336dda665 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs @@ -32,6 +32,8 @@ namespace TestSite { public partial class Startup { + public static bool StartupHookCalled; + public void Configure(IApplicationBuilder app) { if (Environment.GetEnvironmentVariable("ENABLE_HTTPS_REDIRECTION") != null) @@ -900,6 +902,11 @@ namespace TestSite RecursiveFunction(i - 1); } + private async Task StartupHook(HttpContext ctx) + { + await ctx.Response.WriteAsync(StartupHookCalled.ToString()); + } + private async Task GetServerVariableStress(HttpContext ctx) { // This test simulates the scenario where native Flush call is being diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/StartupHook.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/StartupHook.cs new file mode 100644 index 0000000000..5ea24a3175 --- /dev/null +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/StartupHook.cs @@ -0,0 +1,9 @@ +using TestSite; + +internal class StartupHook +{ + public static void Initialize() + { + Startup.StartupHookCalled = true; + } +}