// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. #include "applicationinfo.h" #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" #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(); } HRESULT APPLICATION_INFO::Initialize( _In_ IHttpApplication &pApplication ) { m_pConfiguration.reset(new ASPNETCORE_SHIM_CONFIG()); RETURN_IF_FAILED(m_pConfiguration->Populate(&m_pServer, &pApplication)); RETURN_IF_FAILED(m_struInfoKey.Copy(pApplication.GetApplicationId())); return S_OK; } HRESULT APPLICATION_INFO::GetOrCreateApplication( IHttpContext *pHttpContext, std::unique_ptr& pApplication ) { HRESULT hr = S_OK; SRWExclusiveLock lock(m_applicationLock); auto& httpApplication = *pHttpContext->GetApplication(); if (m_pApplication != nullptr) { if (m_pApplication->QueryStatus() == RECYCLED) { LOG_INFO("Application went offline"); // Application that went offline // are supposed to recycle themselves m_pApplication = nullptr; } else { // another thread created the application FINISHED(S_OK); } } if (AppOfflineApplication::ShouldBeStarted(httpApplication)) { LOG_INFO("Detected app_offline file, creating polling application"); m_pApplication.reset(new AppOfflineApplication(httpApplication)); } else { STRU struExeLocation; FINISHED_IF_FAILED(FindRequestHandlerAssembly(struExeLocation)); if (m_pfnAspNetCoreCreateApplication == NULL) { FINISHED(HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION)); } std::array parameters { {"InProcessExeLocation", struExeLocation.QueryStr()} }; LOG_INFO("Creating handler application"); IAPPLICATION * newApplication; FINISHED_IF_FAILED(m_pfnAspNetCoreCreateApplication( &m_pServer, &httpApplication, parameters.data(), static_cast(parameters.size()), &newApplication)); m_pApplication.reset(newApplication); } Finished: if (FAILED(hr)) { // Log the failure and update application info to not try again UTILITY::LogEventF(g_hEventLog, EVENTLOG_ERROR_TYPE, ASPNETCORE_EVENT_ADD_APPLICATION_ERROR, ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG, httpApplication.GetApplicationId(), hr); m_pApplication.reset(new ServerErrorApplication(httpApplication, hr)); } if (m_pApplication) { pApplication = ReferenceApplication(m_pApplication.get()); } 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); } } WLOG_INFOF(L"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() { SRWExclusiveLock lock(m_applicationLock); if (m_pApplication) { const auto pApplication = m_pApplication.release(); HandleWrapper hThread = CreateThread( NULL, // default security attributes 0, // default stack size (LPTHREAD_START_ROUTINE)DoRecycleApplication, pApplication, // thread function arguments 0, // default creation flags NULL); // receive thread identifier } } DWORD WINAPI APPLICATION_INFO::DoRecycleApplication( LPVOID lpParam) { auto pApplication = std::unique_ptr(static_cast(lpParam)); if (pApplication) { // Recycle will call shutdown for out of process pApplication->Stop(/*fServerInitiated*/ false); } return 0; } VOID APPLICATION_INFO::ShutDownApplication() { SRWExclusiveLock lock(m_applicationLock); if (m_pApplication) { m_pApplication ->Stop(/* fServerInitiated */ true); m_pApplication = nullptr; } }