diff --git a/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj b/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj index 3c64a0dee2..10633c1854 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj +++ b/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj @@ -229,6 +229,7 @@ + @@ -240,6 +241,7 @@ + Create Create diff --git a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp new file mode 100644 index 0000000000..d01d944c5c --- /dev/null +++ b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp @@ -0,0 +1,271 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +#include "HandlerResolver.h" +#include "exceptions.h" +#include "utility.h" +#include "SRWExclusiveLock.h" +#include "applicationinfo.h" +#include "EventLog.h" +#include "hostfxr_utility.h" +#include "GlobalVersionUtility.h" +#include "HandleWrapper.h" + +const PCWSTR HandlerResolver::s_pwzAspnetcoreInProcessRequestHandlerName = L"aspnetcorev2_inprocess.dll"; +const PCWSTR HandlerResolver::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L"aspnetcorev2_outofprocess.dll"; + +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) +{ + InitializeSRWLock(&m_requestHandlerLoadLock); +} + +HRESULT +HandlerResolver::LoadRequestHandlerAssembly(STRU& location, ASPNETCORE_SHIM_CONFIG * pConfiguration) +{ + HRESULT hr; + STACK_STRU(struFileName, MAX_PATH); + PCWSTR pstrHandlerDllName; + if (pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS) + { + pstrHandlerDllName = s_pwzAspnetcoreInProcessRequestHandlerName; + } + else + { + pstrHandlerDllName = s_pwzAspnetcoreOutOfProcessRequestHandlerName; + } + + // Try to see if RH is already loaded, use GetModuleHandleEx to increment ref count + if (!GetModuleHandleEx(0, pstrHandlerDllName, &m_hRequestHandlerDll)) + { + if (pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS) + { + std::unique_ptr options; + + RETURN_IF_FAILED(HOSTFXR_OPTIONS::Create( + NULL, + pConfiguration->QueryProcessPath()->QueryStr(), + pConfiguration->QueryApplicationPhysicalPath()->QueryStr(), + pConfiguration->QueryArguments()->QueryStr(), + g_hEventLog, + options)); + + RETURN_IF_FAILED(location.Copy(options->GetExeLocation())); + + if (FAILED_LOG(hr = FindNativeAssemblyFromHostfxr(options.get(), pstrHandlerDllName, &struFileName))) + { + UTILITY::LogEventF(g_hEventLog, + EVENTLOG_ERROR_TYPE, + ASPNETCORE_EVENT_INPROCESS_RH_MISSING, + ASPNETCORE_EVENT_INPROCESS_RH_MISSING_MSG, + struFileName.IsEmpty() ? s_pwzAspnetcoreInProcessRequestHandlerName : struFileName.QueryStr()); + + return hr; + } + } + else + { + if (FAILED_LOG(hr = FindNativeAssemblyFromGlobalLocation(pConfiguration, pstrHandlerDllName, &struFileName))) + { + UTILITY::LogEventF(g_hEventLog, + EVENTLOG_ERROR_TYPE, + ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING, + ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG, + struFileName.IsEmpty() ? s_pwzAspnetcoreOutOfProcessRequestHandlerName : struFileName.QueryStr()); + + return hr; + } + } + + LOG_INFOF("Loading request handler: %S", struFileName.QueryStr()); + + m_hRequestHandlerDll = LoadLibraryW(struFileName.QueryStr()); + RETURN_LAST_ERROR_IF_NULL(m_hRequestHandlerDll); + } + + m_pfnAspNetCoreCreateApplication = reinterpret_cast(GetProcAddress(m_hRequestHandlerDll, "CreateApplication")); + + RETURN_LAST_ERROR_IF_NULL(m_pfnAspNetCoreCreateApplication); + + return S_OK; +} + +HRESULT +HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, STRU& location, PFN_ASPNETCORE_CREATE_APPLICATION * pfnCreateApplication) +{ + ASPNETCORE_SHIM_CONFIG pConfiguration; + RETURN_IF_FAILED(pConfiguration.Populate(&m_pServer, &pApplication)); + + if (m_fAspnetcoreRHLoadResult == S_FALSE) + { + SRWExclusiveLock lock(m_requestHandlerLoadLock); + if (m_fAspnetcoreRHLoadResult == S_FALSE) + { + m_loadedApplicationHostingModel = pConfiguration.QueryHostingModel(); + m_loadedApplicationId = pApplication.GetApplicationId(); + LOG_IF_FAILED(m_fAspnetcoreRHLoadResult = LoadRequestHandlerAssembly(location, &pConfiguration)); + } + } + + // Mixed hosting models + if (m_loadedApplicationHostingModel != pConfiguration.QueryHostingModel()) + { + UTILITY::LogEventF(g_hEventLog, + EVENTLOG_ERROR_TYPE, + ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR, + ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG, + pApplication.GetApplicationId(), + pConfiguration.QueryHostingModel()); + + return E_FAIL; + } + // Multiple in-process apps + else if (m_loadedApplicationHostingModel == HOSTING_IN_PROCESS && m_loadedApplicationId != pApplication.GetApplicationId()) + { + UTILITY::LogEventF(g_hEventLog, + EVENTLOG_ERROR_TYPE, + ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP, + ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG, + pApplication.GetApplicationId()); + + return E_FAIL; + } + + *pfnCreateApplication = m_pfnAspNetCoreCreateApplication; + return m_fAspnetcoreRHLoadResult; +} + +HRESULT +HandlerResolver::FindNativeAssemblyFromGlobalLocation( + ASPNETCORE_SHIM_CONFIG * pConfiguration, + PCWSTR pstrHandlerDllName, + STRU* struFilename +) +{ + try + { + std::wstring modulePath = GlobalVersionUtility::GetModuleName(m_hModule); + + modulePath = GlobalVersionUtility::RemoveFileNameFromFolderPath(modulePath); + + std::wstring retval = GlobalVersionUtility::GetGlobalRequestHandlerPath(modulePath.c_str(), + pConfiguration->QueryHandlerVersion()->QueryStr(), + pstrHandlerDllName + ); + + RETURN_IF_FAILED(struFilename->Copy(retval.c_str())); + } + catch (...) + { + STRU struEvent; + if (SUCCEEDED(struEvent.Copy(ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG))) + { + UTILITY::LogEvent(g_hEventLog, + EVENTLOG_INFORMATION_TYPE, + ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING, + struEvent.QueryStr()); + } + + return OBSERVE_CAUGHT_EXCEPTION(); + } + + return S_OK; +} + +// +// Tries to find aspnetcorerh.dll from the application +// Calls into hostfxr.dll to find it. +// Will leave hostfxr.dll loaded as it will be used again to call hostfxr_main. +// +HRESULT +HandlerResolver::FindNativeAssemblyFromHostfxr( + HOSTFXR_OPTIONS* hostfxrOptions, + PCWSTR libraryName, + STRU* struFilename +) +{ + STRU struApplicationFullPath; + STRU struNativeSearchPaths; + STRU struNativeDllLocation; + INT intIndex = -1; + INT intPrevIndex = 0; + BOOL fFound = FALSE; + DWORD dwBufferSize = 1024 * 10; + DWORD dwRequiredBufferSize = 0; + + DBG_ASSERT(struFilename != NULL); + + RETURN_LAST_ERROR_IF_NULL(m_hHostFxrDll = LoadLibraryW(hostfxrOptions->GetHostFxrLocation())); + + auto pFnHostFxrSearchDirectories = reinterpret_cast(GetProcAddress(m_hHostFxrDll, "hostfxr_get_native_search_directories")); + + RETURN_LAST_ERROR_IF_NULL(pFnHostFxrSearchDirectories); + RETURN_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize)); + + while (TRUE) + { + auto intHostFxrExitCode = pFnHostFxrSearchDirectories( + hostfxrOptions->GetArgc(), + hostfxrOptions->GetArgv(), + struNativeSearchPaths.QueryStr(), + dwBufferSize, + &dwRequiredBufferSize + ); + + if (intHostFxrExitCode == 0) + { + break; + } + else if (dwRequiredBufferSize > dwBufferSize) + { + dwBufferSize = dwRequiredBufferSize + 1; // for null terminator + + RETURN_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize)); + } + else + { + // Log "Error finding native search directories from aspnetcore application. + return E_UNEXPECTED; + } + } + + RETURN_IF_FAILED(struNativeSearchPaths.SyncWithBuffer()); + + 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) + { + RETURN_IF_FAILED(struNativeDllLocation.Copy(&struNativeSearchPaths.QueryStr()[intPrevIndex], intIndex - intPrevIndex)); + + if (!struNativeDllLocation.EndsWith(L"\\")) + { + RETURN_IF_FAILED(struNativeDllLocation.Append(L"\\")); + } + + RETURN_IF_FAILED(struNativeDllLocation.Append(libraryName)); + + if (UTILITY::CheckIfFileExists(struNativeDllLocation.QueryStr())) + { + RETURN_IF_FAILED(struFilename->Copy(struNativeDllLocation)); + fFound = TRUE; + break; + } + + intPrevIndex = intIndex + 1; + } + + if (!fFound) + { + return HRESULT_FROM_WIN32(ERROR_DLL_NOT_FOUND); + } + + return S_OK; +} + diff --git a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h new file mode 100644 index 0000000000..ad257afef4 --- /dev/null +++ b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h @@ -0,0 +1,49 @@ +// 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 "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 + ); + +class HandlerResolver +{ +public: + HandlerResolver(HMODULE hModule, IHttpServer &pServer); + HRESULT GetApplicationFactory(IHttpApplication &pApplication, STRU& location, PFN_ASPNETCORE_CREATE_APPLICATION *pfnCreateApplication); + +private: + HRESULT LoadRequestHandlerAssembly(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); + + 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; +}; + diff --git a/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp b/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp index 3ac7246ca9..bad9c25e4b 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp @@ -17,18 +17,6 @@ #include "ServerErrorApplication.h" #include "AppOfflineApplication.h" -extern HINSTANCE g_hModule; - -const PCWSTR APPLICATION_INFO::s_pwzAspnetcoreInProcessRequestHandlerName = L"aspnetcorev2_inprocess.dll"; -const PCWSTR APPLICATION_INFO::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L"aspnetcorev2_outofprocess.dll"; - -SRWLOCK APPLICATION_INFO::s_requestHandlerLoadLock {}; -bool APPLICATION_INFO::s_fAspnetcoreRHAssemblyLoaded = false; -bool APPLICATION_INFO::s_fAspnetcoreRHLoadedError = false; -HMODULE APPLICATION_INFO::s_hAspnetCoreRH = nullptr; - -PFN_ASPNETCORE_CREATE_APPLICATION APPLICATION_INFO::s_pfnAspNetCoreCreateApplication = nullptr; - APPLICATION_INFO::~APPLICATION_INFO() { ShutDownApplication(); @@ -36,13 +24,13 @@ APPLICATION_INFO::~APPLICATION_INFO() HRESULT APPLICATION_INFO::Initialize( - _In_ IHttpApplication &pApplication + _In_ IHttpApplication &pApplication, + HandlerResolver * pHandlerResolver ) { - m_pConfiguration.reset(new ASPNETCORE_SHIM_CONFIG()); - RETURN_IF_FAILED(m_pConfiguration->Populate(&m_pServer, &pApplication)); + m_handlerResolver = pHandlerResolver; + RETURN_IF_FAILED(m_struConfigPath.Copy(pApplication.GetAppConfigPath())); RETURN_IF_FAILED(m_struInfoKey.Copy(pApplication.GetApplicationId())); - return S_OK; } @@ -84,20 +72,15 @@ APPLICATION_INFO::GetOrCreateApplication( else { STRU struExeLocation; - FINISHED_IF_FAILED(FindRequestHandlerAssembly(struExeLocation)); - - if (m_pfnAspNetCoreCreateApplication == NULL) - { - FINISHED(HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION)); - } - + PFN_ASPNETCORE_CREATE_APPLICATION pfnAspNetCoreCreateApplication; + FINISHED_IF_FAILED(m_handlerResolver->GetApplicationFactory(httpApplication, struExeLocation, &pfnAspNetCoreCreateApplication)); std::array parameters { {"InProcessExeLocation", struExeLocation.QueryStr()} }; LOG_INFO("Creating handler application"); IAPPLICATION * newApplication; - FINISHED_IF_FAILED(m_pfnAspNetCoreCreateApplication( + FINISHED_IF_FAILED(pfnAspNetCoreCreateApplication( &m_pServer, &httpApplication, parameters.data(), @@ -130,266 +113,6 @@ Finished: return hr; } -HRESULT -APPLICATION_INFO::FindRequestHandlerAssembly(STRU& location) -{ - HRESULT hr = S_OK; - PCWSTR pstrHandlerDllName; - STACK_STRU(struFileName, 256); - - if (s_fAspnetcoreRHLoadedError) - { - FINISHED(E_APPLICATION_ACTIVATION_EXEC_FAILURE); - } - else if (!s_fAspnetcoreRHAssemblyLoaded) - { - SRWExclusiveLock lock(s_requestHandlerLoadLock); - if (s_fAspnetcoreRHLoadedError) - { - FINISHED(E_APPLICATION_ACTIVATION_EXEC_FAILURE); - } - if (s_fAspnetcoreRHAssemblyLoaded) - { - FINISHED(S_OK); - } - - if (m_pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS) - { - pstrHandlerDllName = s_pwzAspnetcoreInProcessRequestHandlerName; - } - else - { - pstrHandlerDllName = s_pwzAspnetcoreOutOfProcessRequestHandlerName; - } - - // Try to see if RH is already loaded - s_hAspnetCoreRH = GetModuleHandle(pstrHandlerDllName); - - if (s_hAspnetCoreRH == NULL) - { - if (m_pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS) - { - std::unique_ptr options; - - FINISHED_IF_FAILED(HOSTFXR_OPTIONS::Create( - NULL, - m_pConfiguration->QueryProcessPath()->QueryStr(), - m_pConfiguration->QueryApplicationPhysicalPath()->QueryStr(), - m_pConfiguration->QueryArguments()->QueryStr(), - g_hEventLog, - options)); - - FINISHED_IF_FAILED(location.Copy(options->GetExeLocation())); - - if (FAILED_LOG(hr = FindNativeAssemblyFromHostfxr(options.get(), pstrHandlerDllName, &struFileName))) - { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, - ASPNETCORE_EVENT_INPROCESS_RH_MISSING, - ASPNETCORE_EVENT_INPROCESS_RH_MISSING_MSG, - struFileName.IsEmpty() ? s_pwzAspnetcoreInProcessRequestHandlerName : struFileName.QueryStr()); - - FINISHED(hr); - } - } - else - { - if (FAILED_LOG(hr = FindNativeAssemblyFromGlobalLocation(pstrHandlerDllName, &struFileName))) - { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, - ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING, - ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG, - struFileName.IsEmpty() ? s_pwzAspnetcoreOutOfProcessRequestHandlerName : struFileName.QueryStr()); - - FINISHED(hr); - } - } - - LOG_INFOF("Loading request handler: %S", struFileName.QueryStr()); - - s_hAspnetCoreRH = LoadLibraryW(struFileName.QueryStr()); - - if (s_hAspnetCoreRH == NULL) - { - FINISHED(HRESULT_FROM_WIN32(GetLastError())); - } - } - - s_pfnAspNetCoreCreateApplication = (PFN_ASPNETCORE_CREATE_APPLICATION) - GetProcAddress(s_hAspnetCoreRH, "CreateApplication"); - if (s_pfnAspNetCoreCreateApplication == NULL) - { - FINISHED(HRESULT_FROM_WIN32(GetLastError())); - } - - s_fAspnetcoreRHAssemblyLoaded = TRUE; - } - -Finished: - // - // Question: we remember the load failure so that we will not try again. - // User needs to check whether the fuction pointer is NULL - // - m_pfnAspNetCoreCreateApplication = s_pfnAspNetCoreCreateApplication; - - if (!s_fAspnetcoreRHLoadedError && FAILED(hr)) - { - s_fAspnetcoreRHLoadedError = TRUE; - } - return hr; -} - -HRESULT -APPLICATION_INFO::FindNativeAssemblyFromGlobalLocation( - PCWSTR pstrHandlerDllName, - STRU* struFilename -) -{ - HRESULT hr = S_OK; - - try - { - std::wstring modulePath = GlobalVersionUtility::GetModuleName(g_hModule); - - modulePath = GlobalVersionUtility::RemoveFileNameFromFolderPath(modulePath); - - std::wstring retval = GlobalVersionUtility::GetGlobalRequestHandlerPath(modulePath.c_str(), - m_pConfiguration->QueryHandlerVersion()->QueryStr(), - pstrHandlerDllName - ); - - RETURN_IF_FAILED(struFilename->Copy(retval.c_str())); - } - catch (std::exception& e) - { - STRU struEvent; - if (SUCCEEDED(struEvent.Copy(ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG)) - && SUCCEEDED(struEvent.AppendA(e.what()))) - { - UTILITY::LogEvent(g_hEventLog, - EVENTLOG_INFORMATION_TYPE, - ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING, - struEvent.QueryStr()); - } - - hr = E_FAIL; - } - catch (...) - { - hr = E_FAIL; - } - - return hr; -} - -// -// Tries to find aspnetcorerh.dll from the application -// Calls into hostfxr.dll to find it. -// Will leave hostfxr.dll loaded as it will be used again to call hostfxr_main. -// -HRESULT -APPLICATION_INFO::FindNativeAssemblyFromHostfxr( - HOSTFXR_OPTIONS* hostfxrOptions, - PCWSTR libraryName, - STRU* struFilename -) -{ - HRESULT hr = S_OK; - STRU struApplicationFullPath; - STRU struNativeSearchPaths; - STRU struNativeDllLocation; - HMODULE hmHostFxrDll = NULL; - INT intHostFxrExitCode = 0; - INT intIndex = -1; - INT intPrevIndex = 0; - BOOL fFound = FALSE; - DWORD dwBufferSize = 1024 * 10; - DWORD dwRequiredBufferSize = 0; - - DBG_ASSERT(struFilename != NULL); - - FINISHED_LAST_ERROR_IF_NULL(hmHostFxrDll = LoadLibraryW(hostfxrOptions->GetHostFxrLocation())); - - hostfxr_get_native_search_directories_fn pFnHostFxrSearchDirectories = (hostfxr_get_native_search_directories_fn) - GetProcAddress(hmHostFxrDll, "hostfxr_get_native_search_directories"); - - if (pFnHostFxrSearchDirectories == NULL) - { - // Host fxr version is incorrect (need a higher version). - // TODO log error - FINISHED(E_FAIL); - } - - FINISHED_IF_FAILED(hr = struNativeSearchPaths.Resize(dwBufferSize)); - - while (TRUE) - { - intHostFxrExitCode = pFnHostFxrSearchDirectories( - hostfxrOptions->GetArgc(), - hostfxrOptions->GetArgv(), - struNativeSearchPaths.QueryStr(), - dwBufferSize, - &dwRequiredBufferSize - ); - - if (intHostFxrExitCode == 0) - { - break; - } - else if (dwRequiredBufferSize > dwBufferSize) - { - dwBufferSize = dwRequiredBufferSize + 1; // for null terminator - - FINISHED_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize)); - } - else - { - // Log "Error finding native search directories from aspnetcore application. - FINISHED(E_FAIL); - } - } - - FINISHED_IF_FAILED(struNativeSearchPaths.SyncWithBuffer()); - - 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) - { - FINISHED_IF_FAILED(struNativeDllLocation.Copy(&struNativeSearchPaths.QueryStr()[intPrevIndex], intIndex - intPrevIndex)); - - if (!struNativeDllLocation.EndsWith(L"\\")) - { - FINISHED_IF_FAILED(struNativeDllLocation.Append(L"\\")); - } - - FINISHED_IF_FAILED(struNativeDllLocation.Append(libraryName)); - - if (UTILITY::CheckIfFileExists(struNativeDllLocation.QueryStr())) - { - FINISHED_IF_FAILED(struFilename->Copy(struNativeDllLocation)); - fFound = TRUE; - break; - } - - intPrevIndex = intIndex + 1; - } - - if (!fFound) - { - FINISHED(E_FAIL); - } - -Finished: - if (FAILED(hr) && hmHostFxrDll != NULL) - { - FreeLibrary(hmHostFxrDll); - } - return hr; -} - VOID APPLICATION_INFO::RecycleApplication() { diff --git a/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.h b/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.h index 989120a538..ddf3e23aaa 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.h +++ b/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.h @@ -9,31 +9,20 @@ #include "aspnetcore_shim_config.h" #include "iapplication.h" #include "SRWSharedLock.h" +#include "HandlerResolver.h" #define API_BUFFER_TOO_SMALL 0x80008098 -typedef -HRESULT -(WINAPI * PFN_ASPNETCORE_CREATE_APPLICATION)( - _In_ IHttpServer *pServer, - _In_ IHttpApplication *pHttpApplication, - _In_ APPLICATION_PARAMETER *pParameters, - _In_ DWORD nParameters, - _Out_ IAPPLICATION **pApplication - ); - extern BOOL g_fRecycleProcessCalled; class APPLICATION_INFO { public: - APPLICATION_INFO(_In_ IHttpServer &pServer) : + APPLICATION_INFO(IHttpServer &pServer) : m_pServer(pServer), m_cRefs(1), - m_fValid(FALSE), - m_pConfiguration(nullptr), - m_pfnAspNetCoreCreateApplication(NULL) + m_handlerResolver(nullptr) { InitializeSRWLock(&m_applicationLock); } @@ -44,19 +33,25 @@ public: return m_struInfoKey.QueryStr(); } + STRU* + QueryConfigPath() + { + return &m_struConfigPath; + } + virtual ~APPLICATION_INFO(); - - static + + static void StaticInitialize() { - InitializeSRWLock(&s_requestHandlerLoadLock); } HRESULT Initialize( - _In_ IHttpApplication &pApplication + IHttpApplication &pApplication, + HandlerResolver *pHandlerResolver ); VOID @@ -74,24 +69,6 @@ public: } } - BOOL - IsValid() - { - return m_fValid; - } - - VOID - MarkValid() - { - m_fValid = TRUE; - } - - ASPNETCORE_SHIM_CONFIG* - QueryConfig() - { - return m_pConfiguration.get(); - } - VOID RecycleApplication(); @@ -105,31 +82,17 @@ public: ); private: - HRESULT FindRequestHandlerAssembly(STRU& location); - HRESULT FindNativeAssemblyFromGlobalLocation(PCWSTR libraryName, STRU* location); - HRESULT FindNativeAssemblyFromHostfxr(HOSTFXR_OPTIONS* hostfxrOptions, PCWSTR libraryName, STRU* location); static DWORD WINAPI DoRecycleApplication(LPVOID lpParam); mutable LONG m_cRefs; + STRU m_struConfigPath; STRU m_struInfoKey; - BOOL m_fValid; SRWLOCK m_applicationLock; IHttpServer &m_pServer; - PFN_ASPNETCORE_CREATE_APPLICATION m_pfnAspNetCoreCreateApplication; - - std::unique_ptr m_pConfiguration; + HandlerResolver *m_handlerResolver; + std::unique_ptr m_pApplication; - - - static const PCWSTR s_pwzAspnetcoreInProcessRequestHandlerName; - static const PCWSTR s_pwzAspnetcoreOutOfProcessRequestHandlerName; - - static SRWLOCK s_requestHandlerLoadLock; - static bool s_fAspnetcoreRHAssemblyLoaded; - static bool s_fAspnetcoreRHLoadedError; - static HMODULE s_hAspnetCoreRH; - static PFN_ASPNETCORE_CREATE_APPLICATION s_pfnAspNetCoreCreateApplication; }; class APPLICATION_INFO_HASH : diff --git a/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp b/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp index 20030594fb..1c5862a339 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp @@ -27,10 +27,7 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo( { HRESULT hr = S_OK; APPLICATION_INFO *pApplicationInfo = NULL; - BOOL fMixedHostingModelError = FALSE; - BOOL fDuplicatedInProcessApp = FALSE; PCWSTR pszApplicationId = NULL; - APP_HOSTING_MODEL hostingModel = HOSTING_UNKNOWN; STACK_STRU ( strEventMsg, 256 ); @@ -80,34 +77,7 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo( pApplicationInfo = new APPLICATION_INFO(m_pHttpServer); - FINISHED_IF_FAILED(pApplicationInfo->Initialize(pApplication)); - - hostingModel = pApplicationInfo->QueryConfig()->QueryHostingModel(); - - if (m_pApplicationInfoHash->Count() == 0) - { - m_hostingModel = hostingModel; - pApplicationInfo->MarkValid(); - } - else - { - if (hostingModel == HOSTING_OUT_PROCESS && hostingModel == m_hostingModel) - { - pApplicationInfo->MarkValid(); - } - else - { - if (hostingModel != m_hostingModel) - { - fMixedHostingModelError = TRUE; - } - else - { - fDuplicatedInProcessApp = TRUE; - } - } - } - + FINISHED_IF_FAILED(pApplicationInfo->Initialize(pApplication, &m_handlerResolver)); FINISHED_IF_FAILED(m_pApplicationInfoHash->InsertRecord(pApplicationInfo)); *ppApplicationInfo = pApplicationInfo; @@ -115,26 +85,6 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo( } Finished: - // log the error - if (fDuplicatedInProcessApp) - { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, - ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP, - ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG, - pszApplicationId); - - } - else if (fMixedHostingModelError) - { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, - ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR, - ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG, - pszApplicationId, - hostingModel); - } - if (pApplicationInfo != NULL) { pApplicationInfo->DereferenceApplicationInfo(); @@ -160,7 +110,7 @@ APPLICATION_MANAGER::FindConfigChangedApplication( // Config Change context contains the original config path that changed // and a multiStr containing CONFIG_CHANGE_CONTEXT* pContext = static_cast(pvContext); - STRU* pstruConfigPath = pEntry->QueryConfig()->QueryConfigPath(); + STRU* pstruConfigPath = pEntry->QueryConfigPath(); // check if the application path contains our app/subapp by seeing if the config path // starts with the notification path. diff --git a/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.h b/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.h index f01df15844..165f3c9d3e 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.h +++ b/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.h @@ -85,10 +85,10 @@ public: } } - static HRESULT StaticInitialize(IHttpServer& pHttpServer) + static HRESULT StaticInitialize(HMODULE hModule, IHttpServer& pHttpServer) { assert(!sm_pApplicationManager); - sm_pApplicationManager = new APPLICATION_MANAGER(pHttpServer); + sm_pApplicationManager = new APPLICATION_MANAGER(hModule, pHttpServer); RETURN_IF_FAILED(sm_pApplicationManager->Initialize()); APPLICATION_INFO::StaticInitialize(); @@ -110,19 +110,21 @@ public: } private: - APPLICATION_MANAGER(IHttpServer& pHttpServer) : + APPLICATION_MANAGER(HMODULE hModule, IHttpServer& pHttpServer) : m_pApplicationInfoHash(NULL), m_hostingModel(HOSTING_UNKNOWN), m_fDebugInitialize(FALSE), - m_pHttpServer(pHttpServer) + m_pHttpServer(pHttpServer), + m_handlerResolver(hModule, pHttpServer) { InitializeSRWLock(&m_srwLock); } APPLICATION_INFO_HASH *m_pApplicationInfoHash; static APPLICATION_MANAGER *sm_pApplicationManager; - SRWLOCK m_srwLock; + SRWLOCK m_srwLock {}; APP_HOSTING_MODEL m_hostingModel; BOOL m_fDebugInitialize; IHttpServer &m_pHttpServer; + HandlerResolver m_handlerResolver; }; diff --git a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp index 4cb6b222cf..6785bac95a 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp @@ -20,13 +20,14 @@ ASPNETCORE_SHIM_CONFIG::Populate( CComPtr pAspNetCoreElement; pAdminManager = pHttpServer->GetAdminManager(); - RETURN_IF_FAILED(m_struConfigPath.Copy(pHttpApplication->GetAppConfigPath())); RETURN_IF_FAILED(m_struApplicationPhysicalPath.Copy(pHttpApplication->GetApplicationPhysicalPath())); const CComBSTR bstrAspNetCoreSection = CS_ASPNETCORE_SECTION; + const CComBSTR applicationConfigPath = pHttpApplication->GetAppConfigPath(); + RETURN_IF_FAILED(pAdminManager->GetAdminSection(bstrAspNetCoreSection, - m_struConfigPath.QueryStr(), + applicationConfigPath, &pAspNetCoreElement)); RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement, diff --git a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h index 5229f95b79..a8f96db425 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h +++ b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h @@ -38,12 +38,6 @@ public: return &m_struApplicationPhysicalPath; } - STRU* - QueryConfigPath() - { - return &m_struConfigPath; - } - STRU* QueryProcessPath() { @@ -78,7 +72,6 @@ private: STRU m_struArguments; STRU m_struProcessPath; STRU m_struApplicationPhysicalPath; - STRU m_struConfigPath; APP_HOSTING_MODEL m_hostingModel; STRU m_struHostFxrLocation; STRU m_struHandlerVersion; diff --git a/src/AspNetCoreModuleV2/AspNetCore/dllmain.cpp b/src/AspNetCoreModuleV2/AspNetCore/dllmain.cpp index 0526d1ccab..10f178ae70 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/dllmain.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/dllmain.cpp @@ -16,6 +16,7 @@ DECLARE_DEBUG_PRINT_OBJECT("aspnetcorev2.dll"); HANDLE g_hEventLog = NULL; BOOL g_fRecycleProcessCalled = FALSE; BOOL g_fInShutdown = FALSE; +HINSTANCE g_hServerModule; VOID StaticCleanup() @@ -40,6 +41,7 @@ BOOL WINAPI DllMain(HMODULE hModule, switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: + g_hServerModule = hModule; DisableThreadLibraryCalls(hModule); DebugInitialize(hModule); break; @@ -157,7 +159,7 @@ HRESULT pFactory = NULL; - FINISHED_IF_FAILED(APPLICATION_MANAGER::StaticInitialize(*pHttpServer)); + FINISHED_IF_FAILED(APPLICATION_MANAGER::StaticInitialize(g_hServerModule, *pHttpServer)); pGlobalModule = NULL; diff --git a/src/AspNetCoreModuleV2/AspNetCore/proxymodule.cpp b/src/AspNetCoreModuleV2/AspNetCore/proxymodule.cpp index 1f0ed72db1..2d9fe9150c 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/proxymodule.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/proxymodule.cpp @@ -89,13 +89,6 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler( pHttpContext, &m_pApplicationInfo)); - if (!m_pApplicationInfo->IsValid()) - { - // Application cannot be started due to wrong hosting mode - // the error should already been logged to window event log for the first request - FINISHED(E_APPLICATION_ACTIVATION_EXEC_FAILURE); - } - DBG_ASSERT(pHttpContext); std::unique_ptr pApplication; diff --git a/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h b/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h index 67f980dfd4..6764f4c14d 100644 --- a/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h +++ b/src/AspNetCoreModuleV2/CommonLib/HandleWrapper.h @@ -19,6 +19,13 @@ struct NullHandleTraits static void Close(HANDLE handle) { CloseHandle(handle); } }; +struct ModuleHandleTraits +{ + using HandleType = HMODULE; + static constexpr HMODULE DefaultHandle = NULL; + static void Close(HMODULE handle) { FreeModule(handle); } +}; + template class HandleWrapper { diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/XElementExtensions.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/XElementExtensions.cs index 45ed897019..35d1b013cd 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/XElementExtensions.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/XElementExtensions.cs @@ -7,7 +7,7 @@ using System.Xml.Linq; namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS { - internal static class XElementExtensions + public static class XElementExtensions { public static XElement RequiredElement(this XElement element, string name) { diff --git a/test/Common.FunctionalTests/MultiApplicationTests.cs b/test/Common.FunctionalTests/MultiApplicationTests.cs new file mode 100644 index 0000000000..df9bb035f9 --- /dev/null +++ b/test/Common.FunctionalTests/MultiApplicationTests.cs @@ -0,0 +1,141 @@ +// 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.Threading.Tasks; +using System.Xml.Linq; +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 +{ + [Collection(PublishedSitesCollection.Name)] + public class MultiApplicationTests : IISFunctionalTestBase + { + private readonly PublishedSitesFixture _fixture; + + private PublishedApplication _publishedApplication; + private PublishedApplication _rootApplication; + + public MultiApplicationTests(PublishedSitesFixture fixture) + { + _fixture = fixture; + } + + [ConditionalFact] + public async Task RunsTwoOutOfProcessApps() + { + var parameters = _fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess, publish: true); + parameters.ServerConfigActionList.Add(DuplicateApplication); + var result = await DeployAsync(parameters); + var id1 = await result.HttpClient.GetStringAsync("/app1/ProcessId"); + var id2 = await result.HttpClient.GetStringAsync("/app2/ProcessId"); + Assert.NotEqual(id2, id1); + } + + [ConditionalFact] + public async Task FailsAndLogsWhenRunningTwoInProcessApps() + { + var parameters = _fixture.GetBaseDeploymentParameters(HostingModel.InProcess, publish: true); + parameters.ServerConfigActionList.Add(DuplicateApplication); + var result = await DeployAsync(parameters); + var result1 = await result.HttpClient.GetAsync("/app1/HelloWorld"); + var result2 = await result.HttpClient.GetAsync("/app2/HelloWorld"); + Assert.Equal(200, (int)result1.StatusCode); + Assert.Equal(500, (int)result2.StatusCode); + StopServer(); + EventLogHelpers.VerifyEventLogEvent(TestSink, "Only one inprocess application is allowed per IIS application pool"); + } + + [ConditionalTheory] + [InlineData(HostingModel.OutOfProcess)] + [InlineData(HostingModel.InProcess)] + public async Task FailsAndLogsEventLogForMixedHostingModel(HostingModel firstApp) + { + var parameters = _fixture.GetBaseDeploymentParameters(firstApp, publish: true); + parameters.ServerConfigActionList.Add(DuplicateApplication); + var result = await DeployAsync(parameters); + + // Modify hosting model of other app to be the opposite + var otherApp = firstApp == HostingModel.InProcess ? HostingModel.OutOfProcess : HostingModel.InProcess; + SetHostingModel(_publishedApplication.Path, otherApp); + + var result1 = await result.HttpClient.GetAsync("/app1/HelloWorld"); + var result2 = await result.HttpClient.GetAsync("/app2/HelloWorld"); + Assert.Equal(200, (int)result1.StatusCode); + Assert.Equal(500, (int)result2.StatusCode); + StopServer(); + EventLogHelpers.VerifyEventLogEvent(TestSink, "Mixed hosting model is not supported."); + } + + private void SetHostingModel(string directory, HostingModel model) + { + var webConfigLocation = GetWebConfigLocation(directory); + XDocument webConfig = XDocument.Load(webConfigLocation); + webConfig.Root + .GetOrAdd("system.webServer") + .GetOrAdd("aspNetCore") + .SetAttributeValue("hostingModel", model.ToString()); + webConfig.Save(webConfigLocation); + } + + private void DuplicateApplication(XElement config, string contentRoot) + { + var siteElement = config + .RequiredElement("system.applicationHost") + .RequiredElement("sites") + .RequiredElement("site"); + + var application = siteElement + .RequiredElement("application"); + + application.SetAttributeValue("path", "/app1"); + + var source = new DirectoryInfo(contentRoot); + + var destination = new DirectoryInfo(contentRoot + "anotherApp"); + destination.Create(); + Helpers.CopyFiles(source, destination, Logger); + + _publishedApplication = new PublishedApplication(destination.FullName, Logger); + + var newApplication = new XElement(application); + newApplication.SetAttributeValue("path", "/app2"); + newApplication.RequiredElement("virtualDirectory") + .SetAttributeValue("physicalPath", destination.FullName); + + siteElement.Add(newApplication); + + // IIS Express requires root application to exist + var rootApplicationDirectory = new DirectoryInfo(contentRoot + "rootApp"); + rootApplicationDirectory.Create(); + + _rootApplication = new PublishedApplication(rootApplicationDirectory.FullName, Logger); + File.WriteAllText(GetWebConfigLocation(rootApplicationDirectory.FullName), ""); + + var rootApplication = new XElement(application); + rootApplication.SetAttributeValue("path", "/"); + rootApplication.RequiredElement("virtualDirectory") + .SetAttributeValue("physicalPath", rootApplicationDirectory.FullName); + + siteElement.Add(rootApplication); + } + + private static string GetWebConfigLocation(string siteRoot) + { + return Path.Combine(siteRoot, "web.config"); + } + + public override void Dispose() + { + base.Dispose(); + _rootApplication.Dispose(); + _publishedApplication.Dispose(); + } + } +} diff --git a/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs b/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs index 1d53ba6843..7729b4359b 100644 --- a/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs +++ b/test/Common.FunctionalTests/Utilities/EventLogHelpers.cs @@ -12,7 +12,16 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public static void VerifyEventLogEvent(ITestSink testSink, string expectedRegexMatchString) { var eventLogRegex = new Regex($"Event Log: {expectedRegexMatchString}"); - Assert.Contains(testSink.Writes, context => eventLogRegex.IsMatch(context.Message)); + + int count = 0; + foreach (var context in testSink.Writes) + { + if (eventLogRegex.IsMatch(context.Message)) + { + count++; + } + } + Assert.Equal(1, count); } } } diff --git a/test/Common.FunctionalTests/Utilities/Helpers.cs b/test/Common.FunctionalTests/Utilities/Helpers.cs index 73d48fd6ca..2781da1944 100644 --- a/test/Common.FunctionalTests/Utilities/Helpers.cs +++ b/test/Common.FunctionalTests/Utilities/Helpers.cs @@ -11,6 +11,7 @@ using System.Xml.Linq; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Testing; +using Microsoft.Extensions.Logging; using Xunit; namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests @@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public static string GetInProcessTestSitesPath() => GetTestWebSitePath("InProcessWebSite"); public static string GetOutOfProcessTestSitesPath() => GetTestWebSitePath("OutOfProcessWebSite"); - + public static async Task AssertStarts(IISDeploymentResult deploymentResult, string path = "/HelloWorld") { @@ -57,5 +58,20 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests await Task.WhenAll(tasks); } + + public static void CopyFiles(DirectoryInfo source, DirectoryInfo target, ILogger logger) + { + foreach (DirectoryInfo directoryInfo in source.GetDirectories()) + { + CopyFiles(directoryInfo, target.CreateSubdirectory(directoryInfo.Name), logger); + } + logger.LogDebug($"Processing {target.FullName}"); + foreach (FileInfo fileInfo in source.GetFiles()) + { + logger.LogDebug($" Copying {fileInfo.Name}"); + var destFileName = Path.Combine(target.FullName, fileInfo.Name); + fileInfo.CopyTo(destFileName); + } + } } } diff --git a/test/WebSites/OutOfProcessWebSite/Startup.cs b/test/WebSites/OutOfProcessWebSite/Startup.cs index be44480891..059b5a1264 100644 --- a/test/WebSites/OutOfProcessWebSite/Startup.cs +++ b/test/WebSites/OutOfProcessWebSite/Startup.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -109,10 +110,15 @@ namespace TestSites private async Task HostingEnvironment(HttpContext context) { var hostingEnv = context.RequestServices.GetService(); - + await context.Response.WriteAsync("ContentRootPath "+hostingEnv.ContentRootPath + Environment.NewLine); await context.Response.WriteAsync("WebRootPath "+hostingEnv.WebRootPath + Environment.NewLine); await context.Response.WriteAsync("CurrentDirectory "+Environment.CurrentDirectory); } + + private async Task ProcessId(HttpContext context) + { + await context.Response.WriteAsync(Process.GetCurrentProcess().Id.ToString()); + } } }