[3.1.x] Allow external startup hooks (#17116)

This commit is contained in:
Justin Kotalik 2019-11-23 10:06:25 -08:00 committed by Andrew Stanton-Nurse
parent e1e6bf8587
commit c31f51e5c7
6 changed files with 79 additions and 1 deletions

View File

@ -47,6 +47,7 @@ void HostFxr::Load(HMODULE moduleHandle)
m_corehost_set_error_writer_fn = ModuleHelpers::GetKnownProcAddress<corehost_set_error_writer_fn>(moduleHandle, "hostfxr_set_error_writer", /* optional */ true);
m_hostfxr_initialize_for_dotnet_commandline_fn = ModuleHelpers::GetKnownProcAddress<hostfxr_initialize_for_dotnet_runtime_fn>(moduleHandle, "hostfxr_initialize_for_dotnet_command_line", /* optional */ true);
m_hostfxr_set_runtime_property_value_fn = ModuleHelpers::GetKnownProcAddress<hostfxr_set_runtime_property_value_fn>(moduleHandle, "hostfxr_set_runtime_property_value", /* optional */ true);
m_hostfxr_get_runtime_property_value_fn = ModuleHelpers::GetKnownProcAddress<hostfxr_get_runtime_property_value_fn>(moduleHandle, "hostfxr_get_runtime_property_value", /* optional */ true);
m_hostfxr_run_app_fn = ModuleHelpers::GetKnownProcAddress<hostfxr_run_app_fn>(moduleHandle, "hostfxr_run_app", /* optional */ true);
m_hostfxr_close_fn = ModuleHelpers::GetKnownProcAddress<hostfxr_close_fn>(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)

View File

@ -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;

View File

@ -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"));

View File

@ -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()

View File

@ -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

View File

@ -0,0 +1,9 @@
using TestSite;
internal class StartupHook
{
public static void Initialize()
{
Startup.StartupHookCalled = true;
}
}