diff --git a/src/AspNetCoreModuleV2/AspNetCore/ApplicationFactory.h b/src/AspNetCoreModuleV2/AspNetCore/ApplicationFactory.h new file mode 100644 index 0000000000..380b0de6cc --- /dev/null +++ b/src/AspNetCoreModuleV2/AspNetCore/ApplicationFactory.h @@ -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 +#include +#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 parameters { + {"InProcessExeLocation", reinterpret_cast(m_location.data())} + }; + return m_pfnAspNetCoreCreateApplication(pServer, pHttpApplication, parameters.data(), static_cast(parameters.size()), pApplication); + } + +private: + PFN_ASPNETCORE_CREATE_APPLICATION m_pfnAspNetCoreCreateApplication; + std::wstring m_location; + HandleWrapper m_hRequestHandlerDll; +}; diff --git a/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj b/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj index 9663eb06ce..b809cdeadc 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj +++ b/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj @@ -227,6 +227,7 @@ + diff --git a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp index 49d1a29093..83c34a30c0 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp @@ -19,56 +19,59 @@ const PCWSTR HandlerResolver::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L" HandlerResolver::HandlerResolver(HMODULE hModule, IHttpServer &pServer) : m_hModule(hModule), m_pServer(pServer), - m_fAspnetcoreRHLoadResult(S_FALSE), - m_loadedApplicationHostingModel(HOSTING_UNKNOWN), - m_hRequestHandlerDll(nullptr), - m_pfnAspNetCoreCreateApplication(nullptr) + m_loadedApplicationHostingModel(HOSTING_UNKNOWN) { InitializeSRWLock(&m_requestHandlerLoadLock); } HRESULT -HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, STRU& location, ASPNETCORE_SHIM_CONFIG * pConfiguration) +HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, ASPNETCORE_SHIM_CONFIG& pConfiguration, std::unique_ptr& pApplicationFactory) { HRESULT hr; - STACK_STRU(struFileName, MAX_PATH); 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; } else { + // OutOfProcess handler is not able to handle unload correctly + // It has code running after application.Stop exits + preventUnload = true; pstrHandlerDllName = s_pwzAspnetcoreOutOfProcessRequestHandlerName; } - + HandleWrapper hRequestHandlerDll; + std::wstring location; + std::wstring handlerDllPath; // 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 options; std::unique_ptr outputManager; RETURN_IF_FAILED(HOSTFXR_OPTIONS::Create( NULL, - pConfiguration->QueryProcessPath()->QueryStr(), + pConfiguration.QueryProcessPath().c_str(), pApplication.GetApplicationPhysicalPath(), - pConfiguration->QueryArguments()->QueryStr(), + pConfiguration.QueryArguments().c_str(), options)); - RETURN_IF_FAILED(location.Copy(options->GetExeLocation())); + location = options->GetExeLocation(); RETURN_IF_FAILED(LoggingHelpers::CreateLoggingProvider( - pConfiguration->QueryStdoutLogEnabled(), + pConfiguration.QueryStdoutLogEnabled(), !m_pServer.IsCommandLineLaunch(), - pConfiguration->QueryStdoutLogFile()->QueryStr(), + pConfiguration.QueryStdoutLogFile()->QueryStr(), pApplication.GetApplicationPhysicalPath(), outputManager)); outputManager->Start(); - hr = FindNativeAssemblyFromHostfxr(options.get(), pstrHandlerDllName, &struFileName); + hr = FindNativeAssemblyFromHostfxr(*options.get(), pstrHandlerDllName, handlerDllPath); outputManager->Stop(); if (FAILED(hr) && m_hHostFxrDll != NULL) @@ -85,85 +88,99 @@ HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, STRU EventLog::Error( ASPNETCORE_EVENT_GENERAL_ERROR, ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG, - struFileName.IsEmpty() ? s_pwzAspnetcoreInProcessRequestHandlerName : struFileName.QueryStr(), + handlerDllPath.empty()? s_pwzAspnetcoreInProcessRequestHandlerName : handlerDllPath.c_str(), struStdMsg.QueryStr()); } } else { - if (FAILED_LOG(hr = FindNativeAssemblyFromGlobalLocation(pConfiguration, pstrHandlerDllName, &struFileName))) + if (FAILED_LOG(hr = FindNativeAssemblyFromGlobalLocation(pConfiguration, pstrHandlerDllName, handlerDllPath))) { EventLog::Error( ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING, ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG, - struFileName.IsEmpty() ? s_pwzAspnetcoreOutOfProcessRequestHandlerName : struFileName.QueryStr()); + handlerDllPath.empty() ? s_pwzAspnetcoreOutOfProcessRequestHandlerName : handlerDllPath.c_str()); return hr; } } - LOG_INFOF("Loading request handler: %S", struFileName.QueryStr()); + LOG_INFOF("Loading request handler: %S", handlerDllPath.c_str()); - m_hRequestHandlerDll = LoadLibraryW(struFileName.QueryStr()); - RETURN_LAST_ERROR_IF_NULL(m_hRequestHandlerDll); + hRequestHandlerDll = LoadLibrary(handlerDllPath.c_str()); + 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(GetProcAddress(m_hRequestHandlerDll, "CreateApplication")); - - RETURN_LAST_ERROR_IF_NULL(m_pfnAspNetCoreCreateApplication); + auto pfnAspNetCoreCreateApplication = reinterpret_cast(GetProcAddress(hRequestHandlerDll, "CreateApplication")); + RETURN_LAST_ERROR_IF_NULL(pfnAspNetCoreCreateApplication); + pApplicationFactory = std::make_unique(hRequestHandlerDll.release(), location, pfnAspNetCoreCreateApplication); return S_OK; } HRESULT -HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, STRU& location, PFN_ASPNETCORE_CREATE_APPLICATION * pfnCreateApplication) +HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, std::unique_ptr& pApplicationFactory) { - ASPNETCORE_SHIM_CONFIG pConfiguration; - RETURN_IF_FAILED(pConfiguration.Populate(&m_pServer, &pApplication)); - - if (m_fAspnetcoreRHLoadResult == S_FALSE) + try { + ASPNETCORE_SHIM_CONFIG pConfiguration; + RETURN_IF_FAILED(pConfiguration.Populate(&m_pServer, &pApplication)); + SRWExclusiveLock lock(m_requestHandlerLoadLock); - if (m_fAspnetcoreRHLoadResult == S_FALSE) + if (m_loadedApplicationHostingModel != HOSTING_UNKNOWN) { - m_loadedApplicationHostingModel = pConfiguration.QueryHostingModel(); - m_loadedApplicationId = pApplication.GetApplicationId(); - LOG_IF_FAILED(m_fAspnetcoreRHLoadResult = LoadRequestHandlerAssembly(pApplication, location, &pConfiguration)); + // Mixed hosting models + 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; + } + // 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 - 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 S_OK; +} - return E_FAIL; - } - // Multiple in-process apps - 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()); +void HandlerResolver::ResetHostingModel() +{ + SRWExclusiveLock lock(m_requestHandlerLoadLock); - return E_FAIL; - } - - *pfnCreateApplication = m_pfnAspNetCoreCreateApplication; - return m_fAspnetcoreRHLoadResult; + m_loadedApplicationHostingModel = APP_HOSTING_MODEL::HOSTING_UNKNOWN; + m_loadedApplicationId.resize(0); } HRESULT HandlerResolver::FindNativeAssemblyFromGlobalLocation( - ASPNETCORE_SHIM_CONFIG * pConfiguration, + ASPNETCORE_SHIM_CONFIG& pConfiguration, PCWSTR pstrHandlerDllName, - STRU* struFilename + std::wstring& handlerDllPath ) { try @@ -172,12 +189,10 @@ HandlerResolver::FindNativeAssemblyFromGlobalLocation( modulePath = GlobalVersionUtility::RemoveFileNameFromFolderPath(modulePath); - std::wstring retval = GlobalVersionUtility::GetGlobalRequestHandlerPath(modulePath.c_str(), - pConfiguration->QueryHandlerVersion()->QueryStr(), + handlerDllPath = GlobalVersionUtility::GetGlobalRequestHandlerPath(modulePath.c_str(), + pConfiguration.QueryHandlerVersion().c_str(), pstrHandlerDllName ); - - RETURN_IF_FAILED(struFilename->Copy(retval.c_str())); } catch (...) { @@ -199,25 +214,18 @@ HandlerResolver::FindNativeAssemblyFromGlobalLocation( // HRESULT HandlerResolver::FindNativeAssemblyFromHostfxr( - HOSTFXR_OPTIONS* hostfxrOptions, + HOSTFXR_OPTIONS& hostfxrOptions, PCWSTR libraryName, - STRU* struFilename + std::wstring& handlerDllPath ) { - STRU struApplicationFullPath; - STRU struNativeSearchPaths; - STRU struNativeDllLocation; - INT intIndex = -1; - INT intPrevIndex = 0; - BOOL fFound = FALSE; - DWORD dwBufferSize = 1024 * 10; - DWORD dwRequiredBufferSize = 0; - STRA output; + std::wstring struNativeSearchPaths; + size_t intIndex; + size_t intPrevIndex = 0; + DWORD dwBufferSize = s_initialGetNativeSearchDirectoriesBufferSize; + DWORD dwRequiredBufferSize = 0; - - DBG_ASSERT(struFilename != NULL); - - RETURN_LAST_ERROR_IF_NULL(m_hHostFxrDll = LoadLibraryW(hostfxrOptions->GetHostFxrLocation())); + RETURN_LAST_ERROR_IF_NULL(m_hHostFxrDll = LoadLibraryW(hostfxrOptions.GetHostFxrLocation())); auto pFnHostFxrSearchDirectories = reinterpret_cast(GetProcAddress(m_hHostFxrDll, "hostfxr_get_native_search_directories")); if (pFnHostFxrSearchDirectories == nullptr) @@ -225,20 +233,20 @@ HandlerResolver::FindNativeAssemblyFromHostfxr( EventLog::Error( ASPNETCORE_EVENT_GENERAL_ERROR, ASPNETCORE_EVENT_HOSTFXR_DLL_INVALID_VERSION_MSG, - hostfxrOptions->GetHostFxrLocation() + hostfxrOptions.GetHostFxrLocation() ); RETURN_IF_FAILED(E_FAIL); } RETURN_LAST_ERROR_IF_NULL(pFnHostFxrSearchDirectories); - RETURN_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize)); + struNativeSearchPaths.resize(dwBufferSize); while (TRUE) { - auto intHostFxrExitCode = pFnHostFxrSearchDirectories( - hostfxrOptions->GetArgc(), - hostfxrOptions->GetArgv(), - struNativeSearchPaths.QueryStr(), + const auto intHostFxrExitCode = pFnHostFxrSearchDirectories( + hostfxrOptions.GetArgc(), + hostfxrOptions.GetArgv(), + struNativeSearchPaths.data(), dwBufferSize, &dwRequiredBufferSize ); @@ -251,7 +259,7 @@ HandlerResolver::FindNativeAssemblyFromHostfxr( { dwBufferSize = dwRequiredBufferSize + 1; // for null terminator - RETURN_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize)); + struNativeSearchPaths.resize(dwBufferSize); } 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. // 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; break; } diff --git a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h index 847d664956..101b1ad2a0 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h +++ b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h @@ -3,47 +3,35 @@ #pragma once #include "aspnetcore_shim_config.h" -#include "hostfxroptions.h" -#include -#include "iapplication.h" -#include -#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 - ); +#include +#include +#include "hostfxroptions.h" +#include "HandleWrapper.h" +#include "ApplicationFactory.h" class HandlerResolver { public: HandlerResolver(HMODULE hModule, IHttpServer &pServer); - HRESULT GetApplicationFactory(IHttpApplication &pApplication, STRU& location, PFN_ASPNETCORE_CREATE_APPLICATION *pfnCreateApplication); + HRESULT GetApplicationFactory(IHttpApplication &pApplication, std::unique_ptr& pApplicationFactory); + void ResetHostingModel(); private: - HRESULT LoadRequestHandlerAssembly(IHttpApplication &pApplication,STRU& location, ASPNETCORE_SHIM_CONFIG * pConfiguration); - HRESULT FindNativeAssemblyFromGlobalLocation(ASPNETCORE_SHIM_CONFIG * pConfiguration, PCWSTR libraryName, STRU* location); - HRESULT FindNativeAssemblyFromHostfxr(HOSTFXR_OPTIONS* hostfxrOptions, PCWSTR libraryName, STRU* location); + HRESULT LoadRequestHandlerAssembly(IHttpApplication &pApplication, ASPNETCORE_SHIM_CONFIG& pConfiguration, std::unique_ptr& pApplicationFactory); + HRESULT FindNativeAssemblyFromGlobalLocation(ASPNETCORE_SHIM_CONFIG& pConfiguration, PCWSTR libraryName, std::wstring& handlerDllPath); + HRESULT FindNativeAssemblyFromHostfxr(HOSTFXR_OPTIONS& hostfxrOptions, PCWSTR libraryName, std::wstring& handlerDllPath); HMODULE m_hModule; IHttpServer &m_pServer; SRWLOCK m_requestHandlerLoadLock {}; - // S_FALSE - not loaded, S_OK - loaded, everything else - error - HRESULT m_fAspnetcoreRHLoadResult; std::wstring m_loadedApplicationId; APP_HOSTING_MODEL m_loadedApplicationHostingModel; - HandleWrapper m_hRequestHandlerDll; HandleWrapper m_hHostFxrDll; - PFN_ASPNETCORE_CREATE_APPLICATION m_pfnAspNetCoreCreateApplication; - static const PCWSTR s_pwzAspnetcoreInProcessRequestHandlerName; static const PCWSTR s_pwzAspnetcoreOutOfProcessRequestHandlerName; + static const DWORD s_initialGetNativeSearchDirectoriesBufferSize = MAX_PATH * 4; }; diff --git a/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp b/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp index 2a00d3c82c..e250045677 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp @@ -3,7 +3,6 @@ #include "applicationinfo.h" -#include #include "proxymodule.h" #include "hostfxr_utility.h" #include "debugutil.h" @@ -11,28 +10,14 @@ #include "SRWExclusiveLock.h" #include "exceptions.h" #include "EventLog.h" -#include "HandleWrapper.h" #include "ServerErrorApplication.h" #include "AppOfflineApplication.h" 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 APPLICATION_INFO::GetOrCreateApplication( IHttpContext *pHttpContext, @@ -54,6 +39,7 @@ APPLICATION_INFO::GetOrCreateApplication( // Call to wait for application to complete stopping m_pApplication->Stop(/* fServerInitiated */ false); m_pApplication = nullptr; + m_pApplicationFactory = nullptr; } else { @@ -69,20 +55,13 @@ APPLICATION_INFO::GetOrCreateApplication( } else { - STRU struExeLocation; - PFN_ASPNETCORE_CREATE_APPLICATION pfnAspNetCoreCreateApplication; - FINISHED_IF_FAILED(m_handlerResolver->GetApplicationFactory(httpApplication, struExeLocation, &pfnAspNetCoreCreateApplication)); - std::array parameters { - {"InProcessExeLocation", struExeLocation.QueryStr()} - }; + FINISHED_IF_FAILED(m_handlerResolver.GetApplicationFactory(httpApplication, m_pApplicationFactory)); LOG_INFO("Creating handler application"); IAPPLICATION * newApplication; - FINISHED_IF_FAILED(pfnAspNetCoreCreateApplication( + FINISHED_IF_FAILED(m_pApplicationFactory->Execute( &m_pServer, &httpApplication, - parameters.data(), - static_cast(parameters.size()), &newApplication)); m_pApplication.reset(newApplication); @@ -110,50 +89,17 @@ Finished: return hr; } + VOID -APPLICATION_INFO::RecycleApplication() +APPLICATION_INFO::ShutDownApplication(bool fServerInitiated) { SRWExclusiveLock lock(m_applicationLock); if (m_pApplication) { - const auto pApplication = m_pApplication.release(); - - HandleWrapper 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(static_cast(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); + LOG_ERRORF("Stopping application %S", QueryApplicationInfoKey().c_str()); + m_pApplication ->Stop(fServerInitiated); m_pApplication = nullptr; + m_pApplicationFactory = nullptr; } } diff --git a/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.h b/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.h index ddf3e23aaa..90f9fe8df9 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.h +++ b/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.h @@ -4,8 +4,6 @@ #pragma once #include "hostfxroptions.h" -#include "hashtable.h" -#include "hashfn.h" #include "aspnetcore_shim_config.h" #include "iapplication.h" #include "SRWSharedLock.h" @@ -19,61 +17,35 @@ class APPLICATION_INFO { public: - APPLICATION_INFO(IHttpServer &pServer) : + APPLICATION_INFO( + IHttpServer &pServer, + IHttpApplication &pApplication, + HandlerResolver &pHandlerResolver + ) : m_pServer(pServer), - m_cRefs(1), - m_handlerResolver(nullptr) + m_handlerResolver(pHandlerResolver), + m_strConfigPath(pApplication.GetAppConfigPath()), + m_strInfoKey(pApplication.GetApplicationId()) { InitializeSRWLock(&m_applicationLock); } - PCWSTR - QueryApplicationInfoKey() - { - return m_struInfoKey.QueryStr(); - } - - STRU* - QueryConfigPath() - { - return &m_struConfigPath; - } - - virtual ~APPLICATION_INFO(); - static - void - StaticInitialize() + std::wstring& + QueryApplicationInfoKey() { + return m_strInfoKey; } - HRESULT - Initialize( - IHttpApplication &pApplication, - HandlerResolver *pHandlerResolver - ); - - VOID - ReferenceApplicationInfo() const + std::wstring& + QueryConfigPath() { - InterlockedIncrement(&m_cRefs); + return m_strConfigPath; } VOID - DereferenceApplicationInfo() const - { - if (InterlockedDecrement(&m_cRefs) == 0) - { - delete this; - } - } - - VOID - RecycleApplication(); - - VOID - ShutDownApplication(); + ShutDownApplication(bool fServerInitiated); HRESULT GetOrCreateApplication( @@ -81,88 +53,30 @@ public: std::unique_ptr& 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: - - static DWORD WINAPI DoRecycleApplication(LPVOID lpParam); - - mutable LONG m_cRefs; - STRU m_struConfigPath; - STRU m_struInfoKey; - SRWLOCK m_applicationLock; IHttpServer &m_pServer; - HandlerResolver *m_handlerResolver; + HandlerResolver &m_handlerResolver; + std::wstring m_strConfigPath; + std::wstring m_strInfoKey; + SRWLOCK m_applicationLock {}; + + std::unique_ptr m_pApplicationFactory; std::unique_ptr m_pApplication; }; -class APPLICATION_INFO_HASH : - public HASH_TABLE -{ - -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(pvData); - DBG_ASSERT(pHash); - pHash->InsertRecord(pEntry); - } - -private: - - APPLICATION_INFO_HASH(const APPLICATION_INFO_HASH &); - void operator=(const APPLICATION_INFO_HASH &); -}; diff --git a/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp b/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp index 25d5966a3d..36fc9a96ba 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp @@ -20,25 +20,16 @@ APPLICATION_MANAGER* APPLICATION_MANAGER::sm_pApplicationManager = NULL; // HRESULT APPLICATION_MANAGER::GetOrCreateApplicationInfo( - _In_ IHttpContext* pHttpContext, - _Out_ APPLICATION_INFO ** ppApplicationInfo + _In_ IHttpContext& pHttpContext, + _Out_ std::shared_ptr& ppApplicationInfo ) { - HRESULT hr = S_OK; - APPLICATION_INFO *pApplicationInfo = NULL; - PCWSTR pszApplicationId = NULL; - STACK_STRU ( strEventMsg, 256 ); - - DBG_ASSERT(pHttpContext); - DBG_ASSERT(ppApplicationInfo); - - *ppApplicationInfo = NULL; - IHttpApplication &pApplication = *pHttpContext->GetApplication(); + auto &pApplication = *pHttpContext.GetApplication(); // The configuration path is unique for each application and is used for the // key in the applicationInfoHash. - pszApplicationId = pApplication.GetApplicationId(); + std::wstring pszApplicationId = pApplication.GetApplicationId(); { // When accessing the m_pApplicationInfoHash, we need to acquire the application manager @@ -47,93 +38,41 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo( 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 // 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 - SRWExclusiveLock writeLock(m_srwLock); - - 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; + DebugInitializeFromConfig(m_pHttpServer, pApplication); + m_fDebugInitialize = TRUE; } - return hr; -} - - -// -// 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(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) + // Check if other thread created the application + const auto pair = m_pApplicationInfoHash.find(pszApplicationId); + if (pair != m_pApplicationInfoHash.end()) { - auto dwLen = wcslen(pContext->pstrPath); - WCHAR wChar = pstruConfigPath->QueryStr()[dwLen]; - - // 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()); - } + ppApplicationInfo = pair->second; + return S_OK; } - return fChanged; + + ppApplicationInfo = std::make_shared(m_pHttpServer, pApplication, m_handlerResolver); + m_pApplicationInfoHash.emplace(pszApplicationId, ppApplicationInfo); + + return S_OK; } // @@ -148,101 +87,82 @@ APPLICATION_MANAGER::RecycleApplicationFromManager( _In_ LPCWSTR pszApplicationId ) { - HRESULT hr = S_OK; - DWORD dwPreviousCounter = 0; - APPLICATION_INFO_HASH* table = NULL; - CONFIG_CHANGE_CONTEXT context; - - if (g_fInShutdown) + try { - // We are already shutting down, ignore this event as a global configuration change event - // can occur after global stop listening for some reason. - return hr; - } + std::vector> applicationsToRecycle; - { - SRWExclusiveLock lock(m_srwLock); 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 - 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(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) + if (!applicationsToRecycle.empty()) { - 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 - EventLog::Info( - ASPNETCORE_EVENT_RECYCLE_CONFIGURATION, - ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG, - path); - - table->FindKey(path, &pRecord); - DBG_ASSERT(pRecord != NULL); - - // RecycleApplication is called on a separate thread. - pRecord->RecycleApplication(); - pRecord->DereferenceApplicationInfo(); - path = context.MultiSz.Next(path); + // 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"); + } + } + } } } + CATCH_RETURN(); -Finished: - 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; + return S_OK; } // @@ -256,34 +176,12 @@ APPLICATION_MANAGER::ShutDown() // However, it is possible to receive multiple OnGlobalStopListening events // Protect against this by checking if we already shut down. 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); - - // 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; + pair.second->ShutDownApplication(/* fServerInitiated */ true); + pair.second = nullptr; } } - -// -// 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(); -} diff --git a/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.h b/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.h index e0f2025f04..5cb341087d 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.h +++ b/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.h @@ -6,6 +6,7 @@ #include "applicationinfo.h" #include "multisz.h" #include "exceptions.h" +#include #define DEFAULT_HASH_BUCKETS 17 @@ -15,11 +16,6 @@ // Should always call GetInstance to get the object instance // -struct CONFIG_CHANGE_CONTEXT -{ - PCWSTR pstrPath; - MULTISZ MultiSz; -}; class APPLICATION_MANAGER { @@ -35,9 +31,7 @@ public: static VOID - Cleanup( - VOID - ) + Cleanup() { 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 GetOrCreateApplicationInfo( - _In_ IHttpContext* pHttpContext, - _Out_ APPLICATION_INFO ** ppApplicationInfo + _In_ IHttpContext& pHttpContext, + _Out_ std::shared_ptr& ppApplicationInfo ); HRESULT @@ -74,40 +54,13 @@ public: VOID ShutDown(); - ~APPLICATION_MANAGER() - { - - if(m_pApplicationInfoHash != NULL) - { - m_pApplicationInfoHash->Clear(); - delete m_pApplicationInfoHash; - m_pApplicationInfoHash = NULL; - } - } - static HRESULT StaticInitialize(HMODULE hModule, IHttpServer& pHttpServer) { assert(!sm_pApplicationManager); sm_pApplicationManager = new APPLICATION_MANAGER(hModule, pHttpServer); - RETURN_IF_FAILED(sm_pApplicationManager->Initialize()); - - APPLICATION_INFO::StaticInitialize(); 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: APPLICATION_MANAGER(HMODULE hModule, IHttpServer& pHttpServer) : @@ -119,7 +72,7 @@ private: InitializeSRWLock(&m_srwLock); } - APPLICATION_INFO_HASH *m_pApplicationInfoHash; + std::unordered_map> m_pApplicationInfoHash; static APPLICATION_MANAGER *sm_pApplicationManager; SRWLOCK m_srwLock {}; BOOL m_fDebugInitialize; diff --git a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp index bf62cd9e87..f994fcc06d 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp @@ -14,7 +14,6 @@ ASPNETCORE_SHIM_CONFIG::Populate( ) { STACK_STRU(strHostingModel, 12); - STRU strApplicationFullPath; CComPtr pAspNetCoreElement; IAppHostAdminManager *pAdminManager = pHttpServer->GetAdminManager(); @@ -25,9 +24,11 @@ ASPNETCORE_SHIM_CONFIG::Populate( applicationConfigPath, &pAspNetCoreElement)); + CComBSTR struProcessPath; RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement, CS_ASPNETCORE_PROCESS_EXE_PATH, - &m_struProcessPath)); + &struProcessPath)); + m_strProcessPath = struProcessPath; // Swallow this error for backward compatibility // Use default behavior for empty string @@ -53,13 +54,18 @@ ASPNETCORE_SHIM_CONFIG::Populate( RETURN_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } + CComBSTR struArguments; RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement, CS_ASPNETCORE_PROCESS_ARGUMENTS, - &m_struArguments)); + &struArguments)); + + m_strArguments = struArguments; 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(); } diff --git a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h index e80071b1c9..c380433e72 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h +++ b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h @@ -3,11 +3,10 @@ #pragma once +#include #include #include -#include "stringu.h" - #define CS_ASPNETCORE_SECTION L"system.webServer/aspNetCore" #define CS_ASPNETCORE_PROCESS_EXE_PATH L"processPath" #define CS_ASPNETCORE_PROCESS_ARGUMENTS L"arguments" @@ -34,16 +33,16 @@ public: IHttpApplication *pHttpApplication ); - STRU* + std::wstring& QueryProcessPath() { - return &m_struProcessPath; + return m_strProcessPath; } - STRU* + std::wstring& QueryArguments() { - return &m_struArguments; + return m_strArguments; } APP_HOSTING_MODEL @@ -52,10 +51,10 @@ public: return m_hostingModel; } - STRU* + std::wstring& QueryHandlerVersion() { - return &m_struHandlerVersion; + return m_strHandlerVersion; } BOOL @@ -77,10 +76,10 @@ public: private: - STRU m_struArguments; - STRU m_struProcessPath; - APP_HOSTING_MODEL m_hostingModel; - STRU m_struHandlerVersion; + std::wstring m_strArguments; + std::wstring m_strProcessPath; + APP_HOSTING_MODEL m_hostingModel; + std::wstring m_strHandlerVersion; BOOL m_fStdoutLogEnabled; STRU m_struStdoutLogFile; }; diff --git a/src/AspNetCoreModuleV2/AspNetCore/proxymodule.cpp b/src/AspNetCoreModuleV2/AspNetCore/proxymodule.cpp index 2d9fe9150c..b4ed0fbaad 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/proxymodule.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/proxymodule.cpp @@ -51,19 +51,10 @@ Return value: } 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 REQUEST_NOTIFICATION_STATUS ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler( @@ -72,9 +63,7 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler( ) { HRESULT hr = S_OK; - APPLICATION_MANAGER *pApplicationManager = NULL; REQUEST_NOTIFICATION_STATUS retVal = RQ_NOTIFICATION_CONTINUE; - STRU struExeLocation; try { @@ -83,13 +72,11 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler( FINISHED(HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS)); } - pApplicationManager = APPLICATION_MANAGER::GetInstance(); + auto pApplicationManager = APPLICATION_MANAGER::GetInstance(); FINISHED_IF_FAILED(pApplicationManager->GetOrCreateApplicationInfo( - pHttpContext, - &m_pApplicationInfo)); - - DBG_ASSERT(pHttpContext); + *pHttpContext, + m_pApplicationInfo)); std::unique_ptr pApplication; FINISHED_IF_FAILED(m_pApplicationInfo->GetOrCreateApplication(pHttpContext, pApplication)); diff --git a/src/AspNetCoreModuleV2/AspNetCore/proxymodule.h b/src/AspNetCoreModuleV2/AspNetCore/proxymodule.h index a66a0fd3e1..06e333d595 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/proxymodule.h +++ b/src/AspNetCoreModuleV2/AspNetCore/proxymodule.h @@ -15,7 +15,7 @@ class ASPNET_CORE_PROXY_MODULE : public CHttpModule ASPNET_CORE_PROXY_MODULE(); - ~ASPNET_CORE_PROXY_MODULE(); + ~ASPNET_CORE_PROXY_MODULE() = default; void * operator new(size_t size, IModuleAllocator * pPlacement) { @@ -46,9 +46,7 @@ class ASPNET_CORE_PROXY_MODULE : public CHttpModule ); private: - - APPLICATION_INFO *m_pApplicationInfo; - IAPPLICATION *m_pApplication; + std::shared_ptr m_pApplicationInfo; std::unique_ptr m_pHandler; }; diff --git a/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h b/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h index d6b50f2381..1d607210e2 100644 --- a/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h +++ b/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h @@ -52,6 +52,13 @@ public: HandleType* operator&() { return &m_handle; } + HandleType release() noexcept + { + auto value = m_handle; + m_handle = traits::DefaultHandle; + return value; + } + private: HandleType m_handle; }; diff --git a/src/AspNetCoreModuleV2/CommonLib/debugutil.cpp b/src/AspNetCoreModuleV2/CommonLib/debugutil.cpp index e31351a764..7184dbfde8 100644 --- a/src/AspNetCoreModuleV2/CommonLib/debugutil.cpp +++ b/src/AspNetCoreModuleV2/CommonLib/debugutil.cpp @@ -65,7 +65,7 @@ void SetDebugFlags(const std::wstring &debugValue) { try { - if (!debugValue.empty()) + if (!debugValue.empty() && debugValue.find_first_not_of(L"0123456789") == std::wstring::npos) { const auto value = std::stoi(debugValue); diff --git a/src/AspNetCoreModuleV2/CommonLib/file_utility.cpp b/src/AspNetCoreModuleV2/CommonLib/file_utility.cpp index 0d12c32259..a971821208 100644 --- a/src/AspNetCoreModuleV2/CommonLib/file_utility.cpp +++ b/src/AspNetCoreModuleV2/CommonLib/file_utility.cpp @@ -165,34 +165,3 @@ FILE_UTILITY::EnsureDirectoryPathExist( Finished: 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; -} diff --git a/src/AspNetCoreModuleV2/CommonLib/file_utility.h b/src/AspNetCoreModuleV2/CommonLib/file_utility.h index 900e787cd7..cc541b712a 100644 --- a/src/AspNetCoreModuleV2/CommonLib/file_utility.h +++ b/src/AspNetCoreModuleV2/CommonLib/file_utility.h @@ -25,12 +25,6 @@ public: EnsureDirectoryPathExist( _In_ LPCWSTR pszPath ); - - static - BOOL - CheckIfFileExists( - PCWSTR pszFilePath - ); private: static HRESULT diff --git a/src/AspNetCoreModuleV2/CommonLib/iapplication.h b/src/AspNetCoreModuleV2/CommonLib/iapplication.h index 1f69a39bbb..222230c672 100644 --- a/src/AspNetCoreModuleV2/CommonLib/iapplication.h +++ b/src/AspNetCoreModuleV2/CommonLib/iapplication.h @@ -14,8 +14,8 @@ enum APPLICATION_STATUS struct APPLICATION_PARAMETER { - LPCSTR pzName; - PVOID pValue; + LPCSTR pzName; + const void *pValue; }; class IAPPLICATION diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp index 25e8054318..451221255b 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp @@ -146,6 +146,8 @@ EnsureOutOfProcessInitializtion() FINISHED(S_OK); } + g_fOutOfProcessInitialize = TRUE; + g_hWinHttpModule = GetModuleHandle(TEXT("winhttp.dll")); g_hAspNetCoreModule = GetModuleHandle(TEXT("aspnetcorev2.dll")); diff --git a/test/Common.FunctionalTests/ConfigurationChangeTests.cs b/test/Common.FunctionalTests/ConfigurationChangeTests.cs index 064df80451..d1e5f62ff1 100644 --- a/test/Common.FunctionalTests/ConfigurationChangeTests.cs +++ b/test/Common.FunctionalTests/ConfigurationChangeTests.cs @@ -1,12 +1,18 @@ // 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. +using System; +using System.IO; +using System.Linq; using System.Net; +using System.Net.Http; +using System.Net.Sockets; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.Extensions.Logging; using Xunit; namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests @@ -14,6 +20,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests [Collection(PublishedSitesCollection.Name)] public class ConfigurationChangeTests : IISFunctionalTestBase { + private static readonly TimeSpan RetryDelay = TimeSpan.FromMilliseconds(100); private readonly PublishedSitesFixture _fixture; public ConfigurationChangeTests(PublishedSitesFixture fixture) @@ -66,13 +73,54 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests await deploymentResult.AssertStarts(); deploymentResult.ModifyWebConfig(element => element - .GetOrAdd("system.webServer") + .Descendants("system.webServer") + .Single() .GetOrAdd("aspNetCore") .SetAttributeValue("hostingModel", "inprocess")); // 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); + + 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; + } + } } } } diff --git a/test/CommonLibTests/hostfxr_utility_tests.cpp b/test/CommonLibTests/hostfxr_utility_tests.cpp index 7997220726..232f7627b8 100644 --- a/test/CommonLibTests/hostfxr_utility_tests.cpp +++ b/test/CommonLibTests/hostfxr_utility_tests.cpp @@ -98,11 +98,11 @@ TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks) 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 { - 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();