diff --git a/build/repo.props b/build/repo.props index 1323b23e8d..712a3fba0e 100644 --- a/build/repo.props +++ b/build/repo.props @@ -4,6 +4,7 @@ Microsoft $(BuildDir)AspNetCoreModule.zip + $(BuildDir)StressTestWebSite.zip diff --git a/build/repo.targets b/build/repo.targets index 4415123011..442b4b4db0 100644 --- a/build/repo.targets +++ b/build/repo.targets @@ -4,7 +4,7 @@ $(PrepareDependsOn) $(GetArtifactInfoDependsOn);GetNativeArtifactsInfo BuildNativeAssets;$(CompileDependsOn) - $(PackageDependsOn);PackageNativeProjects + $(PackageDependsOn);PackageNativeProjects;PackageStressTestApp $(TestDependsOn);RunNativeTest $(RepositoryRoot)NuGetPackageVerifier.xplat.json $(RepositoryRoot)src\ @@ -14,7 +14,7 @@ - + @@ -110,4 +110,26 @@ + + + + $(MSBuildThisFileDirectory)..\test\WebSites\StressTestWebSite\ + $(StressAppBasePath)bin\published\ + + + + + + + + + + + diff --git a/src/AspNetCoreModuleV2/CommonLib/AppOfflineApplication.cpp b/src/AspNetCoreModuleV2/AspNetCore/AppOfflineApplication.cpp similarity index 94% rename from src/AspNetCoreModuleV2/CommonLib/AppOfflineApplication.cpp rename to src/AspNetCoreModuleV2/AspNetCore/AppOfflineApplication.cpp index e42c6ca8e6..49b2b07993 100644 --- a/src/AspNetCoreModuleV2/CommonLib/AppOfflineApplication.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/AppOfflineApplication.cpp @@ -3,9 +3,9 @@ #include "AppOfflineApplication.h" -#include #include "HandleWrapper.h" #include "AppOfflineHandler.h" +#include "exceptions.h" HRESULT AppOfflineApplication::CreateHandler(IHttpContext* pHttpContext, IREQUEST_HANDLER** pRequestHandler) { @@ -57,5 +57,5 @@ HRESULT AppOfflineApplication::OnAppOfflineFound() bool AppOfflineApplication::ShouldBeStarted(IHttpApplication& pApplication) { - return is_regular_file(GetAppOfflineLocation(pApplication)); + return FileExists(GetAppOfflineLocation(pApplication)); } diff --git a/src/AspNetCoreModuleV2/CommonLib/AppOfflineApplication.h b/src/AspNetCoreModuleV2/AspNetCore/AppOfflineApplication.h similarity index 100% rename from src/AspNetCoreModuleV2/CommonLib/AppOfflineApplication.h rename to src/AspNetCoreModuleV2/AspNetCore/AppOfflineApplication.h diff --git a/src/AspNetCoreModuleV2/CommonLib/AppOfflineHandler.cpp b/src/AspNetCoreModuleV2/AspNetCore/AppOfflineHandler.cpp similarity index 100% rename from src/AspNetCoreModuleV2/CommonLib/AppOfflineHandler.cpp rename to src/AspNetCoreModuleV2/AspNetCore/AppOfflineHandler.cpp diff --git a/src/AspNetCoreModuleV2/CommonLib/AppOfflineHandler.h b/src/AspNetCoreModuleV2/AspNetCore/AppOfflineHandler.h similarity index 96% rename from src/AspNetCoreModuleV2/CommonLib/AppOfflineHandler.h rename to src/AspNetCoreModuleV2/AspNetCore/AppOfflineHandler.h index e9fd4b99da..784a470915 100644 --- a/src/AspNetCoreModuleV2/CommonLib/AppOfflineHandler.h +++ b/src/AspNetCoreModuleV2/AspNetCore/AppOfflineHandler.h @@ -3,7 +3,7 @@ #pragma once -#include "application.h" +#include #include "requesthandler.h" class AppOfflineHandler: public REQUEST_HANDLER diff --git a/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj b/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj index 10633c1854..ef86d5ed6b 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj +++ b/src/AspNetCoreModuleV2/AspNetCore/AspNetCore.vcxproj @@ -226,10 +226,13 @@ + + + @@ -238,10 +241,13 @@ + + + Create Create diff --git a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp index d01d944c5c..1f5ede9f0e 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.cpp @@ -3,13 +3,13 @@ #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" +#include "file_utility.h" const PCWSTR HandlerResolver::s_pwzAspnetcoreInProcessRequestHandlerName = L"aspnetcorev2_inprocess.dll"; const PCWSTR HandlerResolver::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L"aspnetcorev2_outofprocess.dll"; @@ -26,7 +26,7 @@ HandlerResolver::HandlerResolver(HMODULE hModule, IHttpServer &pServer) } HRESULT -HandlerResolver::LoadRequestHandlerAssembly(STRU& location, ASPNETCORE_SHIM_CONFIG * pConfiguration) +HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, STRU& location, ASPNETCORE_SHIM_CONFIG * pConfiguration) { HRESULT hr; STACK_STRU(struFileName, MAX_PATH); @@ -50,20 +50,18 @@ HandlerResolver::LoadRequestHandlerAssembly(STRU& location, ASPNETCORE_SHIM_CONF RETURN_IF_FAILED(HOSTFXR_OPTIONS::Create( NULL, pConfiguration->QueryProcessPath()->QueryStr(), - pConfiguration->QueryApplicationPhysicalPath()->QueryStr(), + pApplication.GetApplicationPhysicalPath(), 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()); + EventLog::Error( + ASPNETCORE_EVENT_INPROCESS_RH_MISSING, + ASPNETCORE_EVENT_INPROCESS_RH_MISSING_MSG, + struFileName.IsEmpty() ? s_pwzAspnetcoreInProcessRequestHandlerName : struFileName.QueryStr()); return hr; } @@ -72,8 +70,7 @@ HandlerResolver::LoadRequestHandlerAssembly(STRU& location, ASPNETCORE_SHIM_CONF { if (FAILED_LOG(hr = FindNativeAssemblyFromGlobalLocation(pConfiguration, pstrHandlerDllName, &struFileName))) { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, + EventLog::Error( ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING, ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG, struFileName.IsEmpty() ? s_pwzAspnetcoreOutOfProcessRequestHandlerName : struFileName.QueryStr()); @@ -108,15 +105,14 @@ HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, STRU& loc { m_loadedApplicationHostingModel = pConfiguration.QueryHostingModel(); m_loadedApplicationId = pApplication.GetApplicationId(); - LOG_IF_FAILED(m_fAspnetcoreRHLoadResult = LoadRequestHandlerAssembly(location, &pConfiguration)); + LOG_IF_FAILED(m_fAspnetcoreRHLoadResult = LoadRequestHandlerAssembly(pApplication, location, &pConfiguration)); } } // Mixed hosting models if (m_loadedApplicationHostingModel != pConfiguration.QueryHostingModel()) { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, + EventLog::Error( ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR, ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG, pApplication.GetApplicationId(), @@ -127,8 +123,7 @@ HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, STRU& loc // Multiple in-process apps else if (m_loadedApplicationHostingModel == HOSTING_IN_PROCESS && m_loadedApplicationId != pApplication.GetApplicationId()) { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, + EventLog::Error( ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP, ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG, pApplication.GetApplicationId()); @@ -165,8 +160,7 @@ HandlerResolver::FindNativeAssemblyFromGlobalLocation( STRU struEvent; if (SUCCEEDED(struEvent.Copy(ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG))) { - UTILITY::LogEvent(g_hEventLog, - EVENTLOG_INFORMATION_TYPE, + EventLog::Info( ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING, struEvent.QueryStr()); } @@ -251,7 +245,7 @@ HandlerResolver::FindNativeAssemblyFromHostfxr( RETURN_IF_FAILED(struNativeDllLocation.Append(libraryName)); - if (UTILITY::CheckIfFileExists(struNativeDllLocation.QueryStr())) + if (FILE_UTILITY::CheckIfFileExists(struNativeDllLocation.QueryStr())) { RETURN_IF_FAILED(struFilename->Copy(struNativeDllLocation)); fFound = TRUE; diff --git a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h index ad257afef4..847d664956 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h +++ b/src/AspNetCoreModuleV2/AspNetCore/HandlerResolver.h @@ -26,7 +26,7 @@ public: HRESULT GetApplicationFactory(IHttpApplication &pApplication, STRU& location, PFN_ASPNETCORE_CREATE_APPLICATION *pfnCreateApplication); private: - HRESULT LoadRequestHandlerAssembly(STRU& location, ASPNETCORE_SHIM_CONFIG * pConfiguration); + 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); diff --git a/src/AspNetCoreModuleV2/CommonLib/PollingAppOfflineApplication.cpp b/src/AspNetCoreModuleV2/AspNetCore/PollingAppOfflineApplication.cpp similarity index 75% rename from src/AspNetCoreModuleV2/CommonLib/PollingAppOfflineApplication.cpp rename to src/AspNetCoreModuleV2/AspNetCore/PollingAppOfflineApplication.cpp index 14629ebe10..1270319e8b 100644 --- a/src/AspNetCoreModuleV2/CommonLib/PollingAppOfflineApplication.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/PollingAppOfflineApplication.cpp @@ -6,6 +6,7 @@ #include #include "SRWExclusiveLock.h" #include "HandleWrapper.h" +#include "exceptions.h" APPLICATION_STATUS PollingAppOfflineApplication::QueryStatus() { @@ -27,20 +28,12 @@ PollingAppOfflineApplication::CheckAppOffline() SRWExclusiveLock lock(m_statusLock); if (ulCurrentTime - m_ulLastCheckTime > c_appOfflineRefreshIntervalMS) { - try + m_fAppOfflineFound = FileExists(m_appOfflineLocation); + if(m_fAppOfflineFound) { - m_fAppOfflineFound = is_regular_file(m_appOfflineLocation); - if(m_fAppOfflineFound) - { - LOG_IF_FAILED(OnAppOfflineFound()); - } - m_ulLastCheckTime = ulCurrentTime; - } - catch (...) - { - // is_regular_file might throw in very rare cases - OBSERVE_CAUGHT_EXCEPTION(); + LOG_IF_FAILED(OnAppOfflineFound()); } + m_ulLastCheckTime = ulCurrentTime; } } @@ -55,3 +48,9 @@ std::filesystem::path PollingAppOfflineApplication::GetAppOfflineLocation(IHttpA { return std::filesystem::path(pApplication.GetApplicationPhysicalPath()) / "app_offline.htm"; } + +bool PollingAppOfflineApplication::FileExists(const std::filesystem::path& path) +{ + std::error_code ec; + return is_regular_file(path, ec) || ec.value() == ERROR_SHARING_VIOLATION; +} diff --git a/src/AspNetCoreModuleV2/CommonLib/PollingAppOfflineApplication.h b/src/AspNetCoreModuleV2/AspNetCore/PollingAppOfflineApplication.h similarity index 95% rename from src/AspNetCoreModuleV2/CommonLib/PollingAppOfflineApplication.h rename to src/AspNetCoreModuleV2/AspNetCore/PollingAppOfflineApplication.h index 95097bb3ac..e7ec367c17 100644 --- a/src/AspNetCoreModuleV2/CommonLib/PollingAppOfflineApplication.h +++ b/src/AspNetCoreModuleV2/AspNetCore/PollingAppOfflineApplication.h @@ -32,7 +32,7 @@ public: protected: std::filesystem::path m_appOfflineLocation; static std::filesystem::path GetAppOfflineLocation(IHttpApplication& pApplication); - + static bool FileExists(const std::filesystem::path& path); private: static const int c_appOfflineRefreshIntervalMS = 200; std::string m_strAppOfflineContent; diff --git a/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp b/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp index bad9c25e4b..2a00d3c82c 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/applicationinfo.cpp @@ -6,11 +6,9 @@ #include #include "proxymodule.h" #include "hostfxr_utility.h" -#include "utility.h" #include "debugutil.h" #include "resources.h" #include "SRWExclusiveLock.h" -#include "GlobalVersionUtility.h" #include "exceptions.h" #include "EventLog.h" #include "HandleWrapper.h" @@ -95,8 +93,7 @@ Finished: if (FAILED(hr)) { // Log the failure and update application info to not try again - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, + EventLog::Error( ASPNETCORE_EVENT_ADD_APPLICATION_ERROR, ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG, httpApplication.GetApplicationId(), diff --git a/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp b/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp index 1c5862a339..8e62d9ef68 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp @@ -4,7 +4,6 @@ #include "applicationmanager.h" #include "proxymodule.h" -#include "utility.h" #include "resources.h" #include "SRWExclusiveLock.h" #include "exceptions.h" @@ -217,8 +216,7 @@ APPLICATION_MANAGER::RecycleApplicationFromManager( ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG, path))) { - UTILITY::LogEvent(g_hEventLog, - EVENTLOG_INFORMATION_TYPE, + EventLog::Info( ASPNETCORE_EVENT_RECYCLE_CONFIGURATION, strEventMsg.QueryStr()); } @@ -248,8 +246,7 @@ Finished: ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG, pszApplicationId))) { - UTILITY::LogEvent(g_hEventLog, - EVENTLOG_ERROR_TYPE, + EventLog::Error( ASPNETCORE_EVENT_RECYCLE_APP_FAILURE, strEventMsg.QueryStr()); } diff --git a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp index 6785bac95a..10adc6e2b4 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.cpp @@ -5,7 +5,6 @@ #include "EventLog.h" #include "config_utility.h" -#include "hostfxr_utility.h" #include "ahutil.h" HRESULT @@ -16,14 +15,10 @@ ASPNETCORE_SHIM_CONFIG::Populate( { STACK_STRU(strHostingModel, 12); STRU strApplicationFullPath; - IAppHostAdminManager *pAdminManager = NULL; CComPtr pAspNetCoreElement; - pAdminManager = pHttpServer->GetAdminManager(); - RETURN_IF_FAILED(m_struApplicationPhysicalPath.Copy(pHttpApplication->GetApplicationPhysicalPath())); - + IAppHostAdminManager *pAdminManager = pHttpServer->GetAdminManager(); const CComBSTR bstrAspNetCoreSection = CS_ASPNETCORE_SECTION; - const CComBSTR applicationConfigPath = pHttpApplication->GetAppConfigPath(); RETURN_IF_FAILED(pAdminManager->GetAdminSection(bstrAspNetCoreSection, @@ -51,7 +46,10 @@ ASPNETCORE_SHIM_CONFIG::Populate( else { // block unknown hosting value - EVENTLOG(g_hEventLog, UNKNOWN_HOSTING_MODEL_ERROR, strHostingModel.QueryStr()); + EventLog::Error( + ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR, + ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR_MSG, + strHostingModel.QueryStr()); RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED)); } diff --git a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h index a8f96db425..1ae97f79af 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h +++ b/src/AspNetCoreModuleV2/AspNetCore/aspnetcore_shim_config.h @@ -32,12 +32,6 @@ public: IHttpApplication *pHttpApplication ); - STRU* - QueryApplicationPhysicalPath() - { - return &m_struApplicationPhysicalPath; - } - STRU* QueryProcessPath() { @@ -71,9 +65,7 @@ private: STRU m_struArguments; STRU m_struProcessPath; - STRU m_struApplicationPhysicalPath; 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 10f178ae70..b20ca03b97 100644 --- a/src/AspNetCoreModuleV2/AspNetCore/dllmain.cpp +++ b/src/AspNetCoreModuleV2/AspNetCore/dllmain.cpp @@ -6,10 +6,10 @@ #include "proxymodule.h" #include "globalmodule.h" #include "acache.h" -#include "utility.h" #include "debugutil.h" #include "resources.h" #include "exceptions.h" +#include "EventLog.h" DECLARE_DEBUG_PRINT_OBJECT("aspnetcorev2.dll"); @@ -131,15 +131,9 @@ HRESULT if (fDisableANCM) { // Logging - STACK_STRU(strEventMsg, 256); - if (SUCCEEDED(strEventMsg.SafeSnwprintf( - ASPNETCORE_EVENT_MODULE_DISABLED_MSG))) - { - UTILITY::LogEvent(g_hEventLog, - EVENTLOG_WARNING_TYPE, - ASPNETCORE_EVENT_MODULE_DISABLED, - strEventMsg.QueryStr()); - } + EventLog::Warn( + ASPNETCORE_EVENT_MODULE_DISABLED, + ASPNETCORE_EVENT_MODULE_DISABLED_MSG); // this will return 500 error to client // as we did not register the module goto Finished; diff --git a/src/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj b/src/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj index e905714bfa..f9903b2d38 100644 --- a/src/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj +++ b/src/AspNetCoreModuleV2/CommonLib/CommonLib.vcxproj @@ -193,12 +193,11 @@ - - + @@ -206,28 +205,24 @@ - - - - - + + - @@ -236,7 +231,6 @@ Create Create - diff --git a/src/AspNetCoreModuleV2/CommonLib/EventLog.cpp b/src/AspNetCoreModuleV2/CommonLib/EventLog.cpp new file mode 100644 index 0000000000..7d10a8abf9 --- /dev/null +++ b/src/AspNetCoreModuleV2/CommonLib/EventLog.cpp @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +#include "EventLog.h" +#include "debugutil.h" + +extern HANDLE g_hEventLog; + +VOID +EventLog::LogEvent( + _In_ WORD dwEventInfoType, + _In_ DWORD dwEventId, + _In_ LPCWSTR pstrMsg +) +{ + if (g_hEventLog != NULL) + { + ReportEventW(g_hEventLog, + dwEventInfoType, + 0, // wCategory + dwEventId, + NULL, // lpUserSid + 1, // wNumStrings + 0, // dwDataSize, + &pstrMsg, + NULL // lpRawData + ); + } + + DebugPrintf(dwEventInfoType == EVENTLOG_ERROR_TYPE ? ASPNETCORE_DEBUG_FLAG_ERROR : ASPNETCORE_DEBUG_FLAG_INFO, "Event Log: %S", pstrMsg); +} + +VOID +EventLog::LogEventF( + _In_ WORD dwEventInfoType, + _In_ DWORD dwEventId, + _In_ LPCWSTR pstrMsg, + va_list argsList +) +{ + STACK_STRU ( strEventMsg, 256 ); + + if (SUCCEEDED(strEventMsg.SafeVsnwprintf( + pstrMsg, + argsList))) + { + EventLog::LogEvent( + dwEventInfoType, + dwEventId, + strEventMsg.QueryStr()); + } +} diff --git a/src/AspNetCoreModuleV2/CommonLib/EventLog.h b/src/AspNetCoreModuleV2/CommonLib/EventLog.h index 3efbe38b35..cdc330300f 100644 --- a/src/AspNetCoreModuleV2/CommonLib/EventLog.h +++ b/src/AspNetCoreModuleV2/CommonLib/EventLog.h @@ -3,9 +3,65 @@ #pragma once -#include "Utility.h" #include "resources.h" -extern HANDLE g_hEventLog; +class EventLog +{ +public: + static + VOID + Error( + _In_ DWORD dwEventId, + _In_ PCWSTR pstrMsg, + ...) + { + va_list args; + va_start(args, pstrMsg); + LogEventF(EVENTLOG_ERROR_TYPE, dwEventId, pstrMsg, args); + va_end(args); + } -#define EVENTLOG(log, name, ...) UTILITY::LogEventF(log, ASPNETCORE_EVENT_ ## name ## _LEVEL, ASPNETCORE_EVENT_ ## name, ASPNETCORE_EVENT_ ## name ## _MSG, __VA_ARGS__) + static + VOID + Info( + _In_ DWORD dwEventId, + _In_ PCWSTR pstrMsg, + ...) + { + va_list args; + va_start(args, pstrMsg); + LogEventF(EVENTLOG_INFORMATION_TYPE, dwEventId, pstrMsg, args); + va_end(args); + } + + static + VOID + Warn( + _In_ DWORD dwEventId, + _In_ PCWSTR pstrMsg, + ...) + { + va_list args; + va_start(args, pstrMsg); + LogEventF(EVENTLOG_WARNING_TYPE, dwEventId, pstrMsg, args); + va_end(args); + } + +private: + static + VOID + LogEvent( + _In_ WORD dwEventInfoType, + _In_ DWORD dwEventId, + _In_ LPCWSTR pstrMsg + ); + + static + VOID + LogEventF( + _In_ WORD dwEventInfoType, + _In_ DWORD dwEventId, + __in PCWSTR pstrMsg, + va_list argsList + ); +}; diff --git a/src/AspNetCoreModuleV2/CommonLib/application.h b/src/AspNetCoreModuleV2/CommonLib/application.h index 1167602327..9c02484b1a 100644 --- a/src/AspNetCoreModuleV2/CommonLib/application.h +++ b/src/AspNetCoreModuleV2/CommonLib/application.h @@ -4,8 +4,6 @@ #pragma once #include "iapplication.h" -#include "exceptions.h" -#include "utility.h" #include "ntassert.h" #include "SRWExclusiveLock.h" @@ -73,7 +71,7 @@ public: protected: SRWLOCK m_stateLock; - bool m_fStopCalled; + bool m_fStopCalled; private: mutable LONG m_cRefs; diff --git a/src/AspNetCoreModuleV2/CommonLib/debugutil.cpp b/src/AspNetCoreModuleV2/CommonLib/debugutil.cpp index 4e9e73a936..e31351a764 100644 --- a/src/AspNetCoreModuleV2/CommonLib/debugutil.cpp +++ b/src/AspNetCoreModuleV2/CommonLib/debugutil.cpp @@ -47,8 +47,9 @@ PrintDebugHeader() RETURN_LAST_ERROR_IF(!VerQueryValue(verData.data(), _T("\\StringFileInfo\\040904b0\\FileDescription"), &pvProductName, &iProductNameLen)); // Major, minor are stored in dwFileVersionMS field and patch, build in dwFileVersionLS field as pair of 32 bit numbers - DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, "Initializing logs for %S. File Version: %d.%d.%d.%d. Description: %S", + DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, "Initializing logs for %S. ProcessId: %d. File Version: %d.%d.%d.%d. Description: %S", path, + GetCurrentProcessId(), ( verInfo->dwFileVersionMS >> 16 ) & 0xffff, ( verInfo->dwFileVersionMS >> 0 ) & 0xffff, ( verInfo->dwFileVersionLS >> 16 ) & 0xffff, diff --git a/src/AspNetCoreModuleV2/CommonLib/file_utility.cpp b/src/AspNetCoreModuleV2/CommonLib/file_utility.cpp new file mode 100644 index 0000000000..0d12c32259 --- /dev/null +++ b/src/AspNetCoreModuleV2/CommonLib/file_utility.cpp @@ -0,0 +1,198 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +#include "file_utility.h" + +#include +#include "debugutil.h" + +HRESULT +FILE_UTILITY::IsPathUnc( + __in LPCWSTR pszPath, + __out BOOL * pfIsUnc +) +{ + HRESULT hr = S_OK; + STRU strTempPath; + + if ( pszPath == NULL || pfIsUnc == NULL ) + { + hr = E_INVALIDARG; + goto Finished; + } + + hr = MakePathCanonicalizationProof( (LPWSTR) pszPath, &strTempPath ); + if ( FAILED(hr) ) + { + goto Finished; + } + + // + // MakePathCanonicalizationProof will map \\?\UNC, \\.\UNC and \\ to \\?\UNC + // + (*pfIsUnc) = ( _wcsnicmp( strTempPath.QueryStr(), L"\\\\?\\UNC\\", 8 /* sizeof \\?\UNC\ */) == 0 ); + +Finished: + + return hr; +} + +HRESULT +FILE_UTILITY::ConvertPathToFullPath( + _In_ LPCWSTR pszPath, + _In_ LPCWSTR pszRootPath, + _Out_ STRU* pStruFullPath +) +{ + HRESULT hr = S_OK; + STRU strFileFullPath; + LPWSTR pszFullPath = NULL; + + // if relative path, prefix with root path and then convert to absolute path. + if ( PathIsRelative(pszPath) ) + { + hr = strFileFullPath.Copy(pszRootPath); + if(FAILED(hr)) + { + goto Finished; + } + + if(!strFileFullPath.EndsWith(L"\\")) + { + hr = strFileFullPath.Append(L"\\"); + if(FAILED(hr)) + { + goto Finished; + } + } + } + + hr = strFileFullPath.Append( pszPath ); + if (FAILED(hr)) + { + goto Finished; + } + + pszFullPath = new WCHAR[ strFileFullPath.QueryCCH() + 1]; + + if(_wfullpath( pszFullPath, + strFileFullPath.QueryStr(), + strFileFullPath.QueryCCH() + 1 ) == NULL ) + { + hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); + goto Finished; + } + + // convert to canonical path + hr = MakePathCanonicalizationProof( pszFullPath, pStruFullPath ); + if (FAILED(hr)) + { + goto Finished; + } + +Finished: + + if ( pszFullPath != NULL ) + { + delete[] pszFullPath; + pszFullPath = NULL; + } + + return hr; +} + +HRESULT +FILE_UTILITY::EnsureDirectoryPathExist( + _In_ LPCWSTR pszPath +) +{ + HRESULT hr = S_OK; + STRU struPath; + DWORD dwPosition = 0; + BOOL fDone = FALSE; + BOOL fUnc = FALSE; + + struPath.Copy(pszPath); + hr = IsPathUnc(pszPath, &fUnc); + if (FAILED(hr)) + { + goto Finished; + } + if (fUnc) + { + // "\\?\UNC\" + dwPosition = 8; + } + else if (struPath.IndexOf(L'?', 0) != -1) + { + // sceanrio "\\?\" + dwPosition = 4; + } + while (!fDone) + { + dwPosition = struPath.IndexOf(L'\\', dwPosition + 1); + if (dwPosition == -1) + { + // not found '/' + fDone = TRUE; + goto Finished; + } + else if (dwPosition ==0) + { + hr = ERROR_INTERNAL_ERROR; + goto Finished; + } + else if (struPath.QueryStr()[dwPosition-1] == L':') + { + // skip volume case + continue; + } + else + { + struPath.QueryStr()[dwPosition] = L'\0'; + } + + if (!CreateDirectory(struPath.QueryStr(), NULL) && + ERROR_ALREADY_EXISTS != GetLastError()) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + fDone = TRUE; + goto Finished; + } + struPath.QueryStr()[dwPosition] = L'\\'; + } + +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 new file mode 100644 index 0000000000..900e787cd7 --- /dev/null +++ b/src/AspNetCoreModuleV2/CommonLib/file_utility.h @@ -0,0 +1,42 @@ +// 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 "stdafx.h" + +#include +#include "stringu.h" + +class FILE_UTILITY +{ +public: + + static + HRESULT + ConvertPathToFullPath( + _In_ LPCWSTR pszPath, + _In_ LPCWSTR pszRootPath, + _Out_ STRU* pStrFullPath + ); + + static + HRESULT + EnsureDirectoryPathExist( + _In_ LPCWSTR pszPath + ); + + static + BOOL + CheckIfFileExists( + PCWSTR pszFilePath + ); +private: + static + HRESULT + IsPathUnc( + __in LPCWSTR pszPath, + __out BOOL * pfIsUnc + ); +}; + diff --git a/src/AspNetCoreModuleV2/CommonLib/hostfxr_utility.cpp b/src/AspNetCoreModuleV2/CommonLib/hostfxr_utility.cpp index 792cd4e411..3d360b324d 100644 --- a/src/AspNetCoreModuleV2/CommonLib/hostfxr_utility.cpp +++ b/src/AspNetCoreModuleV2/CommonLib/hostfxr_utility.cpp @@ -12,6 +12,7 @@ #include "exceptions.h" #include "HandleWrapper.h" #include "Environment.h" +#include "file_utility.h" namespace fs = std::filesystem; @@ -32,7 +33,6 @@ HOSTFXR_UTILITY::GetStandaloneHostfxrParameters( PCWSTR pwzExeAbsolutePath, // includes .exe file extension. PCWSTR pcwzApplicationPhysicalPath, PCWSTR pcwzArguments, - HANDLE hEventLog, _Inout_ STRU* pStruHostFxrDllLocation, _Out_ DWORD* pdwArgCount, _Out_ BSTR** ppwzArgv @@ -65,11 +65,19 @@ HOSTFXR_UTILITY::GetStandaloneHostfxrParameters( LOG_INFOF("Checking runtimeconfig.json at %S", runtimeConfigLocation.c_str()); if (!is_regular_file(runtimeConfigLocation)) { - EVENTLOG(hEventLog, INPROCESS_FULL_FRAMEWORK_APP, pcwzApplicationPhysicalPath, 0); + EventLog::Error( + ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP, + ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP_MSG, + pcwzApplicationPhysicalPath, + 0); return E_FAIL; } - EVENTLOG(hEventLog, APPLICATION_EXE_NOT_FOUND, pcwzApplicationPhysicalPath, 0); + EventLog::Error( + ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND, + ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG, + pcwzApplicationPhysicalPath, + 0); return E_FAIL; } @@ -90,7 +98,6 @@ HOSTFXR_UTILITY::GetStandaloneHostfxrParameters( arguments.c_str(), pwzExeAbsolutePath, pcwzApplicationPhysicalPath, - hEventLog, pdwArgCount, ppwzArgv)); @@ -107,7 +114,6 @@ HOSTFXR_UTILITY::IsDotnetExecutable(const std::filesystem::path & dotnetPath) HRESULT HOSTFXR_UTILITY::GetHostFxrParameters( - _In_ HANDLE hEventLog, _In_ PCWSTR pcwzProcessPath, _In_ PCWSTR pcwzApplicationPhysicalPath, _In_ PCWSTR pcwzArguments, @@ -142,13 +148,17 @@ HOSTFXR_UTILITY::GetHostFxrParameters( if (!fullProcessPath.has_value()) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - EVENTLOG(hEventLog, INVALID_PROCESS_PATH, processPath.c_str(), hr); + EventLog::Error( + ASPNETCORE_EVENT_INVALID_PROCESS_PATH, + ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG, + processPath.c_str(), + hr); return hr; } processPath = fullProcessPath.value(); - auto hostFxrPath = GetAbsolutePathToHostFxr(processPath, hEventLog); + auto hostFxrPath = GetAbsolutePathToHostFxr(processPath); if (!hostFxrPath.has_value()) { hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); @@ -159,7 +169,6 @@ HOSTFXR_UTILITY::GetHostFxrParameters( arguments.c_str(), processPath.c_str(), pcwzApplicationPhysicalPath, - hEventLog, pdwArgCount, pbstrArgv)); @@ -186,7 +195,6 @@ HOSTFXR_UTILITY::GetHostFxrParameters( processPath.c_str(), pcwzApplicationPhysicalPath, arguments.c_str(), - hEventLog, pStruHostFxrDllLocation, pdwArgCount, pbstrArgv)); @@ -200,7 +208,11 @@ HOSTFXR_UTILITY::GetHostFxrParameters( // then it is an invalid argument. // hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); - EVENTLOG(hEventLog, INVALID_PROCESS_PATH, processPath.c_str(), hr); + EventLog::Error( + ASPNETCORE_EVENT_INVALID_PROCESS_PATH, + ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG, + processPath.c_str(), + hr); return hr; } } @@ -221,13 +233,10 @@ HOSTFXR_UTILITY::ParseHostfxrArguments( PCWSTR pwzArgumentsFromConfig, PCWSTR pwzExePath, PCWSTR pcwzApplicationPhysicalPath, - HANDLE hEventLog, _Out_ DWORD* pdwArgCount, _Out_ BSTR** pbstrArgv ) { - UNREFERENCED_PARAMETER(hEventLog); // TODO use event log to set errors. - DBG_ASSERT(pdwArgCount != NULL); DBG_ASSERT(pbstrArgv != NULL); DBG_ASSERT(pwzExePath != NULL); @@ -276,7 +285,7 @@ HOSTFXR_UTILITY::ParseHostfxrArguments( struTempPath.Copy(pwzArgs[intArgsProcessed]); if (struTempPath.EndsWith(L".dll")) { - if (SUCCEEDED(UTILITY::ConvertPathToFullPath(pwzArgs[intArgsProcessed], pcwzApplicationPhysicalPath, &struTempPath))) + if (SUCCEEDED(FILE_UTILITY::ConvertPathToFullPath(pwzArgs[intArgsProcessed], pcwzApplicationPhysicalPath, &struTempPath))) { argv[intArgsProcessed + 1] = SysAllocString(struTempPath.QueryStr()); } @@ -398,8 +407,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet( std::optional HOSTFXR_UTILITY::GetAbsolutePathToHostFxr( - const fs::path & dotnetPath, - HANDLE hEventLog + const fs::path & dotnetPath ) { std::vector versionFolders; @@ -409,7 +417,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToHostFxr( if (!is_directory(hostFxrBase)) { - EVENTLOG(hEventLog, HOSTFXR_DIRECTORY_NOT_FOUND, hostFxrBase.c_str(), HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT)); + EventLog::Error(ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND, ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG, hostFxrBase.c_str(), HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT)); return std::nullopt; } @@ -419,7 +427,11 @@ HOSTFXR_UTILITY::GetAbsolutePathToHostFxr( if (versionFolders.empty()) { - EVENTLOG(hEventLog, HOSTFXR_DIRECTORY_NOT_FOUND, hostFxrBase.c_str(), HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT)); + EventLog::Error( + ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND, + ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG, + hostFxrBase.c_str(), + HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT)); return std::nullopt; } @@ -429,7 +441,11 @@ HOSTFXR_UTILITY::GetAbsolutePathToHostFxr( if (!is_regular_file(hostFxrPath)) { - EVENTLOG(hEventLog, HOSTFXR_DLL_NOT_FOUND, hostFxrPath.c_str(), HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); + EventLog::Error( + ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND, + ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG, + hostFxrPath.c_str(), + HRESULT_FROM_WIN32(ERROR_FILE_INVALID)); return std::nullopt; } diff --git a/src/AspNetCoreModuleV2/CommonLib/hostfxr_utility.h b/src/AspNetCoreModuleV2/CommonLib/hostfxr_utility.h index 9e56fb4e28..126867a865 100644 --- a/src/AspNetCoreModuleV2/CommonLib/hostfxr_utility.h +++ b/src/AspNetCoreModuleV2/CommonLib/hostfxr_utility.h @@ -24,7 +24,6 @@ public: PCWSTR pwzExeAbsolutePath, // includes .exe file extension. PCWSTR pcwzApplicationPhysicalPath, PCWSTR pcwzArguments, - HANDLE hEventLog, _Inout_ STRU* pStruHostFxrDllLocation, _Out_ DWORD* pdwArgCount, _Out_ BSTR** ppwzArgv @@ -36,7 +35,6 @@ public: PCWSTR pwzArgumentsFromConfig, PCWSTR pwzExePath, PCWSTR pcwzApplicationPhysicalPath, - HANDLE hEventLog, _Out_ DWORD* pdwArgCount, _Out_ BSTR** pbstrArgv ); @@ -50,7 +48,6 @@ public: static HRESULT GetHostFxrParameters( - _In_ HANDLE hEventLog, _In_ PCWSTR pcwzProcessPath, _In_ PCWSTR pcwzApplicationPhysicalPath, _In_ PCWSTR pcwzArguments, @@ -76,8 +73,7 @@ public: static std::optional GetAbsolutePathToHostFxr( - _In_ const std::filesystem::path & dotnetPath, - _In_ HANDLE hEventLog + _In_ const std::filesystem::path & dotnetPath ); static diff --git a/src/AspNetCoreModuleV2/CommonLib/hostfxroptions.cpp b/src/AspNetCoreModuleV2/CommonLib/hostfxroptions.cpp index fb65edf710..e8f2983028 100644 --- a/src/AspNetCoreModuleV2/CommonLib/hostfxroptions.cpp +++ b/src/AspNetCoreModuleV2/CommonLib/hostfxroptions.cpp @@ -12,7 +12,6 @@ HRESULT HOSTFXR_OPTIONS::Create( _In_ PCWSTR pcwzProcessPath, _In_ PCWSTR pcwzApplicationPhysicalPath, _In_ PCWSTR pcwzArguments, - _In_ HANDLE hEventLog, _Out_ std::unique_ptr& ppWrapper) { STRU struHostFxrDllLocation; @@ -30,7 +29,6 @@ HRESULT HOSTFXR_OPTIONS::Create( if (struExeLocation.IsEmpty()) { RETURN_IF_FAILED(HOSTFXR_UTILITY::GetHostFxrParameters( - hEventLog, pcwzProcessPath, pcwzApplicationPhysicalPath, pcwzArguments, @@ -45,7 +43,6 @@ HRESULT HOSTFXR_OPTIONS::Create( pcwzArguments, pcwzExeLocation, pcwzApplicationPhysicalPath, - hEventLog, &dwArgCount, &pwzArgv)); } @@ -55,7 +52,6 @@ HRESULT HOSTFXR_OPTIONS::Create( pcwzExeLocation, pcwzApplicationPhysicalPath, pcwzArguments, - hEventLog, &struHostFxrDllLocation, &dwArgCount, &pwzArgv)); diff --git a/src/AspNetCoreModuleV2/CommonLib/hostfxroptions.h b/src/AspNetCoreModuleV2/CommonLib/hostfxroptions.h index 0529de4e62..99b972cde8 100644 --- a/src/AspNetCoreModuleV2/CommonLib/hostfxroptions.h +++ b/src/AspNetCoreModuleV2/CommonLib/hostfxroptions.h @@ -48,7 +48,6 @@ public: _In_ PCWSTR pcwzProcessPath, _In_ PCWSTR pcwzApplicationPhysicalPath, _In_ PCWSTR pcwzArguments, - _In_ HANDLE hEventLog, _Out_ std::unique_ptr& ppWrapper); private: diff --git a/src/AspNetCoreModuleV2/CommonLib/resources.h b/src/AspNetCoreModuleV2/CommonLib/resources.h index b1e8264941..63e0e9b163 100644 --- a/src/AspNetCoreModuleV2/CommonLib/resources.h +++ b/src/AspNetCoreModuleV2/CommonLib/resources.h @@ -30,7 +30,6 @@ #define ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG L"Only one inprocess application is allowed per IIS application pool. Please assign the application '%s' to a different IIS application pool." #define ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG L"Mixed hosting model is not supported. Application '%s' configured with different hostingModel value '%d' other than the one of running application(s)." #define ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR_MSG L"Unknown hosting model '%s'. Please specify either hostingModel=\"inprocess\" or hostingModel=\"outofprocess\" in the web.config file." -#define ASPNETCORE_EVENT_UNKNOWN_HOSTING_MODEL_ERROR_LEVEL EVENTLOG_ERROR_TYPE #define ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG L"Failed to start application '%s', ErrorCode '0x%x'." #define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. Last 4KB characters of captured stdout and stderr logs:\r\n%s" #define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. Please check the stderr logs for more information." @@ -40,17 +39,12 @@ #define ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG L"Application '%s' was recycled due to configuration change" #define ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG L"Failed to recycle application due to a configuration change at '%s'. Recycling worker process." #define ASPNETCORE_EVENT_MODULE_DISABLED_MSG L"AspNetCore Module is disabled" -#define ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP_LEVEL EVENTLOG_ERROR_TYPE #define ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP_MSG L"Application '%s' was compiled for .NET Framework. Please compile for .NET core to run the inprocess application or change the process mode to out of process. ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_PORTABLE_APP_DOTNET_MISSING_MSG L"Could not find dotnet.exe on the system PATH environment variable for portable application '%s'. Check that a valid path to dotnet is on the PATH and the bitness of dotnet matches the bitness of the IIS worker process. ErrorCode = '0x%x'." -#define ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_LEVEL EVENTLOG_ERROR_TYPE #define ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG L"Could not find the hostfxr directory '%s' in the dotnet directory. ErrorCode = '0x%x'." -#define ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_LEVEL EVENTLOG_ERROR_TYPE #define ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG L"Could not find hostfxr.dll in '%s'. ErrorCode = '0x%x'." -#define ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_LEVEL EVENTLOG_ERROR_TYPE #define ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG L"Could not find application executable in '%s'. ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, ErrorCode = '0x%x. Please check the stderr logs for more information." -#define ASPNETCORE_EVENT_INVALID_PROCESS_PATH_LEVEL EVENTLOG_ERROR_TYPE #define ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG L"Invalid or unknown processPath provided in web.config: processPath = '%s', ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_INPROCESS_RH_MISSING_MSG L"Could not find the assembly '%s' for in-process application. Please confirm the Microsoft.AspNetCore.Server.IIS package is referenced in your application." #define ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG L"Could not find the assembly '%s' for out-of-process application. Please confirm the assembly is installed correctly for IIS or IISExpress." diff --git a/src/AspNetCoreModuleV2/CommonLib/utility.cpp b/src/AspNetCoreModuleV2/CommonLib/utility.cpp deleted file mode 100644 index 2f8c25378c..0000000000 --- a/src/AspNetCoreModuleV2/CommonLib/utility.cpp +++ /dev/null @@ -1,603 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -#include "utility.h" - -#include -#include "debugutil.h" - -// static -HRESULT -UTILITY::SplitUrl( - PCWSTR pszDestinationUrl, - BOOL *pfSecure, - STRU *pstrDestination, - STRU *pstrUrl -) -/*++ - -Routine Description: - - Split the URL specified for forwarding into its specific components - The format of the URL looks like - http[s]://destination[:port]/path - when port is omitted, the default port for that specific protocol is used - when host is omitted, it gets the same value as the destination - -Arguments: - - pszDestinationUrl - the url to be split up - pfSecure - SSL to be used in forwarding? - pstrDestination - destination - pDestinationPort - port - pstrUrl - URL - -Return Value: - - HRESULT - ---*/ -{ - HRESULT hr; - - // - // First determine if the target is secure - // - if (_wcsnicmp(pszDestinationUrl, L"http://", 7) == 0) - { - *pfSecure = FALSE; - pszDestinationUrl += 7; - } - else if (_wcsnicmp(pszDestinationUrl, L"https://", 8) == 0) - { - *pfSecure = TRUE; - pszDestinationUrl += 8; - } - else - { - return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - - if (*pszDestinationUrl == L'\0') - { - return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - } - - // - // Find the 3rd slash corresponding to the url - // - LPCWSTR pszSlash = wcschr(pszDestinationUrl, L'/'); - if (pszSlash == NULL) - { - if (FAILED(hr = pstrUrl->Copy(L"/", 1)) || - FAILED(hr = pstrDestination->Copy(pszDestinationUrl))) - { - return hr; - } - } - else - { - if (FAILED(hr = pstrUrl->Copy(pszSlash)) || - FAILED(hr = pstrDestination->Copy(pszDestinationUrl, - (DWORD)(pszSlash - pszDestinationUrl)))) - { - return hr; - } - } - - return S_OK; -} - -// Change a hexadecimal digit to its numerical equivalent -#define TOHEX( ch ) \ - ((ch) > L'9' ? \ - (ch) >= L'a' ? \ - (ch) - L'a' + 10 : \ - (ch) - L'A' + 10 \ - : (ch) - L'0') - -// static -HRESULT -UTILITY::UnEscapeUrl( - PCWSTR pszUrl, - DWORD cchUrl, - bool fCopyQuery, - STRA * pstrResult -) -{ - HRESULT hr; - CHAR pch[2]; - pch[1] = '\0'; - DWORD cchStart = 0; - DWORD index = 0; - - while (index < cchUrl && - (fCopyQuery || pszUrl[index] != L'?')) - { - switch (pszUrl[index]) - { - case L'%': - if (iswxdigit(pszUrl[index+1]) && iswxdigit(pszUrl[index+2])) - { - if (index > cchStart && - FAILED(hr = pstrResult->AppendW(pszUrl + cchStart, - index - cchStart))) - { - return hr; - } - cchStart = index+3; - - pch[0] = static_cast(TOHEX(pszUrl[index+1]) * 16 + - TOHEX(pszUrl[index+2])); - if (FAILED(hr = pstrResult->Append(pch, 1))) - { - return hr; - } - index += 3; - break; - } - - __fallthrough; - default: - index++; - } - } - - if (index > cchStart) - { - return pstrResult->AppendW(pszUrl + cchStart, - index - cchStart); - } - - return S_OK; -} - -// static -HRESULT -UTILITY::UnEscapeUrl( - PCWSTR pszUrl, - DWORD cchUrl, - STRU * pstrResult -) -{ - HRESULT hr; - WCHAR pch[2]; - pch[1] = L'\0'; - DWORD cchStart = 0; - DWORD index = 0; - bool fInQuery = FALSE; - - while (index < cchUrl) - { - switch (pszUrl[index]) - { - case L'%': - if (iswxdigit(pszUrl[index+1]) && iswxdigit(pszUrl[index+2])) - { - if (index > cchStart && - FAILED(hr = pstrResult->Append(pszUrl + cchStart, - index - cchStart))) - { - return hr; - } - cchStart = index+3; - - pch[0] = static_cast(TOHEX(pszUrl[index+1]) * 16 + - TOHEX(pszUrl[index+2])); - if (FAILED(hr = pstrResult->Append(pch, 1))) - { - return hr; - } - index += 3; - if (pch[0] == L'?') - { - fInQuery = TRUE; - } - break; - } - - index++; - break; - - case L'/': - if (fInQuery) - { - if (index > cchStart && - FAILED(hr = pstrResult->Append(pszUrl + cchStart, - index - cchStart))) - { - return hr; - } - cchStart = index+1; - - if (FAILED(hr = pstrResult->Append(L"\\", 1))) - { - return hr; - } - index += 1; - break; - } - - __fallthrough; - default: - index++; - } - } - - if (index > cchStart) - { - return pstrResult->Append(pszUrl + cchStart, - index - cchStart); - } - - return S_OK; -} - -HRESULT -UTILITY::EscapeAbsPath( - IHttpRequest * pRequest, - STRU * strEscapedUrl -) -{ - HRESULT hr = S_OK; - STRU strAbsPath; - LPCWSTR pszAbsPath = NULL; - LPCWSTR pszFindStr = NULL; - - hr = strAbsPath.Copy( pRequest->GetRawHttpRequest()->CookedUrl.pAbsPath, - pRequest->GetRawHttpRequest()->CookedUrl.AbsPathLength / sizeof(WCHAR) ); - if(FAILED(hr)) - { - goto Finished; - } - - pszAbsPath = strAbsPath.QueryStr(); - pszFindStr = wcschr(pszAbsPath, L'?'); - - while(pszFindStr != NULL) - { - strEscapedUrl->Append( pszAbsPath, pszFindStr - pszAbsPath); - strEscapedUrl->Append(L"%3F"); - pszAbsPath = pszFindStr + 1; - pszFindStr = wcschr(pszAbsPath, L'?'); - } - - strEscapedUrl->Append(pszAbsPath); - strEscapedUrl->Append(pRequest->GetRawHttpRequest()->CookedUrl.pQueryString, - pRequest->GetRawHttpRequest()->CookedUrl.QueryStringLength / sizeof(WCHAR)); - -Finished: - return hr; -} - -// static -bool -UTILITY::IsValidAttributeNameChar( - WCHAR ch -) -{ - // - // Values based on ASP.NET rendering for cookie names. RFC 2965 is not clear - // what the non-special characters are. - // - return ch == L'\t' || (ch > 31 && ch < 127); -} - -// static -bool -UTILITY::FindInMultiString( - PCWSTR pszMultiString, - PCWSTR pszStringToFind -) -{ - while (*pszMultiString != L'\0') - { - if (wcscmp(pszMultiString, pszStringToFind) == 0) - { - return TRUE; - } - pszMultiString += wcslen(pszMultiString) + 1; - } - - return FALSE; -} - -// static -bool -UTILITY::IsValidQueryStringName( - PCWSTR pszName -) -{ - while (*pszName != L'\0') - { - WCHAR c = *pszName; - if (c != L'-' && c != L'_' && c != L'+' && - c != L'.' && c != L'*' && c != L'$' && c != L'%' && c != L',' && - !iswalnum(c)) - { - return FALSE; - } - pszName++; - } - - return TRUE; -} - -// static -bool -UTILITY::IsValidHeaderName( - PCWSTR pszName -) -{ - while (*pszName != L'\0') - { - WCHAR c = *pszName; - if (c != L'-' && c != L'_' && c != L'+' && - c != L'.' && c != L'*' && c != L'$' && c != L'%' - && !iswalnum(c)) - { - return FALSE; - } - pszName++; - } - - return TRUE; -} - -HRESULT -UTILITY::IsPathUnc( - __in LPCWSTR pszPath, - __out BOOL * pfIsUnc -) -{ - HRESULT hr = S_OK; - STRU strTempPath; - - if ( pszPath == NULL || pfIsUnc == NULL ) - { - hr = E_INVALIDARG; - goto Finished; - } - - hr = MakePathCanonicalizationProof( (LPWSTR) pszPath, &strTempPath ); - if ( FAILED(hr) ) - { - goto Finished; - } - - // - // MakePathCanonicalizationProof will map \\?\UNC, \\.\UNC and \\ to \\?\UNC - // - (*pfIsUnc) = ( _wcsnicmp( strTempPath.QueryStr(), L"\\\\?\\UNC\\", 8 /* sizeof \\?\UNC\ */) == 0 ); - -Finished: - - return hr; -} - -HRESULT -UTILITY::ConvertPathToFullPath( - _In_ LPCWSTR pszPath, - _In_ LPCWSTR pszRootPath, - _Out_ STRU* pStruFullPath -) -{ - HRESULT hr = S_OK; - STRU strFileFullPath; - LPWSTR pszFullPath = NULL; - - // if relative path, prefix with root path and then convert to absolute path. - if ( PathIsRelative(pszPath) ) - { - hr = strFileFullPath.Copy(pszRootPath); - if(FAILED(hr)) - { - goto Finished; - } - - if(!strFileFullPath.EndsWith(L"\\")) - { - hr = strFileFullPath.Append(L"\\"); - if(FAILED(hr)) - { - goto Finished; - } - } - } - - hr = strFileFullPath.Append( pszPath ); - if (FAILED(hr)) - { - goto Finished; - } - - pszFullPath = new WCHAR[ strFileFullPath.QueryCCH() + 1]; - - if(_wfullpath( pszFullPath, - strFileFullPath.QueryStr(), - strFileFullPath.QueryCCH() + 1 ) == NULL ) - { - hr = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); - goto Finished; - } - - // convert to canonical path - hr = MakePathCanonicalizationProof( pszFullPath, pStruFullPath ); - if (FAILED(hr)) - { - goto Finished; - } - -Finished: - - if ( pszFullPath != NULL ) - { - delete[] pszFullPath; - pszFullPath = NULL; - } - - return hr; -} - -HRESULT -UTILITY::EnsureDirectoryPathExist( - _In_ LPCWSTR pszPath -) -{ - HRESULT hr = S_OK; - STRU struPath; - DWORD dwPosition = 0; - BOOL fDone = FALSE; - BOOL fUnc = FALSE; - - struPath.Copy(pszPath); - hr = IsPathUnc(pszPath, &fUnc); - if (FAILED(hr)) - { - goto Finished; - } - if (fUnc) - { - // "\\?\UNC\" - dwPosition = 8; - } - else if (struPath.IndexOf(L'?', 0) != -1) - { - // sceanrio "\\?\" - dwPosition = 4; - } - while (!fDone) - { - dwPosition = struPath.IndexOf(L'\\', dwPosition + 1); - if (dwPosition == -1) - { - // not found '/' - fDone = TRUE; - goto Finished; - } - else if (dwPosition ==0) - { - hr = ERROR_INTERNAL_ERROR; - goto Finished; - } - else if (struPath.QueryStr()[dwPosition-1] == L':') - { - // skip volume case - continue; - } - else - { - struPath.QueryStr()[dwPosition] = L'\0'; - } - - if (!CreateDirectory(struPath.QueryStr(), NULL) && - ERROR_ALREADY_EXISTS != GetLastError()) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - fDone = TRUE; - goto Finished; - } - struPath.QueryStr()[dwPosition] = L'\\'; - } - -Finished: - return hr; -} - -BOOL -UTILITY::DirectoryExists( - _In_ STRU *pstrPath -) -{ - WIN32_FILE_ATTRIBUTE_DATA data; - - if (pstrPath->IsEmpty()) - { - return false; - } - - return GetFileAttributesExW(pstrPath->QueryStr(), GetFileExInfoStandard, &data); -} - -BOOL -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; -} - -VOID -UTILITY::LogEvent( - _In_ HANDLE hEventLog, - _In_ WORD dwEventInfoType, - _In_ DWORD dwEventId, - _In_ LPCWSTR pstrMsg -) -{ - if (hEventLog != NULL) - { - ReportEventW(hEventLog, - dwEventInfoType, - 0, // wCategory - dwEventId, - NULL, // lpUserSid - 1, // wNumStrings - 0, // dwDataSize, - &pstrMsg, - NULL // lpRawData - ); - } - - DebugPrintf(dwEventInfoType == EVENTLOG_ERROR_TYPE ? ASPNETCORE_DEBUG_FLAG_ERROR : ASPNETCORE_DEBUG_FLAG_INFO, "Event Log: %S", pstrMsg); -} - -VOID -UTILITY::LogEventF( - _In_ HANDLE hEventLog, - _In_ WORD dwEventInfoType, - _In_ DWORD dwEventId, - _In_ LPCWSTR pstrMsg, - ... -) -{ - va_list argsList; - va_start(argsList, pstrMsg); - - STACK_STRU ( strEventMsg, 256 ); - - if (SUCCEEDED(strEventMsg.SafeVsnwprintf( - pstrMsg, - argsList))) - { - UTILITY::LogEvent(hEventLog, - dwEventInfoType, - dwEventId, - strEventMsg.QueryStr()); - } - - va_end( argsList ); -} diff --git a/src/AspNetCoreModuleV2/CommonLib/utility.h b/src/AspNetCoreModuleV2/CommonLib/utility.h deleted file mode 100644 index 998d8fb3d6..0000000000 --- a/src/AspNetCoreModuleV2/CommonLib/utility.h +++ /dev/null @@ -1,130 +0,0 @@ -// 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 "stdafx.h" - -#include -#include "stringa.h" -#include "stringu.h" - -class UTILITY -{ -public: - - static - HRESULT - SplitUrl( - PCWSTR pszDestinationUrl, - BOOL *pfSecure, - STRU *pstrDestination, - STRU *pstrUrl - ); - - static - HRESULT - UnEscapeUrl( - PCWSTR pszUrl, - DWORD cchUrl, - bool fCopyQuery, - STRA * pstrResult - ); - - static - HRESULT - UnEscapeUrl( - PCWSTR pszUrl, - DWORD cchUrl, - STRU * pstrResult - ); - - static HRESULT - EscapeAbsPath( - IHttpRequest * pRequest, - STRU * strEscapedUrl - ); - - static - bool - IsValidAttributeNameChar( - WCHAR ch - ); - - static - bool - IsValidQueryStringName( - PCWSTR pszName - ); - - static - bool - IsValidHeaderName( - PCWSTR pszName - ); - - static - bool - FindInMultiString( - PCWSTR pszMultiString, - PCWSTR pszStringToFind - ); - - static - HRESULT - IsPathUnc( - __in LPCWSTR pszPath, - __out BOOL * pfIsUnc - ); - - static - HRESULT - ConvertPathToFullPath( - _In_ LPCWSTR pszPath, - _In_ LPCWSTR pszRootPath, - _Out_ STRU* pStrFullPath - ); - - static - HRESULT - EnsureDirectoryPathExist( - _In_ LPCWSTR pszPath - ); - - static - BOOL - DirectoryExists( - _In_ STRU *pstrPath - ); - - static - BOOL - CheckIfFileExists( - PCWSTR pszFilePath - ); - - static - VOID - LogEvent( - _In_ HANDLE hEventLog, - _In_ WORD dwEventInfoType, - _In_ DWORD dwEventId, - _In_ LPCWSTR pstrMsg - ); - - static - VOID - LogEventF( - _In_ HANDLE hEventLog, - _In_ WORD dwEventInfoType, - _In_ DWORD dwEventId, - __in PCWSTR pstrMsg, - ... - ); - -private: - - UTILITY() {} - ~UTILITY() {} -}; - diff --git a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp index 7207f745c3..b9746ff204 100644 --- a/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp +++ b/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp @@ -6,11 +6,11 @@ #include "hostfxroptions.h" #include "requesthandler_config.h" #include "environmentvariablehelpers.h" -#include "utility.h" #include "SRWExclusiveLock.h" #include "exceptions.h" #include "LoggingHelpers.h" #include "resources.h" +#include "EventLog.h" const LPCSTR IN_PROCESS_APPLICATION::s_exeLocationParameterName = "InProcessExeLocation"; @@ -112,16 +112,14 @@ Finished: if (FAILED(hr)) { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_WARNING_TYPE, + EventLog::Warn( ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE, ASPNETCORE_EVENT_APP_SHUTDOWN_FAILURE_MSG, m_pConfig->QueryConfigPath()->QueryStr()); } else { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_INFORMATION_TYPE, + EventLog::Info( ASPNETCORE_EVENT_APP_SHUTDOWN_SUCCESSFUL, ASPNETCORE_EVENT_APP_SHUTDOWN_SUCCESSFUL_MSG, m_pConfig->QueryConfigPath()->QueryStr()); @@ -220,8 +218,7 @@ IN_PROCESS_APPLICATION::SetCallbackHandles( // Can't check the std err handle as it isn't a critical error // Initialization complete - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_INFORMATION_TYPE, + EventLog::Info( ASPNETCORE_EVENT_INPROCESS_START_SUCCESS, ASPNETCORE_EVENT_INPROCESS_START_SUCCESS_MSG, m_pConfig->QueryApplicationPhysicalPath()->QueryStr()); @@ -361,8 +358,7 @@ Finished: { m_status = MANAGED_APPLICATION_STATUS::FAIL; - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, + EventLog::Error( ASPNETCORE_EVENT_LOAD_CLR_FALIURE, ASPNETCORE_EVENT_LOAD_CLR_FALIURE_MSG, m_pConfig->QueryApplicationPath()->QueryStr(), @@ -463,7 +459,6 @@ IN_PROCESS_APPLICATION::ExecuteApplication( m_pConfig->QueryProcessPath()->QueryStr(), m_pConfig->QueryApplicationPhysicalPath()->QueryStr(), m_pConfig->QueryArguments()->QueryStr(), - g_hEventLog, hostFxrOptions )); @@ -527,8 +522,7 @@ IN_PROCESS_APPLICATION::LogErrorsOnMainExit( if (m_pLoggerProvider->GetStdOutContent(&straStdErrOutput)) { if (SUCCEEDED(struStdMsg.CopyA(straStdErrOutput.QueryStr()))) { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, + EventLog::Error( ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT, ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG, m_pConfig->QueryApplicationPath()->QueryStr(), @@ -539,8 +533,7 @@ IN_PROCESS_APPLICATION::LogErrorsOnMainExit( } else { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, + EventLog::Error( ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT, ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG, m_pConfig->QueryApplicationPath()->QueryStr(), diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/OutOfProcessRequestHandler.vcxproj b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/OutOfProcessRequestHandler.vcxproj index 4fc4809b7f..781eda827c 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/OutOfProcessRequestHandler.vcxproj +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/OutOfProcessRequestHandler.vcxproj @@ -231,6 +231,7 @@ + @@ -251,6 +252,7 @@ Create Create + diff --git a/src/AspNetCoreModuleV2/CommonLib/disconnectcontext.h b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/disconnectcontext.h similarity index 100% rename from src/AspNetCoreModuleV2/CommonLib/disconnectcontext.h rename to src/AspNetCoreModuleV2/OutOfProcessRequestHandler/disconnectcontext.h diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp index 7df01e1ab1..d74571e953 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/dllmain.cpp @@ -2,6 +2,7 @@ #include #include +#include "exceptions.h" DECLARE_DEBUG_PRINT_OBJECT("aspnetcorev2_outofprocess.dll"); diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwardinghandler.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwardinghandler.cpp index 8b41b72ee4..f1bcb7a2cc 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwardinghandler.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/forwardinghandler.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. #include "forwardinghandler.h" +#include "url_utility.h" // Just to be aware of the FORWARDING_HANDLER object size. C_ASSERT(sizeof(FORWARDING_HANDLER) <= 632); @@ -164,7 +165,7 @@ FORWARDING_HANDLER::OnExecuteRequestHandler() // // parse original url // - if (FAILED(hr = UTILITY::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl, + if (FAILED(hr = URL_UTILITY::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl, &fSecure, &strDestination, &strUrl))) @@ -172,7 +173,7 @@ FORWARDING_HANDLER::OnExecuteRequestHandler() goto Failure; } - if (FAILED(hr = UTILITY::EscapeAbsPath(pRequest, &struEscapedUrl))) + if (FAILED(hr = URL_UTILITY::EscapeAbsPath(pRequest, &struEscapedUrl))) { goto Failure; } @@ -870,7 +871,7 @@ FORWARDING_HANDLER::GetHeaders( // if (!pProtocol->QueryPreserveHostHeader()) { - if (FAILED(hr = UTILITY::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl, + if (FAILED(hr = URL_UTILITY::SplitUrl(pRequest->GetRawHttpRequest()->CookedUrl.pFullUrl, &fSecure, &struDestination, &struUrl)) || diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp index 5619319c22..b0dd2991e2 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/processmanager.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. #include "processmanager.h" +#include "EventLog.h" volatile BOOL PROCESS_MANAGER::sm_fWSAStartupDone = FALSE; @@ -194,8 +195,7 @@ PROCESS_MANAGER::GetProcess( // // rapid fails per minute exceeded, do not create new process. // - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_INFORMATION_TYPE, + EventLog::Info( ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED, ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED_MSG, pConfig->QueryRapidFailsPerMinute()); diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp index 74a29edc26..03abd7fd23 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp @@ -4,6 +4,8 @@ #include "serverprocess.h" #include +#include "EventLog.h" +#include "file_utility.h" //#include //extern BOOL g_fNsiApiNotSupported; @@ -212,8 +214,7 @@ Finished: if (FAILED(hr)) { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, + EventLog::Error( ASPNETCORE_EVENT_PROCESS_START_SUCCESS, ASPNETCORE_EVENT_PROCESS_START_PORTSETUP_ERROR_MSG, m_struAppFullPath.QueryStr(), @@ -735,9 +736,7 @@ Finished: if (!strEventMsg.IsEmpty()) { - UTILITY::LogEvent( - g_hEventLog, - EVENTLOG_WARNING_TYPE, + EventLog::Warn( ASPNETCORE_EVENT_PROCESS_START_ERROR, strEventMsg.QueryStr()); } @@ -900,8 +899,7 @@ SERVER_PROCESS::StartProcess( // Backend process starts successfully. Set retry counter to 0 dwRetryCount = 0; - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_INFORMATION_TYPE, + EventLog::Info( ASPNETCORE_EVENT_PROCESS_START_SUCCESS, ASPNETCORE_EVENT_PROCESS_START_SUCCESS_MSG, m_struAppFullPath.QueryStr(), @@ -918,8 +916,7 @@ SERVER_PROCESS::StartProcess( dwRetryCount = 0; } - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_WARNING_TYPE, + EventLog::Warn( ASPNETCORE_EVENT_PROCESS_START_ERROR, ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG, m_struAppFullPath.QueryStr(), @@ -963,8 +960,7 @@ Finished: m_Timer.CancelTimer(); } - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_ERROR_TYPE, + EventLog::Error( ASPNETCORE_EVENT_PROCESS_START_FAILURE, ASPNETCORE_EVENT_PROCESS_START_FAILURE_MSG, m_struAppFullPath.QueryStr(), @@ -1034,7 +1030,7 @@ SERVER_PROCESS::SetupStdHandles( m_hStdoutHandle = NULL; } - hr = UTILITY::ConvertPathToFullPath( + hr = FILE_UTILITY::ConvertPathToFullPath( m_struLogFile.QueryStr(), m_struPhysicalPath.QueryStr(), &struPath); @@ -1058,7 +1054,7 @@ SERVER_PROCESS::SetupStdHandles( goto Finished; } - hr = UTILITY::EnsureDirectoryPathExist(struPath.QueryStr()); + hr = FILE_UTILITY::EnsureDirectoryPathExist(struPath.QueryStr()); if (FAILED(hr)) { goto Finished; @@ -1100,8 +1096,7 @@ Finished: if (m_fStdoutLogEnabled) { // Log the error - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_WARNING_TYPE, + EventLog::Warn( ASPNETCORE_EVENT_CONFIG_ERROR, ASPNETCORE_EVENT_INVALID_STDOUT_LOG_FILE_MSG, m_struFullLogFile.IsEmpty()? m_struLogFile.QueryStr() : m_struFullLogFile.QueryStr(), @@ -1900,9 +1895,7 @@ SERVER_PROCESS::HandleProcessExit( VOID ) if (!fReady) { - UTILITY::LogEventF( - g_hEventLog, - EVENTLOG_INFORMATION_TYPE, + EventLog::Info( ASPNETCORE_EVENT_PROCESS_SHUTDOWN, ASPNETCORE_EVENT_PROCESS_SHUTDOWN_MSG, m_struAppFullPath.QueryStr(), @@ -2032,8 +2025,7 @@ SERVER_PROCESS::SendShutdownHttpMessage( VOID ) } // log - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_INFORMATION_TYPE, + EventLog::Info( ASPNETCORE_EVENT_SENT_SHUTDOWN_HTTP_REQUEST, ASPNETCORE_EVENT_SENT_SHUTDOWN_HTTP_REQUEST_MSG, m_dwProcessId, @@ -2150,8 +2142,7 @@ SERVER_PROCESS::TerminateBackendProcess( } // log a warning for ungraceful shutdown - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_WARNING_TYPE, + EventLog::Warn( ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE, ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE_MSG, m_dwProcessId); diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/stdafx.h b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/stdafx.h index 7cdcfa0146..83498f3057 100644 --- a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/stdafx.h +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/stdafx.h @@ -55,7 +55,6 @@ // Common lib #include "requesthandler.h" -#include "utility.h" #include "application.h" #include "resources.h" #include "aspnetcore_event.h" diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/url_utility.cpp b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/url_utility.cpp new file mode 100644 index 0000000000..c5b3a06e86 --- /dev/null +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/url_utility.cpp @@ -0,0 +1,135 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +#include "url_utility.h" + +#include +#include "debugutil.h" + +// static +HRESULT +URL_UTILITY::SplitUrl( + PCWSTR pszDestinationUrl, + BOOL *pfSecure, + STRU *pstrDestination, + STRU *pstrUrl +) +/*++ + +Routine Description: + + Split the URL specified for forwarding into its specific components + The format of the URL looks like + http[s]://destination[:port]/path + when port is omitted, the default port for that specific protocol is used + when host is omitted, it gets the same value as the destination + +Arguments: + + pszDestinationUrl - the url to be split up + pfSecure - SSL to be used in forwarding? + pstrDestination - destination + pDestinationPort - port + pstrUrl - URL + +Return Value: + + HRESULT + +--*/ +{ + HRESULT hr; + + // + // First determine if the target is secure + // + if (_wcsnicmp(pszDestinationUrl, L"http://", 7) == 0) + { + *pfSecure = FALSE; + pszDestinationUrl += 7; + } + else if (_wcsnicmp(pszDestinationUrl, L"https://", 8) == 0) + { + *pfSecure = TRUE; + pszDestinationUrl += 8; + } + else + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + + if (*pszDestinationUrl == L'\0') + { + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + + // + // Find the 3rd slash corresponding to the url + // + LPCWSTR pszSlash = wcschr(pszDestinationUrl, L'/'); + if (pszSlash == NULL) + { + if (FAILED(hr = pstrUrl->Copy(L"/", 1)) || + FAILED(hr = pstrDestination->Copy(pszDestinationUrl))) + { + return hr; + } + } + else + { + if (FAILED(hr = pstrUrl->Copy(pszSlash)) || + FAILED(hr = pstrDestination->Copy(pszDestinationUrl, + (DWORD)(pszSlash - pszDestinationUrl)))) + { + return hr; + } + } + + return S_OK; +} + +// Change a hexadecimal digit to its numerical equivalent +#define TOHEX( ch ) \ + ((ch) > L'9' ? \ + (ch) >= L'a' ? \ + (ch) - L'a' + 10 : \ + (ch) - L'A' + 10 \ + : (ch) - L'0') + +HRESULT +URL_UTILITY::EscapeAbsPath( + IHttpRequest * pRequest, + STRU * strEscapedUrl +) +{ + HRESULT hr = S_OK; + STRU strAbsPath; + LPCWSTR pszAbsPath = NULL; + LPCWSTR pszFindStr = NULL; + + hr = strAbsPath.Copy( pRequest->GetRawHttpRequest()->CookedUrl.pAbsPath, + pRequest->GetRawHttpRequest()->CookedUrl.AbsPathLength / sizeof(WCHAR) ); + if(FAILED(hr)) + { + goto Finished; + } + + pszAbsPath = strAbsPath.QueryStr(); + pszFindStr = wcschr(pszAbsPath, L'?'); + + while(pszFindStr != NULL) + { + strEscapedUrl->Append( pszAbsPath, pszFindStr - pszAbsPath); + strEscapedUrl->Append(L"%3F"); + pszAbsPath = pszFindStr + 1; + pszFindStr = wcschr(pszAbsPath, L'?'); + } + + strEscapedUrl->Append(pszAbsPath); + strEscapedUrl->Append(pRequest->GetRawHttpRequest()->CookedUrl.pQueryString, + pRequest->GetRawHttpRequest()->CookedUrl.QueryStringLength / sizeof(WCHAR)); + +Finished: + return hr; +} + diff --git a/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/url_utility.h b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/url_utility.h new file mode 100644 index 0000000000..a096a96cf0 --- /dev/null +++ b/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/url_utility.h @@ -0,0 +1,30 @@ +// 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 "stdafx.h" + +#include +#include "stringu.h" + +class URL_UTILITY +{ +public: + + static + HRESULT + SplitUrl( + PCWSTR pszDestinationUrl, + BOOL *pfSecure, + STRU *pstrDestination, + STRU *pstrUrl + ); + + static HRESULT + EscapeAbsPath( + IHttpRequest * pRequest, + STRU * strEscapedUrl + ); +}; + diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/AppOfflineTrackingApplication.cpp b/src/AspNetCoreModuleV2/RequestHandlerLib/AppOfflineTrackingApplication.cpp index 47a92d3daa..f156ec1fbc 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/AppOfflineTrackingApplication.cpp +++ b/src/AspNetCoreModuleV2/RequestHandlerLib/AppOfflineTrackingApplication.cpp @@ -13,12 +13,11 @@ HRESULT AppOfflineTrackingApplication::StartMonitoringAppOffline() if (FAILED_LOG(hr)) { - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_WARNING_TYPE, - ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR, - ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR_MSG, - m_applicationPath.c_str(), - hr); + EventLog::Warn( + ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR, + ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR_MSG, + m_applicationPath.c_str(), + hr); } return hr; @@ -58,11 +57,10 @@ void AppOfflineTrackingApplication::OnAppOffline() } LOG_INFOF("Received app_offline notification in application %S", m_applicationPath.c_str()); - UTILITY::LogEventF(g_hEventLog, - EVENTLOG_INFORMATION_TYPE, - ASPNETCORE_EVENT_RECYCLE_APPOFFLINE, - ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG, - m_applicationPath.c_str()); + EventLog::Info( + ASPNETCORE_EVENT_RECYCLE_APPOFFLINE, + ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG, + m_applicationPath.c_str()); Stop(/*fServerInitiated*/ false); } diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.cpp b/src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.cpp index 65e70757ab..29041f7ab5 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.cpp +++ b/src/AspNetCoreModuleV2/RequestHandlerLib/FileOutputManager.cpp @@ -4,10 +4,10 @@ #include "stdafx.h" #include "FileOutputManager.h" #include "sttimer.h" -#include "utility.h" #include "exceptions.h" #include "debugutil.h" #include "SRWExclusiveLock.h" +#include "file_utility.h" FileOutputManager::FileOutputManager() : m_hLogFileHandle(INVALID_HANDLE_VALUE), @@ -82,12 +82,12 @@ FileOutputManager::Start() SECURITY_ATTRIBUTES saAttr = { 0 }; STRU struPath; - RETURN_IF_FAILED(UTILITY::ConvertPathToFullPath( + RETURN_IF_FAILED(FILE_UTILITY::ConvertPathToFullPath( m_wsStdOutLogFileName.QueryStr(), m_wsApplicationPath.QueryStr(), &struPath)); - RETURN_IF_FAILED(UTILITY::EnsureDirectoryPathExist(struPath.QueryStr())); + RETURN_IF_FAILED(FILE_UTILITY::EnsureDirectoryPathExist(struPath.QueryStr())); GetSystemTime(&systemTime); diff --git a/src/AspNetCoreModuleV2/RequestHandlerLib/filewatcher.cpp b/src/AspNetCoreModuleV2/RequestHandlerLib/filewatcher.cpp index a4d7a0c57e..7c0aaccefa 100644 --- a/src/AspNetCoreModuleV2/RequestHandlerLib/filewatcher.cpp +++ b/src/AspNetCoreModuleV2/RequestHandlerLib/filewatcher.cpp @@ -5,6 +5,7 @@ #include "filewatcher.h" #include "debugutil.h" #include "AppOfflineTrackingApplication.h" +#include "exceptions.h" FILE_WATCHER::FILE_WATCHER() : m_hCompletionPort(NULL), diff --git a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs index e257ed00a7..54efe1f3fe 100644 --- a/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs +++ b/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS/IISDeployer.cs @@ -123,47 +123,61 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS private void GetLogsFromFile() { - // Handle cases where debug file is redirected by test - var debugLogLocations = new List(); - if (IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile")) + try { - debugLogLocations.Add(IISDeploymentParameters.HandlerSettings["debugFile"]); - } - if (DeploymentParameters.EnvironmentVariables.ContainsKey("ASPNETCORE_MODULE_DEBUG_FILE")) - { - debugLogLocations.Add(DeploymentParameters.EnvironmentVariables["ASPNETCORE_MODULE_DEBUG_FILE"]); - } - - // default debug file name - debugLogLocations.Add("aspnetcore-debug.log"); - - foreach (var debugLogLocation in debugLogLocations) - { - if (string.IsNullOrEmpty(debugLogLocation)) + // Handle cases where debug file is redirected by test + var debugLogLocations = new List(); + if (IISDeploymentParameters.HandlerSettings.ContainsKey("debugFile")) { - continue; + debugLogLocations.Add(IISDeploymentParameters.HandlerSettings["debugFile"]); } - var file = Path.Combine(DeploymentParameters.PublishedApplicationRootPath, debugLogLocation); - if (File.Exists(file)) + if (DeploymentParameters.EnvironmentVariables.ContainsKey("ASPNETCORE_MODULE_DEBUG_FILE")) { - var lines = File.ReadAllLines(file); - if (!lines.Any()) + debugLogLocations.Add(DeploymentParameters.EnvironmentVariables["ASPNETCORE_MODULE_DEBUG_FILE"]); + } + + // default debug file name + debugLogLocations.Add("aspnetcore-debug.log"); + + foreach (var debugLogLocation in debugLogLocations) + { + if (string.IsNullOrEmpty(debugLogLocation)) { - Logger.LogInformation("Debug log file found but was empty"); continue; } - foreach (var line in lines) + var file = Path.Combine(DeploymentParameters.PublishedApplicationRootPath, debugLogLocation); + if (File.Exists(file)) { - Logger.LogInformation(line); + var lines = File.ReadAllLines(file); + if (!lines.Any()) + { + Logger.LogInformation($"Debug log file {file} found but was empty"); + continue; + } + + foreach (var line in lines) + { + Logger.LogInformation(line); + } + return; } - return; + } + // ANCM V1 does not support logs + if (DeploymentParameters.AncmVersion == AncmVersion.AspNetCoreModuleV2) + { + throw new InvalidOperationException($"Unable to find non-empty debug log files. Tried: {string.Join(", ", debugLogLocations)}"); + } + } + finally + { + if (File.Exists(_debugLogFile)) + { + File.Delete(_debugLogFile); } } - - throw new InvalidOperationException($"Not able to find non-empty debug log files. Tried: {string.Join(", ", debugLogLocations)}"); } public void StartIIS(Uri uri, string contentRoot) @@ -339,6 +353,12 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting.IIS throw new InvalidOperationException("Site not stopped yet"); } + if (appPool.WorkerProcesses.Any(wp => wp.State == WorkerProcessState.Running || + wp.State == WorkerProcessState.Stopping)) + { + throw new InvalidOperationException("WorkerProcess not stopped yet"); + } + if (!HostProcess.HasExited) { throw new InvalidOperationException("Site is stopped but host process is not"); diff --git a/test/Common.FunctionalTests/AppOfflineTests.cs b/test/Common.FunctionalTests/AppOfflineTests.cs index 3e1580fcb9..41bf8874d9 100644 --- a/test/Common.FunctionalTests/AppOfflineTests.cs +++ b/test/Common.FunctionalTests/AppOfflineTests.cs @@ -82,7 +82,6 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests } [ConditionalFact] - [RequiresIIS(IISCapability.ShutdownToken)] public async Task AppOfflineDroppedWhileSiteFailedToStartInRequestHandler_SiteStops_InProcess() { var deploymentResult = await DeployApp(HostingModel.InProcess); @@ -95,7 +94,8 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests Assert.Contains("500.30", await result.Content.ReadAsStringAsync()); AddAppOffline(deploymentResult.ContentRoot); - AssertStopsProcess(deploymentResult); + + await deploymentResult.AssertRecycledAsync(() => AssertAppOffline(deploymentResult)); } [ConditionalFact] @@ -130,7 +130,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests } catch { - AssertStopsProcess(deploymentResult); + deploymentResult.AssertWorkerProcessStop(); return; } } @@ -139,18 +139,16 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests } [ConditionalFact] - [RequiresIIS(IISCapability.ShutdownToken)] public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown_InProcess() { var deploymentResult = await AssertStarts(HostingModel.InProcess); AddAppOffline(deploymentResult.ContentRoot); - AssertStopsProcess(deploymentResult); + await deploymentResult.AssertRecycledAsync(() => AssertAppOffline(deploymentResult)); } [ConditionalFact] - [RequiresIIS(IISCapability.ShutdownToken)] public async Task AppOfflineDroppedWhileSiteRunning_SiteShutsDown_OutOfProcess() { var deploymentResult = await AssertStarts(HostingModel.OutOfProcess); @@ -245,28 +243,10 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests private async Task AssertAppOffline(IISDeploymentResult deploymentResult, string expectedResponse = "The app is offline.") { - HttpResponseMessage response = null; - - for (var i = 0; i < 5 && response?.StatusCode != HttpStatusCode.ServiceUnavailable; i++) - { - // Keep retrying until app_offline is present. - response = await deploymentResult.HttpClient.GetAsync("HelloWorld"); - await Task.Delay(RetryDelay); - } - - Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode); - + var response = await deploymentResult.HttpClient.RetryRequestAsync("HelloWorld", r => r.StatusCode == HttpStatusCode.ServiceUnavailable); Assert.Equal(expectedResponse, await response.Content.ReadAsStringAsync()); } - private void AssertStopsProcess(IISDeploymentResult deploymentResult) - { - var hostShutdownToken = deploymentResult.HostShutdownToken; - - Assert.True(hostShutdownToken.WaitHandle.WaitOne(TimeoutExtensions.DefaultTimeout)); - Assert.True(hostShutdownToken.IsCancellationRequested); - } - private async Task AssertStarts(HostingModel hostingModel) { var deploymentResult = await DeployApp(hostingModel); @@ -278,14 +258,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests private static async Task AssertRunning(IISDeploymentResult deploymentResult) { - HttpResponseMessage response = null; - - for (var i = 0; i < 5 && response?.IsSuccessStatusCode != true; i++) - { - response = await deploymentResult.HttpClient.GetAsync("HelloWorld"); - await Task.Delay(RetryDelay); - } - + var response = await deploymentResult.HttpClient.RetryRequestAsync("HelloWorld", r => r.IsSuccessStatusCode); var responseText = await response.Content.ReadAsStringAsync(); Assert.Equal("Hello World", responseText); } diff --git a/test/Common.FunctionalTests/ConfigurationChangeTests.cs b/test/Common.FunctionalTests/ConfigurationChangeTests.cs new file mode 100644 index 0000000000..b809bf42ca --- /dev/null +++ b/test/Common.FunctionalTests/ConfigurationChangeTests.cs @@ -0,0 +1,58 @@ +// 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.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; +using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.AspNetCore.Testing.xunit; +using Xunit; + +namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +{ + [Collection(PublishedSitesCollection.Name)] + public class ConfigurationChangeTests : IISFunctionalTestBase + { + private readonly PublishedSitesFixture _fixture; + + public ConfigurationChangeTests(PublishedSitesFixture fixture) + { + _fixture = fixture; + } + + [ConditionalFact] + public async Task ConfigurationChangeStopsInProcess() + { + var deploymentParameters = _fixture.GetBaseDeploymentParameters(HostingModel.InProcess, publish: true); + + var deploymentResult = await DeployAsync(deploymentParameters); + + await deploymentResult.AssertStarts(); + + // Just "touching" web.config should be enough + deploymentResult.ModifyWebConfig(element => {}); + + await deploymentResult.AssertRecycledAsync(); + } + + [ConditionalTheory] + [InlineData(AncmVersion.AspNetCoreModule)] + [InlineData(AncmVersion.AspNetCoreModuleV2)] + public async Task ConfigurationChangeForcesChildProcessRestart(AncmVersion version) + { + var deploymentParameters = _fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess, publish: true); + deploymentParameters.AncmVersion = version; + + var deploymentResult = await DeployAsync(deploymentParameters); + + var processBefore = await deploymentResult.HttpClient.GetStringAsync("/ProcessId"); + + // Just "touching" web.config should be enough + deploymentResult.ModifyWebConfig(element => {}); + + // Have to retry here to allow ANCM to receive notification and react to it + // Verify that worker process gets restarted with new process id + await deploymentResult.HttpClient.RetryRequestAsync("/ProcessId", async r => await r.Content.ReadAsStringAsync() != processBefore); + } + } +} diff --git a/test/Common.FunctionalTests/Utilities/Helpers.cs b/test/Common.FunctionalTests/Utilities/Helpers.cs index 2781da1944..30c597a111 100644 --- a/test/Common.FunctionalTests/Utilities/Helpers.cs +++ b/test/Common.FunctionalTests/Utilities/Helpers.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Xml.Linq; @@ -18,6 +17,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests { public static class Helpers { + private static readonly TimeSpan RetryRequestDelay = TimeSpan.FromMilliseconds(100); + private static readonly int RetryRequestCount = 5; + public static string GetTestWebSitePath(string name) { return Path.Combine(TestPathUtilities.GetSolutionRootDirectory("IISIntegration"),"test", "WebSites", name); @@ -26,9 +28,8 @@ 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") + public static async Task AssertStarts(this IISDeploymentResult deploymentResult, string path = "/HelloWorld") { var response = await deploymentResult.HttpClient.GetAsync(path); @@ -73,5 +74,64 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests fileInfo.CopyTo(destFileName); } } + + public static void ModifyWebConfig(this DeploymentResult deploymentResult, Action action) + { + var webConfigPath = Path.Combine(deploymentResult.ContentRoot, "web.config"); + var document = XDocument.Load(webConfigPath); + + document.Save(webConfigPath); + } + + public static Task RetryRequestAsync(this HttpClient client, string uri, Func predicate) + { + return RetryRequestAsync(client, uri, message => Task.FromResult(predicate(message))); + } + + public static async Task RetryRequestAsync(this HttpClient client, string uri, Func> predicate) + { + HttpResponseMessage response = await client.GetAsync(uri); + + for (var i = 0; i < RetryRequestCount && !await predicate(response); i++) + { + // Keep retrying until app_offline is present. + response = await client.GetAsync(uri); + await Task.Delay(RetryRequestDelay); + } + + if (!await predicate(response)) + { + throw new InvalidOperationException($"Didn't get response that satisfies predicate after {RetryRequestCount} retries"); + } + + return response; + } + + public static void AssertWorkerProcessStop(this IISDeploymentResult deploymentResult) + { + var hostProcess = deploymentResult.HostProcess; + Assert.True(hostProcess.WaitForExit((int)TimeoutExtensions.DefaultTimeout.TotalMilliseconds)); + + if (deploymentResult.DeploymentParameters.ServerType == ServerType.IISExpress) + { + Assert.Equal(0, hostProcess.ExitCode); + } + } + + + public static async Task AssertRecycledAsync(this IISDeploymentResult deploymentResult, Func verificationAction = null) + { + if (deploymentResult.DeploymentParameters.HostingModel != HostingModel.InProcess) + { + throw new NotSupportedException(); + } + + deploymentResult.AssertWorkerProcessStop(); + if (deploymentResult.DeploymentParameters.ServerType == ServerType.IIS) + { + verificationAction = verificationAction ?? (() => deploymentResult.AssertStarts()); + await verificationAction(); + } + } } } diff --git a/test/CommonLibTests/hostfxr_utility_tests.cpp b/test/CommonLibTests/hostfxr_utility_tests.cpp index a8cd554ec0..7997220726 100644 --- a/test/CommonLibTests/hostfxr_utility_tests.cpp +++ b/test/CommonLibTests/hostfxr_utility_tests.cpp @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #include "stdafx.h" +#include "file_utility.h" TEST(ParseHostFxrArguments, BasicHostFxrArguments) { @@ -12,7 +13,6 @@ TEST(ParseHostFxrArguments, BasicHostFxrArguments) L"exec \"test.dll\"", // args exeStr, // exe path L"invalid", // physical path to application - NULL, // event log &retVal, // arg count &bstrArray); // args array. @@ -33,7 +33,6 @@ TEST(ParseHostFxrArguments, NoExecProvided) L"test.dll", // args exeStr, // exe path L"ignored", // physical path to application - NULL, // event log &retVal, // arg count &bstrArray); // args array. @@ -53,7 +52,6 @@ TEST(ParseHostFxrArguments, ConvertDllToAbsolutePath) L"exec \"test.dll\"", // args exeStr, // exe path L"C:/test", // physical path to application - NULL, // event log &retVal, // arg count &bstrArray); // args array. @@ -74,7 +72,6 @@ TEST(ParseHostFxrArguments, ProvideNoArgs_InvalidArgs) L"", // args exeStr, // exe path L"ignored", // physical path to application - NULL, // event log &retVal, // arg count &bstrArray); // args array. @@ -101,11 +98,11 @@ TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks) if (is64Bit) { - fDotnetInProgramFiles = UTILITY::CheckIfFileExists(L"C:/Program Files/dotnet/dotnet.exe"); + fDotnetInProgramFiles = FILE_UTILITY::CheckIfFileExists(L"C:/Program Files/dotnet/dotnet.exe"); } else { - fDotnetInProgramFiles = UTILITY::CheckIfFileExists(L"C:/Program Files (x86)/dotnet/dotnet.exe"); + fDotnetInProgramFiles = FILE_UTILITY::CheckIfFileExists(L"C:/Program Files (x86)/dotnet/dotnet.exe"); } auto dotnetPath = HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles(); @@ -127,11 +124,10 @@ TEST(GetHostFxrArguments, InvalidParams) STRU struExeLocation; HRESULT hr = HOSTFXR_UTILITY::GetHostFxrParameters( - INVALID_HANDLE_VALUE, L"bogus", // processPath L"", // application physical path, ignored. L"ignored", //arguments - NULL, // event log + NULL, &struExeLocation, &retVal, // arg count &bstrArray); // args array. diff --git a/test/CommonLibTests/stdafx.h b/test/CommonLibTests/stdafx.h index 93dffac456..4b9ac7cd27 100644 --- a/test/CommonLibTests/stdafx.h +++ b/test/CommonLibTests/stdafx.h @@ -46,7 +46,6 @@ #include "config_utility.h" #include "environmentvariablehash.h" #include "iapplication.h" -#include "utility.h" #include "debugutil.h" #include "requesthandler.h" #include "resources.h"