Cleanup application manager (#1250)

This commit is contained in:
Pavel Krymets 2018-08-22 08:23:40 -07:00 committed by GitHub
parent e23d9cb4ac
commit e26231b613
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 394 additions and 630 deletions

View File

@ -0,0 +1,46 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#pragma once
#include <array>
#include <string>
#include "iapplication.h"
#include "HandleWrapper.h"
typedef
HRESULT
(WINAPI * PFN_ASPNETCORE_CREATE_APPLICATION)(
_In_ IHttpServer *pServer,
_In_ IHttpApplication *pHttpApplication,
_In_ APPLICATION_PARAMETER *pParameters,
_In_ DWORD nParameters,
_Out_ IAPPLICATION **pApplication
);
class ApplicationFactory
{
public:
ApplicationFactory(HMODULE hRequestHandlerDll, std::wstring location, PFN_ASPNETCORE_CREATE_APPLICATION pfnAspNetCoreCreateApplication):
m_pfnAspNetCoreCreateApplication(pfnAspNetCoreCreateApplication),
m_location(location),
m_hRequestHandlerDll(hRequestHandlerDll)
{
}
HRESULT Execute(
_In_ IHttpServer *pServer,
_In_ IHttpApplication *pHttpApplication,
_Out_ IAPPLICATION **pApplication) const
{
std::array<APPLICATION_PARAMETER, 1> parameters {
{"InProcessExeLocation", reinterpret_cast<const void*>(m_location.data())}
};
return m_pfnAspNetCoreCreateApplication(pServer, pHttpApplication, parameters.data(), static_cast<DWORD>(parameters.size()), pApplication);
}
private:
PFN_ASPNETCORE_CREATE_APPLICATION m_pfnAspNetCoreCreateApplication;
std::wstring m_location;
HandleWrapper<ModuleHandleTraits> m_hRequestHandlerDll;
};

View File

@ -227,6 +227,7 @@
</ResourceCompile> </ResourceCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="ApplicationFactory.h" />
<ClInclude Include="applicationinfo.h" /> <ClInclude Include="applicationinfo.h" />
<ClInclude Include="AppOfflineApplication.h" /> <ClInclude Include="AppOfflineApplication.h" />
<ClInclude Include="AppOfflineHandler.h" /> <ClInclude Include="AppOfflineHandler.h" />

View File

@ -19,56 +19,59 @@ const PCWSTR HandlerResolver::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L"
HandlerResolver::HandlerResolver(HMODULE hModule, IHttpServer &pServer) HandlerResolver::HandlerResolver(HMODULE hModule, IHttpServer &pServer)
: m_hModule(hModule), : m_hModule(hModule),
m_pServer(pServer), m_pServer(pServer),
m_fAspnetcoreRHLoadResult(S_FALSE), m_loadedApplicationHostingModel(HOSTING_UNKNOWN)
m_loadedApplicationHostingModel(HOSTING_UNKNOWN),
m_hRequestHandlerDll(nullptr),
m_pfnAspNetCoreCreateApplication(nullptr)
{ {
InitializeSRWLock(&m_requestHandlerLoadLock); InitializeSRWLock(&m_requestHandlerLoadLock);
} }
HRESULT HRESULT
HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, STRU& location, ASPNETCORE_SHIM_CONFIG * pConfiguration) HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, ASPNETCORE_SHIM_CONFIG& pConfiguration, std::unique_ptr<ApplicationFactory>& pApplicationFactory)
{ {
HRESULT hr; HRESULT hr;
STACK_STRU(struFileName, MAX_PATH);
PCWSTR pstrHandlerDllName; PCWSTR pstrHandlerDllName;
if (pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS) bool preventUnload;
if (pConfiguration.QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS)
{ {
preventUnload = false;
pstrHandlerDllName = s_pwzAspnetcoreInProcessRequestHandlerName; pstrHandlerDllName = s_pwzAspnetcoreInProcessRequestHandlerName;
} }
else else
{ {
// OutOfProcess handler is not able to handle unload correctly
// It has code running after application.Stop exits
preventUnload = true;
pstrHandlerDllName = s_pwzAspnetcoreOutOfProcessRequestHandlerName; pstrHandlerDllName = s_pwzAspnetcoreOutOfProcessRequestHandlerName;
} }
HandleWrapper<ModuleHandleTraits> hRequestHandlerDll;
std::wstring location;
std::wstring handlerDllPath;
// Try to see if RH is already loaded, use GetModuleHandleEx to increment ref count // Try to see if RH is already loaded, use GetModuleHandleEx to increment ref count
if (!GetModuleHandleEx(0, pstrHandlerDllName, &m_hRequestHandlerDll)) if (!GetModuleHandleEx(0, pstrHandlerDllName, &hRequestHandlerDll))
{ {
if (pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS) if (pConfiguration.QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS)
{ {
std::unique_ptr<HOSTFXR_OPTIONS> options; std::unique_ptr<HOSTFXR_OPTIONS> options;
std::unique_ptr<IOutputManager> outputManager; std::unique_ptr<IOutputManager> outputManager;
RETURN_IF_FAILED(HOSTFXR_OPTIONS::Create( RETURN_IF_FAILED(HOSTFXR_OPTIONS::Create(
NULL, NULL,
pConfiguration->QueryProcessPath()->QueryStr(), pConfiguration.QueryProcessPath().c_str(),
pApplication.GetApplicationPhysicalPath(), pApplication.GetApplicationPhysicalPath(),
pConfiguration->QueryArguments()->QueryStr(), pConfiguration.QueryArguments().c_str(),
options)); options));
RETURN_IF_FAILED(location.Copy(options->GetExeLocation())); location = options->GetExeLocation();
RETURN_IF_FAILED(LoggingHelpers::CreateLoggingProvider( RETURN_IF_FAILED(LoggingHelpers::CreateLoggingProvider(
pConfiguration->QueryStdoutLogEnabled(), pConfiguration.QueryStdoutLogEnabled(),
!m_pServer.IsCommandLineLaunch(), !m_pServer.IsCommandLineLaunch(),
pConfiguration->QueryStdoutLogFile()->QueryStr(), pConfiguration.QueryStdoutLogFile()->QueryStr(),
pApplication.GetApplicationPhysicalPath(), pApplication.GetApplicationPhysicalPath(),
outputManager)); outputManager));
outputManager->Start(); outputManager->Start();
hr = FindNativeAssemblyFromHostfxr(options.get(), pstrHandlerDllName, &struFileName); hr = FindNativeAssemblyFromHostfxr(*options.get(), pstrHandlerDllName, handlerDllPath);
outputManager->Stop(); outputManager->Stop();
if (FAILED(hr) && m_hHostFxrDll != NULL) if (FAILED(hr) && m_hHostFxrDll != NULL)
@ -85,85 +88,99 @@ HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, STRU
EventLog::Error( EventLog::Error(
ASPNETCORE_EVENT_GENERAL_ERROR, ASPNETCORE_EVENT_GENERAL_ERROR,
ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG, ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG,
struFileName.IsEmpty() ? s_pwzAspnetcoreInProcessRequestHandlerName : struFileName.QueryStr(), handlerDllPath.empty()? s_pwzAspnetcoreInProcessRequestHandlerName : handlerDllPath.c_str(),
struStdMsg.QueryStr()); struStdMsg.QueryStr());
} }
} }
else else
{ {
if (FAILED_LOG(hr = FindNativeAssemblyFromGlobalLocation(pConfiguration, pstrHandlerDllName, &struFileName))) if (FAILED_LOG(hr = FindNativeAssemblyFromGlobalLocation(pConfiguration, pstrHandlerDllName, handlerDllPath)))
{ {
EventLog::Error( EventLog::Error(
ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING, ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING,
ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG, ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG,
struFileName.IsEmpty() ? s_pwzAspnetcoreOutOfProcessRequestHandlerName : struFileName.QueryStr()); handlerDllPath.empty() ? s_pwzAspnetcoreOutOfProcessRequestHandlerName : handlerDllPath.c_str());
return hr; return hr;
} }
} }
LOG_INFOF("Loading request handler: %S", struFileName.QueryStr()); LOG_INFOF("Loading request handler: %S", handlerDllPath.c_str());
m_hRequestHandlerDll = LoadLibraryW(struFileName.QueryStr()); hRequestHandlerDll = LoadLibrary(handlerDllPath.c_str());
RETURN_LAST_ERROR_IF_NULL(m_hRequestHandlerDll); if (preventUnload)
{
// Pin module in memory
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_PIN, pstrHandlerDllName, &hRequestHandlerDll);
}
RETURN_LAST_ERROR_IF_NULL(hRequestHandlerDll);
} }
m_pfnAspNetCoreCreateApplication = reinterpret_cast<PFN_ASPNETCORE_CREATE_APPLICATION>(GetProcAddress(m_hRequestHandlerDll, "CreateApplication")); auto pfnAspNetCoreCreateApplication = reinterpret_cast<PFN_ASPNETCORE_CREATE_APPLICATION>(GetProcAddress(hRequestHandlerDll, "CreateApplication"));
RETURN_LAST_ERROR_IF_NULL(pfnAspNetCoreCreateApplication);
RETURN_LAST_ERROR_IF_NULL(m_pfnAspNetCoreCreateApplication);
pApplicationFactory = std::make_unique<ApplicationFactory>(hRequestHandlerDll.release(), location, pfnAspNetCoreCreateApplication);
return S_OK; return S_OK;
} }
HRESULT HRESULT
HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, STRU& location, PFN_ASPNETCORE_CREATE_APPLICATION * pfnCreateApplication) HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, std::unique_ptr<ApplicationFactory>& pApplicationFactory)
{ {
ASPNETCORE_SHIM_CONFIG pConfiguration; try
RETURN_IF_FAILED(pConfiguration.Populate(&m_pServer, &pApplication));
if (m_fAspnetcoreRHLoadResult == S_FALSE)
{ {
ASPNETCORE_SHIM_CONFIG pConfiguration;
RETURN_IF_FAILED(pConfiguration.Populate(&m_pServer, &pApplication));
SRWExclusiveLock lock(m_requestHandlerLoadLock); SRWExclusiveLock lock(m_requestHandlerLoadLock);
if (m_fAspnetcoreRHLoadResult == S_FALSE) if (m_loadedApplicationHostingModel != HOSTING_UNKNOWN)
{ {
m_loadedApplicationHostingModel = pConfiguration.QueryHostingModel(); // Mixed hosting models
m_loadedApplicationId = pApplication.GetApplicationId(); if (m_loadedApplicationHostingModel != pConfiguration.QueryHostingModel())
LOG_IF_FAILED(m_fAspnetcoreRHLoadResult = LoadRequestHandlerAssembly(pApplication, location, &pConfiguration)); {
EventLog::Error(
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR,
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG,
pApplication.GetApplicationId(),
pConfiguration.QueryHostingModel());
return E_FAIL;
}
// Multiple in-process apps
if (m_loadedApplicationHostingModel == HOSTING_IN_PROCESS && m_loadedApplicationId != pApplication.GetApplicationId())
{
EventLog::Error(
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP,
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG,
pApplication.GetApplicationId());
return E_FAIL;
}
} }
m_loadedApplicationHostingModel = pConfiguration.QueryHostingModel();
m_loadedApplicationId = pApplication.GetApplicationId();
RETURN_IF_FAILED(LoadRequestHandlerAssembly(pApplication, pConfiguration, pApplicationFactory));
} }
CATCH_RETURN();
// Mixed hosting models return S_OK;
if (m_loadedApplicationHostingModel != pConfiguration.QueryHostingModel()) }
{
EventLog::Error(
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR,
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG,
pApplication.GetApplicationId(),
pConfiguration.QueryHostingModel());
return E_FAIL; void HandlerResolver::ResetHostingModel()
} {
// Multiple in-process apps SRWExclusiveLock lock(m_requestHandlerLoadLock);
else if (m_loadedApplicationHostingModel == HOSTING_IN_PROCESS && m_loadedApplicationId != pApplication.GetApplicationId())
{
EventLog::Error(
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP,
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG,
pApplication.GetApplicationId());
return E_FAIL; m_loadedApplicationHostingModel = APP_HOSTING_MODEL::HOSTING_UNKNOWN;
} m_loadedApplicationId.resize(0);
*pfnCreateApplication = m_pfnAspNetCoreCreateApplication;
return m_fAspnetcoreRHLoadResult;
} }
HRESULT HRESULT
HandlerResolver::FindNativeAssemblyFromGlobalLocation( HandlerResolver::FindNativeAssemblyFromGlobalLocation(
ASPNETCORE_SHIM_CONFIG * pConfiguration, ASPNETCORE_SHIM_CONFIG& pConfiguration,
PCWSTR pstrHandlerDllName, PCWSTR pstrHandlerDllName,
STRU* struFilename std::wstring& handlerDllPath
) )
{ {
try try
@ -172,12 +189,10 @@ HandlerResolver::FindNativeAssemblyFromGlobalLocation(
modulePath = GlobalVersionUtility::RemoveFileNameFromFolderPath(modulePath); modulePath = GlobalVersionUtility::RemoveFileNameFromFolderPath(modulePath);
std::wstring retval = GlobalVersionUtility::GetGlobalRequestHandlerPath(modulePath.c_str(), handlerDllPath = GlobalVersionUtility::GetGlobalRequestHandlerPath(modulePath.c_str(),
pConfiguration->QueryHandlerVersion()->QueryStr(), pConfiguration.QueryHandlerVersion().c_str(),
pstrHandlerDllName pstrHandlerDllName
); );
RETURN_IF_FAILED(struFilename->Copy(retval.c_str()));
} }
catch (...) catch (...)
{ {
@ -199,25 +214,18 @@ HandlerResolver::FindNativeAssemblyFromGlobalLocation(
// //
HRESULT HRESULT
HandlerResolver::FindNativeAssemblyFromHostfxr( HandlerResolver::FindNativeAssemblyFromHostfxr(
HOSTFXR_OPTIONS* hostfxrOptions, HOSTFXR_OPTIONS& hostfxrOptions,
PCWSTR libraryName, PCWSTR libraryName,
STRU* struFilename std::wstring& handlerDllPath
) )
{ {
STRU struApplicationFullPath; std::wstring struNativeSearchPaths;
STRU struNativeSearchPaths; size_t intIndex;
STRU struNativeDllLocation; size_t intPrevIndex = 0;
INT intIndex = -1; DWORD dwBufferSize = s_initialGetNativeSearchDirectoriesBufferSize;
INT intPrevIndex = 0; DWORD dwRequiredBufferSize = 0;
BOOL fFound = FALSE;
DWORD dwBufferSize = 1024 * 10;
DWORD dwRequiredBufferSize = 0;
STRA output;
RETURN_LAST_ERROR_IF_NULL(m_hHostFxrDll = LoadLibraryW(hostfxrOptions.GetHostFxrLocation()));
DBG_ASSERT(struFilename != NULL);
RETURN_LAST_ERROR_IF_NULL(m_hHostFxrDll = LoadLibraryW(hostfxrOptions->GetHostFxrLocation()));
auto pFnHostFxrSearchDirectories = reinterpret_cast<hostfxr_get_native_search_directories_fn>(GetProcAddress(m_hHostFxrDll, "hostfxr_get_native_search_directories")); auto pFnHostFxrSearchDirectories = reinterpret_cast<hostfxr_get_native_search_directories_fn>(GetProcAddress(m_hHostFxrDll, "hostfxr_get_native_search_directories"));
if (pFnHostFxrSearchDirectories == nullptr) if (pFnHostFxrSearchDirectories == nullptr)
@ -225,20 +233,20 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
EventLog::Error( EventLog::Error(
ASPNETCORE_EVENT_GENERAL_ERROR, ASPNETCORE_EVENT_GENERAL_ERROR,
ASPNETCORE_EVENT_HOSTFXR_DLL_INVALID_VERSION_MSG, ASPNETCORE_EVENT_HOSTFXR_DLL_INVALID_VERSION_MSG,
hostfxrOptions->GetHostFxrLocation() hostfxrOptions.GetHostFxrLocation()
); );
RETURN_IF_FAILED(E_FAIL); RETURN_IF_FAILED(E_FAIL);
} }
RETURN_LAST_ERROR_IF_NULL(pFnHostFxrSearchDirectories); RETURN_LAST_ERROR_IF_NULL(pFnHostFxrSearchDirectories);
RETURN_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize)); struNativeSearchPaths.resize(dwBufferSize);
while (TRUE) while (TRUE)
{ {
auto intHostFxrExitCode = pFnHostFxrSearchDirectories( const auto intHostFxrExitCode = pFnHostFxrSearchDirectories(
hostfxrOptions->GetArgc(), hostfxrOptions.GetArgc(),
hostfxrOptions->GetArgv(), hostfxrOptions.GetArgv(),
struNativeSearchPaths.QueryStr(), struNativeSearchPaths.data(),
dwBufferSize, dwBufferSize,
&dwRequiredBufferSize &dwRequiredBufferSize
); );
@ -251,7 +259,7 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
{ {
dwBufferSize = dwRequiredBufferSize + 1; // for null terminator dwBufferSize = dwRequiredBufferSize + 1; // for null terminator
RETURN_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize)); struNativeSearchPaths.resize(dwBufferSize);
} }
else else
{ {
@ -260,26 +268,26 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
} }
} }
RETURN_IF_FAILED(struNativeSearchPaths.SyncWithBuffer()); struNativeSearchPaths.resize(struNativeSearchPaths.find(L'\0'));
fFound = FALSE; auto fFound = FALSE;
// The native search directories are semicolon delimited. // The native search directories are semicolon delimited.
// Split on semicolons, append aspnetcorerh.dll, and check if the file exists. // Split on semicolons, append aspnetcorerh.dll, and check if the file exists.
while ((intIndex = struNativeSearchPaths.IndexOf(L";", intPrevIndex)) != -1) while ((intIndex = struNativeSearchPaths.find(L';', intPrevIndex)) != std::wstring::npos)
{ {
RETURN_IF_FAILED(struNativeDllLocation.Copy(&struNativeSearchPaths.QueryStr()[intPrevIndex], intIndex - intPrevIndex)); auto path = struNativeSearchPaths.substr(intPrevIndex, intIndex - intPrevIndex);
if (!struNativeDllLocation.EndsWith(L"\\")) if (!path.empty() && !(path[path.length() - 1] == L'\\'))
{ {
RETURN_IF_FAILED(struNativeDllLocation.Append(L"\\")); path.append(L"\\");
} }
RETURN_IF_FAILED(struNativeDllLocation.Append(libraryName)); path.append(libraryName);
if (FILE_UTILITY::CheckIfFileExists(struNativeDllLocation.QueryStr())) if (std::filesystem::is_regular_file(path))
{ {
RETURN_IF_FAILED(struFilename->Copy(struNativeDllLocation)); handlerDllPath = path;
fFound = TRUE; fFound = TRUE;
break; break;
} }

View File

@ -3,47 +3,35 @@
#pragma once #pragma once
#include "aspnetcore_shim_config.h" #include "aspnetcore_shim_config.h"
#include "hostfxroptions.h"
#include <memory>
#include "iapplication.h"
#include <string>
#include "HandleWrapper.h"
typedef #include <memory>
HRESULT #include <string>
(WINAPI * PFN_ASPNETCORE_CREATE_APPLICATION)( #include "hostfxroptions.h"
_In_ IHttpServer *pServer, #include "HandleWrapper.h"
_In_ IHttpApplication *pHttpApplication, #include "ApplicationFactory.h"
_In_ APPLICATION_PARAMETER *pParameters,
_In_ DWORD nParameters,
_Out_ IAPPLICATION **pApplication
);
class HandlerResolver class HandlerResolver
{ {
public: public:
HandlerResolver(HMODULE hModule, IHttpServer &pServer); HandlerResolver(HMODULE hModule, IHttpServer &pServer);
HRESULT GetApplicationFactory(IHttpApplication &pApplication, STRU& location, PFN_ASPNETCORE_CREATE_APPLICATION *pfnCreateApplication); HRESULT GetApplicationFactory(IHttpApplication &pApplication, std::unique_ptr<ApplicationFactory>& pApplicationFactory);
void ResetHostingModel();
private: private:
HRESULT LoadRequestHandlerAssembly(IHttpApplication &pApplication,STRU& location, ASPNETCORE_SHIM_CONFIG * pConfiguration); HRESULT LoadRequestHandlerAssembly(IHttpApplication &pApplication, ASPNETCORE_SHIM_CONFIG& pConfiguration, std::unique_ptr<ApplicationFactory>& pApplicationFactory);
HRESULT FindNativeAssemblyFromGlobalLocation(ASPNETCORE_SHIM_CONFIG * pConfiguration, PCWSTR libraryName, STRU* location); HRESULT FindNativeAssemblyFromGlobalLocation(ASPNETCORE_SHIM_CONFIG& pConfiguration, PCWSTR libraryName, std::wstring& handlerDllPath);
HRESULT FindNativeAssemblyFromHostfxr(HOSTFXR_OPTIONS* hostfxrOptions, PCWSTR libraryName, STRU* location); HRESULT FindNativeAssemblyFromHostfxr(HOSTFXR_OPTIONS& hostfxrOptions, PCWSTR libraryName, std::wstring& handlerDllPath);
HMODULE m_hModule; HMODULE m_hModule;
IHttpServer &m_pServer; IHttpServer &m_pServer;
SRWLOCK m_requestHandlerLoadLock {}; SRWLOCK m_requestHandlerLoadLock {};
// S_FALSE - not loaded, S_OK - loaded, everything else - error
HRESULT m_fAspnetcoreRHLoadResult;
std::wstring m_loadedApplicationId; std::wstring m_loadedApplicationId;
APP_HOSTING_MODEL m_loadedApplicationHostingModel; APP_HOSTING_MODEL m_loadedApplicationHostingModel;
HandleWrapper<ModuleHandleTraits> m_hRequestHandlerDll;
HandleWrapper<ModuleHandleTraits> m_hHostFxrDll; HandleWrapper<ModuleHandleTraits> m_hHostFxrDll;
PFN_ASPNETCORE_CREATE_APPLICATION m_pfnAspNetCoreCreateApplication;
static const PCWSTR s_pwzAspnetcoreInProcessRequestHandlerName; static const PCWSTR s_pwzAspnetcoreInProcessRequestHandlerName;
static const PCWSTR s_pwzAspnetcoreOutOfProcessRequestHandlerName; static const PCWSTR s_pwzAspnetcoreOutOfProcessRequestHandlerName;
static const DWORD s_initialGetNativeSearchDirectoriesBufferSize = MAX_PATH * 4;
}; };

View File

@ -3,7 +3,6 @@
#include "applicationinfo.h" #include "applicationinfo.h"
#include <array>
#include "proxymodule.h" #include "proxymodule.h"
#include "hostfxr_utility.h" #include "hostfxr_utility.h"
#include "debugutil.h" #include "debugutil.h"
@ -11,28 +10,14 @@
#include "SRWExclusiveLock.h" #include "SRWExclusiveLock.h"
#include "exceptions.h" #include "exceptions.h"
#include "EventLog.h" #include "EventLog.h"
#include "HandleWrapper.h"
#include "ServerErrorApplication.h" #include "ServerErrorApplication.h"
#include "AppOfflineApplication.h" #include "AppOfflineApplication.h"
APPLICATION_INFO::~APPLICATION_INFO() APPLICATION_INFO::~APPLICATION_INFO()
{ {
ShutDownApplication(); ShutDownApplication(/* fServerInitiated */ false);
} }
HRESULT
APPLICATION_INFO::Initialize(
_In_ IHttpApplication &pApplication,
HandlerResolver * pHandlerResolver
)
{
m_handlerResolver = pHandlerResolver;
RETURN_IF_FAILED(m_struConfigPath.Copy(pApplication.GetAppConfigPath()));
RETURN_IF_FAILED(m_struInfoKey.Copy(pApplication.GetApplicationId()));
return S_OK;
}
HRESULT HRESULT
APPLICATION_INFO::GetOrCreateApplication( APPLICATION_INFO::GetOrCreateApplication(
IHttpContext *pHttpContext, IHttpContext *pHttpContext,
@ -54,6 +39,7 @@ APPLICATION_INFO::GetOrCreateApplication(
// Call to wait for application to complete stopping // Call to wait for application to complete stopping
m_pApplication->Stop(/* fServerInitiated */ false); m_pApplication->Stop(/* fServerInitiated */ false);
m_pApplication = nullptr; m_pApplication = nullptr;
m_pApplicationFactory = nullptr;
} }
else else
{ {
@ -69,20 +55,13 @@ APPLICATION_INFO::GetOrCreateApplication(
} }
else else
{ {
STRU struExeLocation; FINISHED_IF_FAILED(m_handlerResolver.GetApplicationFactory(httpApplication, m_pApplicationFactory));
PFN_ASPNETCORE_CREATE_APPLICATION pfnAspNetCoreCreateApplication;
FINISHED_IF_FAILED(m_handlerResolver->GetApplicationFactory(httpApplication, struExeLocation, &pfnAspNetCoreCreateApplication));
std::array<APPLICATION_PARAMETER, 1> parameters {
{"InProcessExeLocation", struExeLocation.QueryStr()}
};
LOG_INFO("Creating handler application"); LOG_INFO("Creating handler application");
IAPPLICATION * newApplication; IAPPLICATION * newApplication;
FINISHED_IF_FAILED(pfnAspNetCoreCreateApplication( FINISHED_IF_FAILED(m_pApplicationFactory->Execute(
&m_pServer, &m_pServer,
&httpApplication, &httpApplication,
parameters.data(),
static_cast<DWORD>(parameters.size()),
&newApplication)); &newApplication));
m_pApplication.reset(newApplication); m_pApplication.reset(newApplication);
@ -110,50 +89,17 @@ Finished:
return hr; return hr;
} }
VOID VOID
APPLICATION_INFO::RecycleApplication() APPLICATION_INFO::ShutDownApplication(bool fServerInitiated)
{ {
SRWExclusiveLock lock(m_applicationLock); SRWExclusiveLock lock(m_applicationLock);
if (m_pApplication) if (m_pApplication)
{ {
const auto pApplication = m_pApplication.release(); LOG_ERRORF("Stopping application %S", QueryApplicationInfoKey().c_str());
m_pApplication ->Stop(fServerInitiated);
HandleWrapper<InvalidHandleTraits> hThread = CreateThread(
NULL, // default security attributes
0, // default stack size
(LPTHREAD_START_ROUTINE)DoRecycleApplication,
pApplication, // thread function arguments
0, // default creation flags
NULL); // receive thread identifier
}
}
DWORD WINAPI
APPLICATION_INFO::DoRecycleApplication(
LPVOID lpParam)
{
auto pApplication = std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER>(static_cast<IAPPLICATION*>(lpParam));
if (pApplication)
{
// Recycle will call shutdown for out of process
pApplication->Stop(/*fServerInitiated*/ false);
}
return 0;
}
VOID
APPLICATION_INFO::ShutDownApplication()
{
SRWExclusiveLock lock(m_applicationLock);
if (m_pApplication)
{
m_pApplication ->Stop(/* fServerInitiated */ true);
m_pApplication = nullptr; m_pApplication = nullptr;
m_pApplicationFactory = nullptr;
} }
} }

View File

@ -4,8 +4,6 @@
#pragma once #pragma once
#include "hostfxroptions.h" #include "hostfxroptions.h"
#include "hashtable.h"
#include "hashfn.h"
#include "aspnetcore_shim_config.h" #include "aspnetcore_shim_config.h"
#include "iapplication.h" #include "iapplication.h"
#include "SRWSharedLock.h" #include "SRWSharedLock.h"
@ -19,61 +17,35 @@ class APPLICATION_INFO
{ {
public: public:
APPLICATION_INFO(IHttpServer &pServer) : APPLICATION_INFO(
IHttpServer &pServer,
IHttpApplication &pApplication,
HandlerResolver &pHandlerResolver
) :
m_pServer(pServer), m_pServer(pServer),
m_cRefs(1), m_handlerResolver(pHandlerResolver),
m_handlerResolver(nullptr) m_strConfigPath(pApplication.GetAppConfigPath()),
m_strInfoKey(pApplication.GetApplicationId())
{ {
InitializeSRWLock(&m_applicationLock); InitializeSRWLock(&m_applicationLock);
} }
PCWSTR
QueryApplicationInfoKey()
{
return m_struInfoKey.QueryStr();
}
STRU*
QueryConfigPath()
{
return &m_struConfigPath;
}
virtual
~APPLICATION_INFO(); ~APPLICATION_INFO();
static std::wstring&
void QueryApplicationInfoKey()
StaticInitialize()
{ {
return m_strInfoKey;
} }
HRESULT std::wstring&
Initialize( QueryConfigPath()
IHttpApplication &pApplication,
HandlerResolver *pHandlerResolver
);
VOID
ReferenceApplicationInfo() const
{ {
InterlockedIncrement(&m_cRefs); return m_strConfigPath;
} }
VOID VOID
DereferenceApplicationInfo() const ShutDownApplication(bool fServerInitiated);
{
if (InterlockedDecrement(&m_cRefs) == 0)
{
delete this;
}
}
VOID
RecycleApplication();
VOID
ShutDownApplication();
HRESULT HRESULT
GetOrCreateApplication( GetOrCreateApplication(
@ -81,88 +53,30 @@ public:
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER>& pApplication std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER>& pApplication
); );
bool ConfigurationPathApplies(const std::wstring& path)
{
// We need to check that the last character of the config path
// is either a null terminator or a slash.
// This checks the case where the config path was
// MACHINE/WEBROOT/site and your site path is MACHINE/WEBROOT/siteTest
auto const changed = m_strConfigPath._Starts_with(path);
if (changed)
{
const auto lastChar = m_strConfigPath[m_strConfigPath.length()];
return lastChar == L'\0' || lastChar == L'/';
}
return false;
}
private: private:
static DWORD WINAPI DoRecycleApplication(LPVOID lpParam);
mutable LONG m_cRefs;
STRU m_struConfigPath;
STRU m_struInfoKey;
SRWLOCK m_applicationLock;
IHttpServer &m_pServer; IHttpServer &m_pServer;
HandlerResolver *m_handlerResolver; HandlerResolver &m_handlerResolver;
std::wstring m_strConfigPath;
std::wstring m_strInfoKey;
SRWLOCK m_applicationLock {};
std::unique_ptr<ApplicationFactory> m_pApplicationFactory;
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER> m_pApplication; std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER> m_pApplication;
}; };
class APPLICATION_INFO_HASH :
public HASH_TABLE<APPLICATION_INFO, PCWSTR>
{
public:
APPLICATION_INFO_HASH()
{}
PCWSTR
ExtractKey(
APPLICATION_INFO *pApplicationInfo
)
{
return pApplicationInfo->QueryApplicationInfoKey();
}
DWORD
CalcKeyHash(
PCWSTR pszApplicationId
)
{
return HashStringNoCase(pszApplicationId);
}
BOOL
EqualKeys(
PCWSTR pszKey1,
PCWSTR pszKey2
)
{
return CompareStringOrdinal(pszKey1,
-1,
pszKey2,
-1,
TRUE) == CSTR_EQUAL;
}
VOID
ReferenceRecord(
APPLICATION_INFO *pApplicationInfo
)
{
pApplicationInfo->ReferenceApplicationInfo();
}
VOID
DereferenceRecord(
APPLICATION_INFO *pApplicationInfo
)
{
pApplicationInfo->DereferenceApplicationInfo();
}
static
VOID
ReferenceCopyToTable(
APPLICATION_INFO * pEntry,
PVOID pvData
)
{
APPLICATION_INFO_HASH *pHash = static_cast<APPLICATION_INFO_HASH *>(pvData);
DBG_ASSERT(pHash);
pHash->InsertRecord(pEntry);
}
private:
APPLICATION_INFO_HASH(const APPLICATION_INFO_HASH &);
void operator=(const APPLICATION_INFO_HASH &);
};

View File

@ -20,25 +20,16 @@ APPLICATION_MANAGER* APPLICATION_MANAGER::sm_pApplicationManager = NULL;
// //
HRESULT HRESULT
APPLICATION_MANAGER::GetOrCreateApplicationInfo( APPLICATION_MANAGER::GetOrCreateApplicationInfo(
_In_ IHttpContext* pHttpContext, _In_ IHttpContext& pHttpContext,
_Out_ APPLICATION_INFO ** ppApplicationInfo _Out_ std::shared_ptr<APPLICATION_INFO>& ppApplicationInfo
) )
{ {
HRESULT hr = S_OK;
APPLICATION_INFO *pApplicationInfo = NULL;
PCWSTR pszApplicationId = NULL;
STACK_STRU ( strEventMsg, 256 ); auto &pApplication = *pHttpContext.GetApplication();
DBG_ASSERT(pHttpContext);
DBG_ASSERT(ppApplicationInfo);
*ppApplicationInfo = NULL;
IHttpApplication &pApplication = *pHttpContext->GetApplication();
// The configuration path is unique for each application and is used for the // The configuration path is unique for each application and is used for the
// key in the applicationInfoHash. // key in the applicationInfoHash.
pszApplicationId = pApplication.GetApplicationId(); std::wstring pszApplicationId = pApplication.GetApplicationId();
{ {
// When accessing the m_pApplicationInfoHash, we need to acquire the application manager // When accessing the m_pApplicationInfoHash, we need to acquire the application manager
@ -47,93 +38,41 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo(
if (g_fInShutdown) if (g_fInShutdown)
{ {
FINISHED(HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS)); return HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS);
} }
m_pApplicationInfoHash->FindKey(pszApplicationId, ppApplicationInfo); const auto pair = m_pApplicationInfoHash.find(pszApplicationId);
if (pair != m_pApplicationInfoHash.end())
{
ppApplicationInfo = pair->second;
return S_OK;
}
// It's important to release read lock here so exclusive lock // It's important to release read lock here so exclusive lock
// can be reacquired later as SRW lock doesn't allow upgrades // can be reacquired later as SRW lock doesn't allow upgrades
} }
if (*ppApplicationInfo == NULL) // Take exclusive lock before creating the application
SRWExclusiveLock writeLock(m_srwLock);
if (!m_fDebugInitialize)
{ {
// Take exclusive lock before creating the application DebugInitializeFromConfig(m_pHttpServer, pApplication);
SRWExclusiveLock writeLock(m_srwLock); m_fDebugInitialize = TRUE;
if (!m_fDebugInitialize)
{
DebugInitializeFromConfig(m_pHttpServer, pApplication);
m_fDebugInitialize = TRUE;
}
// Check if other thread created the application
m_pApplicationInfoHash->FindKey(pszApplicationId, ppApplicationInfo);
if (*ppApplicationInfo != NULL)
{
FINISHED(S_OK);
}
pApplicationInfo = new APPLICATION_INFO(m_pHttpServer);
FINISHED_IF_FAILED(pApplicationInfo->Initialize(pApplication, &m_handlerResolver));
FINISHED_IF_FAILED(m_pApplicationInfoHash->InsertRecord(pApplicationInfo));
*ppApplicationInfo = pApplicationInfo;
pApplicationInfo = NULL;
}
Finished:
if (pApplicationInfo != NULL)
{
pApplicationInfo->DereferenceApplicationInfo();
pApplicationInfo = NULL;
} }
return hr; // Check if other thread created the application
} const auto pair = m_pApplicationInfoHash.find(pszApplicationId);
if (pair != m_pApplicationInfoHash.end())
//
// If the application's configuration was changed,
// append the configuration path to the config change context.
//
BOOL
APPLICATION_MANAGER::FindConfigChangedApplication(
_In_ APPLICATION_INFO * pEntry,
_In_ PVOID pvContext)
{
DBG_ASSERT(pEntry);
DBG_ASSERT(pvContext);
// Config Change context contains the original config path that changed
// and a multiStr containing
CONFIG_CHANGE_CONTEXT* pContext = static_cast<CONFIG_CHANGE_CONTEXT*>(pvContext);
STRU* pstruConfigPath = pEntry->QueryConfigPath();
// check if the application path contains our app/subapp by seeing if the config path
// starts with the notification path.
BOOL fChanged = pstruConfigPath->StartsWith(pContext->pstrPath, true);
if (fChanged)
{ {
auto dwLen = wcslen(pContext->pstrPath); ppApplicationInfo = pair->second;
WCHAR wChar = pstruConfigPath->QueryStr()[dwLen]; return S_OK;
// We need to check that the last character of the config path
// is either a null terminator or a slash.
// This checks the case where the config path was
// MACHINE/WEBROOT/site and your site path is MACHINE/WEBROOT/siteTest
if (wChar != L'\0' && wChar != L'/')
{
// not current app or sub app
fChanged = FALSE;
}
else
{
pContext->MultiSz.Append(pEntry->QueryApplicationInfoKey());
}
} }
return fChanged;
ppApplicationInfo = std::make_shared<APPLICATION_INFO>(m_pHttpServer, pApplication, m_handlerResolver);
m_pApplicationInfoHash.emplace(pszApplicationId, ppApplicationInfo);
return S_OK;
} }
// //
@ -148,101 +87,82 @@ APPLICATION_MANAGER::RecycleApplicationFromManager(
_In_ LPCWSTR pszApplicationId _In_ LPCWSTR pszApplicationId
) )
{ {
HRESULT hr = S_OK; try
DWORD dwPreviousCounter = 0;
APPLICATION_INFO_HASH* table = NULL;
CONFIG_CHANGE_CONTEXT context;
if (g_fInShutdown)
{ {
// We are already shutting down, ignore this event as a global configuration change event std::vector<std::shared_ptr<APPLICATION_INFO>> applicationsToRecycle;
// can occur after global stop listening for some reason.
return hr;
}
{
SRWExclusiveLock lock(m_srwLock);
if (g_fInShutdown) if (g_fInShutdown)
{ {
return hr; // We are already shutting down, ignore this event as a global configuration change event
// can occur after global stop listening for some reason.
return S_OK;
} }
// Make a shallow copy of existing hashtable as we may need to remove nodes
// This will be used for finding differences in which applications are affected by a config change.
table = new APPLICATION_INFO_HASH();
// few application expected, small bucket size for hash table
if (FAILED(hr = table->Initialize(17 /*prime*/)))
{ {
goto Finished; SRWExclusiveLock lock(m_srwLock);
if (g_fInShutdown)
{
return S_OK;
}
const std::wstring configurationPath = pszApplicationId;
auto itr = m_pApplicationInfoHash.begin();
while (itr != m_pApplicationInfoHash.end())
{
if (itr->second->ConfigurationPathApplies(configurationPath))
{
applicationsToRecycle.emplace_back(itr->second);
itr = m_pApplicationInfoHash.erase(itr);
}
else
{
++itr;
}
}
// All applications were unloaded reset handler resolver validation logic
if (m_pApplicationInfoHash.empty())
{
m_handlerResolver.ResetHostingModel();
}
} }
context.pstrPath = pszApplicationId; // If we receive a request at this point.
// OutOfProcess: we will create a new application with new configuration
// InProcess: the request would have to be rejected, as we are about to call g_HttpServer->RecycleProcess
// on the worker proocess
// Keep track of the preview count of applications to know whether there are applications to delete if (!applicationsToRecycle.empty())
dwPreviousCounter = m_pApplicationInfoHash->Count();
// We don't want to hold the application manager lock for long time as it will block all incoming requests
// Don't call application shutdown inside the lock
m_pApplicationInfoHash->Apply(APPLICATION_INFO_HASH::ReferenceCopyToTable, static_cast<PVOID>(table));
DBG_ASSERT(dwPreviousCounter == table->Count());
// Removed the applications which are impacted by the configurtion change
m_pApplicationInfoHash->DeleteIf(FindConfigChangedApplication, (PVOID)&context);
}
// If we receive a request at this point.
// OutOfProcess: we will create a new application with new configuration
// InProcess: the request would have to be rejected, as we are about to call g_HttpServer->RecycleProcess
// on the worker proocess
if (!context.MultiSz.IsEmpty())
{
PCWSTR path = context.MultiSz.First();
// Iterate through each of the paths that were shut down,
// calling RecycleApplication on each of them.
while (path != NULL)
{ {
APPLICATION_INFO* pRecord; for (auto& application : applicationsToRecycle)
{
try
{
application->ShutDownApplication(/* fServerInitiated */ false);
}
catch (...)
{
LOG_ERRORF("Failed to stop application %S", application->QueryApplicationInfoKey().c_str());
OBSERVE_CAUGHT_EXCEPTION();
// Application got recycled. Log an event // Failed to recycle an application. Log an event
EventLog::Info( EventLog::Error(
ASPNETCORE_EVENT_RECYCLE_CONFIGURATION, ASPNETCORE_EVENT_RECYCLE_APP_FAILURE,
ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG, ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG,
path); pszApplicationId);
// Need to recycle the process as we cannot recycle the application
table->FindKey(path, &pRecord); if (!g_fRecycleProcessCalled)
DBG_ASSERT(pRecord != NULL); {
g_fRecycleProcessCalled = TRUE;
// RecycleApplication is called on a separate thread. m_pHttpServer.RecycleProcess(L"AspNetCore Recycle Process on Demand Due Application Recycle Error");
pRecord->RecycleApplication(); }
pRecord->DereferenceApplicationInfo(); }
path = context.MultiSz.Next(path); }
} }
} }
CATCH_RETURN();
Finished: return S_OK;
if (table != NULL)
{
table->Clear();
delete table;
}
if (FAILED(hr))
{
// Failed to recycle an application. Log an event
EventLog::Error(
ASPNETCORE_EVENT_RECYCLE_APP_FAILURE,
ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG,
pszApplicationId);
// Need to recycle the process as we cannot recycle the application
if (!g_fRecycleProcessCalled)
{
g_fRecycleProcessCalled = TRUE;
m_pHttpServer.RecycleProcess(L"AspNetCore Recycle Process on Demand Due Application Recycle Error");
}
}
return hr;
} }
// //
@ -256,34 +176,12 @@ APPLICATION_MANAGER::ShutDown()
// However, it is possible to receive multiple OnGlobalStopListening events // However, it is possible to receive multiple OnGlobalStopListening events
// Protect against this by checking if we already shut down. // Protect against this by checking if we already shut down.
g_fInShutdown = TRUE; g_fInShutdown = TRUE;
if (m_pApplicationInfoHash != NULL)
// During shutdown we lock until we delete the application
SRWExclusiveLock lock(m_srwLock);
for (auto &pair : m_pApplicationInfoHash)
{ {
DBG_ASSERT(m_pApplicationInfoHash); pair.second->ShutDownApplication(/* fServerInitiated */ true);
pair.second = nullptr;
// During shutdown we lock until we delete the application
SRWExclusiveLock lock(m_srwLock);
// Call shutdown on each application in the application manager
m_pApplicationInfoHash->Apply(ShutdownApplication, NULL);
m_pApplicationInfoHash->Clear();
delete m_pApplicationInfoHash;
m_pApplicationInfoHash = NULL;
} }
} }
//
// Calls shutdown on each application. ApplicationManager's lock is held for duration of
// each shutdown call, guaranteeing another application cannot be created.
//
// static
VOID
APPLICATION_MANAGER::ShutdownApplication(
_In_ APPLICATION_INFO * pEntry,
_In_ PVOID pvContext
)
{
UNREFERENCED_PARAMETER(pvContext);
DBG_ASSERT(pEntry != NULL);
pEntry->ShutDownApplication();
}

View File

@ -6,6 +6,7 @@
#include "applicationinfo.h" #include "applicationinfo.h"
#include "multisz.h" #include "multisz.h"
#include "exceptions.h" #include "exceptions.h"
#include <unordered_map>
#define DEFAULT_HASH_BUCKETS 17 #define DEFAULT_HASH_BUCKETS 17
@ -15,11 +16,6 @@
// Should always call GetInstance to get the object instance // Should always call GetInstance to get the object instance
// //
struct CONFIG_CHANGE_CONTEXT
{
PCWSTR pstrPath;
MULTISZ MultiSz;
};
class APPLICATION_MANAGER class APPLICATION_MANAGER
{ {
@ -35,9 +31,7 @@ public:
static static
VOID VOID
Cleanup( Cleanup()
VOID
)
{ {
if(sm_pApplicationManager != NULL) if(sm_pApplicationManager != NULL)
{ {
@ -46,24 +40,10 @@ public:
} }
} }
static
BOOL
FindConfigChangedApplication(
_In_ APPLICATION_INFO * pEntry,
_In_ PVOID pvContext
);
static
VOID
ShutdownApplication(
_In_ APPLICATION_INFO * pEntry,
_In_ PVOID pvContext
);
HRESULT HRESULT
GetOrCreateApplicationInfo( GetOrCreateApplicationInfo(
_In_ IHttpContext* pHttpContext, _In_ IHttpContext& pHttpContext,
_Out_ APPLICATION_INFO ** ppApplicationInfo _Out_ std::shared_ptr<APPLICATION_INFO>& ppApplicationInfo
); );
HRESULT HRESULT
@ -74,40 +54,13 @@ public:
VOID VOID
ShutDown(); ShutDown();
~APPLICATION_MANAGER()
{
if(m_pApplicationInfoHash != NULL)
{
m_pApplicationInfoHash->Clear();
delete m_pApplicationInfoHash;
m_pApplicationInfoHash = NULL;
}
}
static HRESULT StaticInitialize(HMODULE hModule, IHttpServer& pHttpServer) static HRESULT StaticInitialize(HMODULE hModule, IHttpServer& pHttpServer)
{ {
assert(!sm_pApplicationManager); assert(!sm_pApplicationManager);
sm_pApplicationManager = new APPLICATION_MANAGER(hModule, pHttpServer); sm_pApplicationManager = new APPLICATION_MANAGER(hModule, pHttpServer);
RETURN_IF_FAILED(sm_pApplicationManager->Initialize());
APPLICATION_INFO::StaticInitialize();
return S_OK; return S_OK;
} }
HRESULT Initialize()
{
if(m_pApplicationInfoHash == NULL)
{
try
{
m_pApplicationInfoHash = new APPLICATION_INFO_HASH();
}
CATCH_RETURN();
RETURN_IF_FAILED(m_pApplicationInfoHash->Initialize(DEFAULT_HASH_BUCKETS));
}
return S_OK;
}
private: private:
APPLICATION_MANAGER(HMODULE hModule, IHttpServer& pHttpServer) : APPLICATION_MANAGER(HMODULE hModule, IHttpServer& pHttpServer) :
@ -119,7 +72,7 @@ private:
InitializeSRWLock(&m_srwLock); InitializeSRWLock(&m_srwLock);
} }
APPLICATION_INFO_HASH *m_pApplicationInfoHash; std::unordered_map<std::wstring, std::shared_ptr<APPLICATION_INFO>> m_pApplicationInfoHash;
static APPLICATION_MANAGER *sm_pApplicationManager; static APPLICATION_MANAGER *sm_pApplicationManager;
SRWLOCK m_srwLock {}; SRWLOCK m_srwLock {};
BOOL m_fDebugInitialize; BOOL m_fDebugInitialize;

View File

@ -14,7 +14,6 @@ ASPNETCORE_SHIM_CONFIG::Populate(
) )
{ {
STACK_STRU(strHostingModel, 12); STACK_STRU(strHostingModel, 12);
STRU strApplicationFullPath;
CComPtr<IAppHostElement> pAspNetCoreElement; CComPtr<IAppHostElement> pAspNetCoreElement;
IAppHostAdminManager *pAdminManager = pHttpServer->GetAdminManager(); IAppHostAdminManager *pAdminManager = pHttpServer->GetAdminManager();
@ -25,9 +24,11 @@ ASPNETCORE_SHIM_CONFIG::Populate(
applicationConfigPath, applicationConfigPath,
&pAspNetCoreElement)); &pAspNetCoreElement));
CComBSTR struProcessPath;
RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement, RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement,
CS_ASPNETCORE_PROCESS_EXE_PATH, CS_ASPNETCORE_PROCESS_EXE_PATH,
&m_struProcessPath)); &struProcessPath));
m_strProcessPath = struProcessPath;
// Swallow this error for backward compatibility // Swallow this error for backward compatibility
// Use default behavior for empty string // Use default behavior for empty string
@ -53,13 +54,18 @@ ASPNETCORE_SHIM_CONFIG::Populate(
RETURN_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); RETURN_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
} }
CComBSTR struArguments;
RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement, RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement,
CS_ASPNETCORE_PROCESS_ARGUMENTS, CS_ASPNETCORE_PROCESS_ARGUMENTS,
&m_struArguments)); &struArguments));
m_strArguments = struArguments;
if (m_hostingModel == HOSTING_OUT_PROCESS) if (m_hostingModel == HOSTING_OUT_PROCESS)
{ {
RETURN_IF_FAILED(ConfigUtility::FindHandlerVersion(pAspNetCoreElement, m_struHandlerVersion)); STRU struHandlerVersion;
RETURN_IF_FAILED(ConfigUtility::FindHandlerVersion(pAspNetCoreElement, struHandlerVersion));
m_strHandlerVersion = struHandlerVersion.QueryStr();
} }

View File

@ -3,11 +3,10 @@
#pragma once #pragma once
#include <string>
#include <windows.h> #include <windows.h>
#include <httpserv.h> #include <httpserv.h>
#include "stringu.h"
#define CS_ASPNETCORE_SECTION L"system.webServer/aspNetCore" #define CS_ASPNETCORE_SECTION L"system.webServer/aspNetCore"
#define CS_ASPNETCORE_PROCESS_EXE_PATH L"processPath" #define CS_ASPNETCORE_PROCESS_EXE_PATH L"processPath"
#define CS_ASPNETCORE_PROCESS_ARGUMENTS L"arguments" #define CS_ASPNETCORE_PROCESS_ARGUMENTS L"arguments"
@ -34,16 +33,16 @@ public:
IHttpApplication *pHttpApplication IHttpApplication *pHttpApplication
); );
STRU* std::wstring&
QueryProcessPath() QueryProcessPath()
{ {
return &m_struProcessPath; return m_strProcessPath;
} }
STRU* std::wstring&
QueryArguments() QueryArguments()
{ {
return &m_struArguments; return m_strArguments;
} }
APP_HOSTING_MODEL APP_HOSTING_MODEL
@ -52,10 +51,10 @@ public:
return m_hostingModel; return m_hostingModel;
} }
STRU* std::wstring&
QueryHandlerVersion() QueryHandlerVersion()
{ {
return &m_struHandlerVersion; return m_strHandlerVersion;
} }
BOOL BOOL
@ -77,10 +76,10 @@ public:
private: private:
STRU m_struArguments; std::wstring m_strArguments;
STRU m_struProcessPath; std::wstring m_strProcessPath;
APP_HOSTING_MODEL m_hostingModel; APP_HOSTING_MODEL m_hostingModel;
STRU m_struHandlerVersion; std::wstring m_strHandlerVersion;
BOOL m_fStdoutLogEnabled; BOOL m_fStdoutLogEnabled;
STRU m_struStdoutLogFile; STRU m_struStdoutLogFile;
}; };

View File

@ -51,19 +51,10 @@ Return value:
} }
ASPNET_CORE_PROXY_MODULE::ASPNET_CORE_PROXY_MODULE( ASPNET_CORE_PROXY_MODULE::ASPNET_CORE_PROXY_MODULE(
) : m_pApplicationInfo(nullptr), m_pApplication(nullptr), m_pHandler(nullptr) ) : m_pApplicationInfo(nullptr), m_pHandler(nullptr)
{ {
} }
ASPNET_CORE_PROXY_MODULE::~ASPNET_CORE_PROXY_MODULE()
{
if (m_pApplicationInfo != NULL)
{
m_pApplicationInfo->DereferenceApplicationInfo();
m_pApplicationInfo = NULL;
}
}
__override __override
REQUEST_NOTIFICATION_STATUS REQUEST_NOTIFICATION_STATUS
ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler( ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
@ -72,9 +63,7 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
) )
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
APPLICATION_MANAGER *pApplicationManager = NULL;
REQUEST_NOTIFICATION_STATUS retVal = RQ_NOTIFICATION_CONTINUE; REQUEST_NOTIFICATION_STATUS retVal = RQ_NOTIFICATION_CONTINUE;
STRU struExeLocation;
try try
{ {
@ -83,13 +72,11 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
FINISHED(HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS)); FINISHED(HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS));
} }
pApplicationManager = APPLICATION_MANAGER::GetInstance(); auto pApplicationManager = APPLICATION_MANAGER::GetInstance();
FINISHED_IF_FAILED(pApplicationManager->GetOrCreateApplicationInfo( FINISHED_IF_FAILED(pApplicationManager->GetOrCreateApplicationInfo(
pHttpContext, *pHttpContext,
&m_pApplicationInfo)); m_pApplicationInfo));
DBG_ASSERT(pHttpContext);
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER> pApplication; std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER> pApplication;
FINISHED_IF_FAILED(m_pApplicationInfo->GetOrCreateApplication(pHttpContext, pApplication)); FINISHED_IF_FAILED(m_pApplicationInfo->GetOrCreateApplication(pHttpContext, pApplication));

View File

@ -15,7 +15,7 @@ class ASPNET_CORE_PROXY_MODULE : public CHttpModule
ASPNET_CORE_PROXY_MODULE(); ASPNET_CORE_PROXY_MODULE();
~ASPNET_CORE_PROXY_MODULE(); ~ASPNET_CORE_PROXY_MODULE() = default;
void * operator new(size_t size, IModuleAllocator * pPlacement) void * operator new(size_t size, IModuleAllocator * pPlacement)
{ {
@ -46,9 +46,7 @@ class ASPNET_CORE_PROXY_MODULE : public CHttpModule
); );
private: private:
std::shared_ptr<APPLICATION_INFO> m_pApplicationInfo;
APPLICATION_INFO *m_pApplicationInfo;
IAPPLICATION *m_pApplication;
std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER> m_pHandler; std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER> m_pHandler;
}; };

View File

@ -52,6 +52,13 @@ public:
HandleType* operator&() { return &m_handle; } HandleType* operator&() { return &m_handle; }
HandleType release() noexcept
{
auto value = m_handle;
m_handle = traits::DefaultHandle;
return value;
}
private: private:
HandleType m_handle; HandleType m_handle;
}; };

View File

@ -65,7 +65,7 @@ void SetDebugFlags(const std::wstring &debugValue)
{ {
try try
{ {
if (!debugValue.empty()) if (!debugValue.empty() && debugValue.find_first_not_of(L"0123456789") == std::wstring::npos)
{ {
const auto value = std::stoi(debugValue); const auto value = std::stoi(debugValue);

View File

@ -165,34 +165,3 @@ FILE_UTILITY::EnsureDirectoryPathExist(
Finished: Finished:
return hr; return hr;
} }
BOOL
FILE_UTILITY::CheckIfFileExists(
_In_ PCWSTR pszFilePath
)
{
HANDLE hFileHandle = INVALID_HANDLE_VALUE;
SECURITY_ATTRIBUTES saAttr;
BOOL fFileExists = FALSE;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
hFileHandle = CreateFile(pszFilePath,
GENERIC_READ,
FILE_SHARE_READ,
&saAttr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
fFileExists = hFileHandle != INVALID_HANDLE_VALUE || GetLastError() == ERROR_SHARING_VIOLATION;
if (fFileExists)
{
CloseHandle(hFileHandle);
}
return fFileExists;
}

View File

@ -25,12 +25,6 @@ public:
EnsureDirectoryPathExist( EnsureDirectoryPathExist(
_In_ LPCWSTR pszPath _In_ LPCWSTR pszPath
); );
static
BOOL
CheckIfFileExists(
PCWSTR pszFilePath
);
private: private:
static static
HRESULT HRESULT

View File

@ -14,8 +14,8 @@ enum APPLICATION_STATUS
struct APPLICATION_PARAMETER struct APPLICATION_PARAMETER
{ {
LPCSTR pzName; LPCSTR pzName;
PVOID pValue; const void *pValue;
}; };
class IAPPLICATION class IAPPLICATION

View File

@ -146,6 +146,8 @@ EnsureOutOfProcessInitializtion()
FINISHED(S_OK); FINISHED(S_OK);
} }
g_fOutOfProcessInitialize = TRUE;
g_hWinHttpModule = GetModuleHandle(TEXT("winhttp.dll")); g_hWinHttpModule = GetModuleHandle(TEXT("winhttp.dll"));
g_hAspNetCoreModule = GetModuleHandle(TEXT("aspnetcorev2.dll")); g_hAspNetCoreModule = GetModuleHandle(TEXT("aspnetcorev2.dll"));

View File

@ -1,12 +1,18 @@
// 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.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; 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
@ -14,6 +20,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
[Collection(PublishedSitesCollection.Name)] [Collection(PublishedSitesCollection.Name)]
public class ConfigurationChangeTests : IISFunctionalTestBase public class ConfigurationChangeTests : IISFunctionalTestBase
{ {
private static readonly TimeSpan RetryDelay = TimeSpan.FromMilliseconds(100);
private readonly PublishedSitesFixture _fixture; private readonly PublishedSitesFixture _fixture;
public ConfigurationChangeTests(PublishedSitesFixture fixture) public ConfigurationChangeTests(PublishedSitesFixture fixture)
@ -66,13 +73,54 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
await deploymentResult.AssertStarts(); await deploymentResult.AssertStarts();
deploymentResult.ModifyWebConfig(element => element deploymentResult.ModifyWebConfig(element => element
.GetOrAdd("system.webServer") .Descendants("system.webServer")
.Single()
.GetOrAdd("aspNetCore") .GetOrAdd("aspNetCore")
.SetAttributeValue("hostingModel", "inprocess")); .SetAttributeValue("hostingModel", "inprocess"));
// Have to retry here to allow ANCM to receive notification and react to it // Have to retry here to allow ANCM to receive notification and react to it
// Verify that worker process gets restarted with new process id // Verify that inprocess application was created and tried to start
await deploymentResult.HttpClient.RetryRequestAsync("/HelloWorld", r => r.StatusCode == HttpStatusCode.InternalServerError); await deploymentResult.HttpClient.RetryRequestAsync("/HelloWorld", r => r.StatusCode == HttpStatusCode.InternalServerError);
StopServer();
EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, "Could not find the assembly 'aspnetcorev2_inprocess.dll'");
}
[ConditionalTheory]
[InlineData(HostingModel.InProcess)]
[InlineData(HostingModel.OutOfProcess)]
public async Task ConfigurationTouchedStress(HostingModel hostingModel)
{
var deploymentResult = await DeployAsync(_fixture.GetBaseDeploymentParameters(hostingModel, publish: true));
await deploymentResult.AssertStarts();
var load = Helpers.StressLoad(deploymentResult.HttpClient, "/HelloWorld", response => {
var statusCode = (int)response.StatusCode;
Assert.True(statusCode == 200 || statusCode == 503, "Status code was " + statusCode);
});
for (int i = 0; i < 100; i++)
{
// ModifyWebConfig might fail if web.config is being read by IIS
RetryHelper.RetryOperation(
() => deploymentResult.ModifyWebConfig(element => {}),
e => Logger.LogError($"Failed to touch web.config : {e.Message}"),
retryCount: 3,
retryDelayMilliseconds: RetryDelay.Milliseconds);
}
try
{
await load;
}
catch (HttpRequestException ex) when (ex.InnerException is IOException | ex.InnerException is SocketException)
{
// IOException in InProcess is fine, just means process stopped
if (hostingModel != HostingModel.InProcess)
{
throw;
}
}
} }
} }
} }

View File

@ -98,11 +98,11 @@ TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
if (is64Bit) if (is64Bit)
{ {
fDotnetInProgramFiles = FILE_UTILITY::CheckIfFileExists(L"C:/Program Files/dotnet/dotnet.exe"); fDotnetInProgramFiles = std::filesystem::is_regular_file(L"C:/Program Files/dotnet/dotnet.exe");
} }
else else
{ {
fDotnetInProgramFiles = FILE_UTILITY::CheckIfFileExists(L"C:/Program Files (x86)/dotnet/dotnet.exe"); fDotnetInProgramFiles = std::filesystem::is_regular_file(L"C:/Program Files (x86)/dotnet/dotnet.exe");
} }
auto dotnetPath = HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles(); auto dotnetPath = HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles();