Cleanup application manager (#1250)
This commit is contained in:
parent
e23d9cb4ac
commit
e26231b613
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include "iapplication.h"
|
||||
#include "HandleWrapper.h"
|
||||
|
||||
typedef
|
||||
HRESULT
|
||||
(WINAPI * PFN_ASPNETCORE_CREATE_APPLICATION)(
|
||||
_In_ IHttpServer *pServer,
|
||||
_In_ IHttpApplication *pHttpApplication,
|
||||
_In_ APPLICATION_PARAMETER *pParameters,
|
||||
_In_ DWORD nParameters,
|
||||
_Out_ IAPPLICATION **pApplication
|
||||
);
|
||||
|
||||
class ApplicationFactory
|
||||
{
|
||||
public:
|
||||
ApplicationFactory(HMODULE hRequestHandlerDll, std::wstring location, PFN_ASPNETCORE_CREATE_APPLICATION pfnAspNetCoreCreateApplication):
|
||||
m_pfnAspNetCoreCreateApplication(pfnAspNetCoreCreateApplication),
|
||||
m_location(location),
|
||||
m_hRequestHandlerDll(hRequestHandlerDll)
|
||||
{
|
||||
}
|
||||
|
||||
HRESULT Execute(
|
||||
_In_ IHttpServer *pServer,
|
||||
_In_ IHttpApplication *pHttpApplication,
|
||||
_Out_ IAPPLICATION **pApplication) const
|
||||
{
|
||||
std::array<APPLICATION_PARAMETER, 1> parameters {
|
||||
{"InProcessExeLocation", reinterpret_cast<const void*>(m_location.data())}
|
||||
};
|
||||
return m_pfnAspNetCoreCreateApplication(pServer, pHttpApplication, parameters.data(), static_cast<DWORD>(parameters.size()), pApplication);
|
||||
}
|
||||
|
||||
private:
|
||||
PFN_ASPNETCORE_CREATE_APPLICATION m_pfnAspNetCoreCreateApplication;
|
||||
std::wstring m_location;
|
||||
HandleWrapper<ModuleHandleTraits> m_hRequestHandlerDll;
|
||||
};
|
||||
|
|
@ -227,6 +227,7 @@
|
|||
</ResourceCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ApplicationFactory.h" />
|
||||
<ClInclude Include="applicationinfo.h" />
|
||||
<ClInclude Include="AppOfflineApplication.h" />
|
||||
<ClInclude Include="AppOfflineHandler.h" />
|
||||
|
|
|
|||
|
|
@ -19,56 +19,59 @@ const PCWSTR HandlerResolver::s_pwzAspnetcoreOutOfProcessRequestHandlerName = L"
|
|||
HandlerResolver::HandlerResolver(HMODULE hModule, IHttpServer &pServer)
|
||||
: m_hModule(hModule),
|
||||
m_pServer(pServer),
|
||||
m_fAspnetcoreRHLoadResult(S_FALSE),
|
||||
m_loadedApplicationHostingModel(HOSTING_UNKNOWN),
|
||||
m_hRequestHandlerDll(nullptr),
|
||||
m_pfnAspNetCoreCreateApplication(nullptr)
|
||||
m_loadedApplicationHostingModel(HOSTING_UNKNOWN)
|
||||
{
|
||||
InitializeSRWLock(&m_requestHandlerLoadLock);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, STRU& location, ASPNETCORE_SHIM_CONFIG * pConfiguration)
|
||||
HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, ASPNETCORE_SHIM_CONFIG& pConfiguration, std::unique_ptr<ApplicationFactory>& pApplicationFactory)
|
||||
{
|
||||
HRESULT hr;
|
||||
STACK_STRU(struFileName, MAX_PATH);
|
||||
PCWSTR pstrHandlerDllName;
|
||||
if (pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS)
|
||||
bool preventUnload;
|
||||
if (pConfiguration.QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS)
|
||||
{
|
||||
preventUnload = false;
|
||||
pstrHandlerDllName = s_pwzAspnetcoreInProcessRequestHandlerName;
|
||||
}
|
||||
else
|
||||
{
|
||||
// OutOfProcess handler is not able to handle unload correctly
|
||||
// It has code running after application.Stop exits
|
||||
preventUnload = true;
|
||||
pstrHandlerDllName = s_pwzAspnetcoreOutOfProcessRequestHandlerName;
|
||||
}
|
||||
|
||||
HandleWrapper<ModuleHandleTraits> hRequestHandlerDll;
|
||||
std::wstring location;
|
||||
std::wstring handlerDllPath;
|
||||
// Try to see if RH is already loaded, use GetModuleHandleEx to increment ref count
|
||||
if (!GetModuleHandleEx(0, pstrHandlerDllName, &m_hRequestHandlerDll))
|
||||
if (!GetModuleHandleEx(0, pstrHandlerDllName, &hRequestHandlerDll))
|
||||
{
|
||||
if (pConfiguration->QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS)
|
||||
if (pConfiguration.QueryHostingModel() == APP_HOSTING_MODEL::HOSTING_IN_PROCESS)
|
||||
{
|
||||
std::unique_ptr<HOSTFXR_OPTIONS> options;
|
||||
std::unique_ptr<IOutputManager> outputManager;
|
||||
|
||||
RETURN_IF_FAILED(HOSTFXR_OPTIONS::Create(
|
||||
NULL,
|
||||
pConfiguration->QueryProcessPath()->QueryStr(),
|
||||
pConfiguration.QueryProcessPath().c_str(),
|
||||
pApplication.GetApplicationPhysicalPath(),
|
||||
pConfiguration->QueryArguments()->QueryStr(),
|
||||
pConfiguration.QueryArguments().c_str(),
|
||||
options));
|
||||
|
||||
RETURN_IF_FAILED(location.Copy(options->GetExeLocation()));
|
||||
location = options->GetExeLocation();
|
||||
|
||||
RETURN_IF_FAILED(LoggingHelpers::CreateLoggingProvider(
|
||||
pConfiguration->QueryStdoutLogEnabled(),
|
||||
pConfiguration.QueryStdoutLogEnabled(),
|
||||
!m_pServer.IsCommandLineLaunch(),
|
||||
pConfiguration->QueryStdoutLogFile()->QueryStr(),
|
||||
pConfiguration.QueryStdoutLogFile()->QueryStr(),
|
||||
pApplication.GetApplicationPhysicalPath(),
|
||||
outputManager));
|
||||
|
||||
outputManager->Start();
|
||||
|
||||
hr = FindNativeAssemblyFromHostfxr(options.get(), pstrHandlerDllName, &struFileName);
|
||||
hr = FindNativeAssemblyFromHostfxr(*options.get(), pstrHandlerDllName, handlerDllPath);
|
||||
outputManager->Stop();
|
||||
|
||||
if (FAILED(hr) && m_hHostFxrDll != NULL)
|
||||
|
|
@ -85,85 +88,99 @@ HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, STRU
|
|||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_GENERAL_ERROR,
|
||||
ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG,
|
||||
struFileName.IsEmpty() ? s_pwzAspnetcoreInProcessRequestHandlerName : struFileName.QueryStr(),
|
||||
handlerDllPath.empty()? s_pwzAspnetcoreInProcessRequestHandlerName : handlerDllPath.c_str(),
|
||||
struStdMsg.QueryStr());
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FAILED_LOG(hr = FindNativeAssemblyFromGlobalLocation(pConfiguration, pstrHandlerDllName, &struFileName)))
|
||||
if (FAILED_LOG(hr = FindNativeAssemblyFromGlobalLocation(pConfiguration, pstrHandlerDllName, handlerDllPath)))
|
||||
{
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING,
|
||||
ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG,
|
||||
struFileName.IsEmpty() ? s_pwzAspnetcoreOutOfProcessRequestHandlerName : struFileName.QueryStr());
|
||||
handlerDllPath.empty() ? s_pwzAspnetcoreOutOfProcessRequestHandlerName : handlerDllPath.c_str());
|
||||
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFOF("Loading request handler: %S", struFileName.QueryStr());
|
||||
LOG_INFOF("Loading request handler: %S", handlerDllPath.c_str());
|
||||
|
||||
m_hRequestHandlerDll = LoadLibraryW(struFileName.QueryStr());
|
||||
RETURN_LAST_ERROR_IF_NULL(m_hRequestHandlerDll);
|
||||
hRequestHandlerDll = LoadLibrary(handlerDllPath.c_str());
|
||||
if (preventUnload)
|
||||
{
|
||||
// Pin module in memory
|
||||
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_PIN, pstrHandlerDllName, &hRequestHandlerDll);
|
||||
}
|
||||
RETURN_LAST_ERROR_IF_NULL(hRequestHandlerDll);
|
||||
}
|
||||
|
||||
m_pfnAspNetCoreCreateApplication = reinterpret_cast<PFN_ASPNETCORE_CREATE_APPLICATION>(GetProcAddress(m_hRequestHandlerDll, "CreateApplication"));
|
||||
|
||||
RETURN_LAST_ERROR_IF_NULL(m_pfnAspNetCoreCreateApplication);
|
||||
auto pfnAspNetCoreCreateApplication = reinterpret_cast<PFN_ASPNETCORE_CREATE_APPLICATION>(GetProcAddress(hRequestHandlerDll, "CreateApplication"));
|
||||
RETURN_LAST_ERROR_IF_NULL(pfnAspNetCoreCreateApplication);
|
||||
|
||||
pApplicationFactory = std::make_unique<ApplicationFactory>(hRequestHandlerDll.release(), location, pfnAspNetCoreCreateApplication);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, STRU& location, PFN_ASPNETCORE_CREATE_APPLICATION * pfnCreateApplication)
|
||||
HandlerResolver::GetApplicationFactory(IHttpApplication &pApplication, std::unique_ptr<ApplicationFactory>& pApplicationFactory)
|
||||
{
|
||||
ASPNETCORE_SHIM_CONFIG pConfiguration;
|
||||
RETURN_IF_FAILED(pConfiguration.Populate(&m_pServer, &pApplication));
|
||||
|
||||
if (m_fAspnetcoreRHLoadResult == S_FALSE)
|
||||
try
|
||||
{
|
||||
ASPNETCORE_SHIM_CONFIG pConfiguration;
|
||||
RETURN_IF_FAILED(pConfiguration.Populate(&m_pServer, &pApplication));
|
||||
|
||||
SRWExclusiveLock lock(m_requestHandlerLoadLock);
|
||||
if (m_fAspnetcoreRHLoadResult == S_FALSE)
|
||||
if (m_loadedApplicationHostingModel != HOSTING_UNKNOWN)
|
||||
{
|
||||
m_loadedApplicationHostingModel = pConfiguration.QueryHostingModel();
|
||||
m_loadedApplicationId = pApplication.GetApplicationId();
|
||||
LOG_IF_FAILED(m_fAspnetcoreRHLoadResult = LoadRequestHandlerAssembly(pApplication, location, &pConfiguration));
|
||||
// Mixed hosting models
|
||||
if (m_loadedApplicationHostingModel != pConfiguration.QueryHostingModel())
|
||||
{
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR,
|
||||
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG,
|
||||
pApplication.GetApplicationId(),
|
||||
pConfiguration.QueryHostingModel());
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
// Multiple in-process apps
|
||||
if (m_loadedApplicationHostingModel == HOSTING_IN_PROCESS && m_loadedApplicationId != pApplication.GetApplicationId())
|
||||
{
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP,
|
||||
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG,
|
||||
pApplication.GetApplicationId());
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
m_loadedApplicationHostingModel = pConfiguration.QueryHostingModel();
|
||||
m_loadedApplicationId = pApplication.GetApplicationId();
|
||||
RETURN_IF_FAILED(LoadRequestHandlerAssembly(pApplication, pConfiguration, pApplicationFactory));
|
||||
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
// Mixed hosting models
|
||||
if (m_loadedApplicationHostingModel != pConfiguration.QueryHostingModel())
|
||||
{
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR,
|
||||
ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG,
|
||||
pApplication.GetApplicationId(),
|
||||
pConfiguration.QueryHostingModel());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
// Multiple in-process apps
|
||||
else if (m_loadedApplicationHostingModel == HOSTING_IN_PROCESS && m_loadedApplicationId != pApplication.GetApplicationId())
|
||||
{
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP,
|
||||
ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG,
|
||||
pApplication.GetApplicationId());
|
||||
void HandlerResolver::ResetHostingModel()
|
||||
{
|
||||
SRWExclusiveLock lock(m_requestHandlerLoadLock);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
*pfnCreateApplication = m_pfnAspNetCoreCreateApplication;
|
||||
return m_fAspnetcoreRHLoadResult;
|
||||
m_loadedApplicationHostingModel = APP_HOSTING_MODEL::HOSTING_UNKNOWN;
|
||||
m_loadedApplicationId.resize(0);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
HandlerResolver::FindNativeAssemblyFromGlobalLocation(
|
||||
ASPNETCORE_SHIM_CONFIG * pConfiguration,
|
||||
ASPNETCORE_SHIM_CONFIG& pConfiguration,
|
||||
PCWSTR pstrHandlerDllName,
|
||||
STRU* struFilename
|
||||
std::wstring& handlerDllPath
|
||||
)
|
||||
{
|
||||
try
|
||||
|
|
@ -172,12 +189,10 @@ HandlerResolver::FindNativeAssemblyFromGlobalLocation(
|
|||
|
||||
modulePath = GlobalVersionUtility::RemoveFileNameFromFolderPath(modulePath);
|
||||
|
||||
std::wstring retval = GlobalVersionUtility::GetGlobalRequestHandlerPath(modulePath.c_str(),
|
||||
pConfiguration->QueryHandlerVersion()->QueryStr(),
|
||||
handlerDllPath = GlobalVersionUtility::GetGlobalRequestHandlerPath(modulePath.c_str(),
|
||||
pConfiguration.QueryHandlerVersion().c_str(),
|
||||
pstrHandlerDllName
|
||||
);
|
||||
|
||||
RETURN_IF_FAILED(struFilename->Copy(retval.c_str()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
|
@ -199,25 +214,18 @@ HandlerResolver::FindNativeAssemblyFromGlobalLocation(
|
|||
//
|
||||
HRESULT
|
||||
HandlerResolver::FindNativeAssemblyFromHostfxr(
|
||||
HOSTFXR_OPTIONS* hostfxrOptions,
|
||||
HOSTFXR_OPTIONS& hostfxrOptions,
|
||||
PCWSTR libraryName,
|
||||
STRU* struFilename
|
||||
std::wstring& handlerDllPath
|
||||
)
|
||||
{
|
||||
STRU struApplicationFullPath;
|
||||
STRU struNativeSearchPaths;
|
||||
STRU struNativeDllLocation;
|
||||
INT intIndex = -1;
|
||||
INT intPrevIndex = 0;
|
||||
BOOL fFound = FALSE;
|
||||
DWORD dwBufferSize = 1024 * 10;
|
||||
DWORD dwRequiredBufferSize = 0;
|
||||
STRA output;
|
||||
std::wstring struNativeSearchPaths;
|
||||
size_t intIndex;
|
||||
size_t intPrevIndex = 0;
|
||||
DWORD dwBufferSize = s_initialGetNativeSearchDirectoriesBufferSize;
|
||||
DWORD dwRequiredBufferSize = 0;
|
||||
|
||||
|
||||
DBG_ASSERT(struFilename != NULL);
|
||||
|
||||
RETURN_LAST_ERROR_IF_NULL(m_hHostFxrDll = LoadLibraryW(hostfxrOptions->GetHostFxrLocation()));
|
||||
RETURN_LAST_ERROR_IF_NULL(m_hHostFxrDll = LoadLibraryW(hostfxrOptions.GetHostFxrLocation()));
|
||||
|
||||
auto pFnHostFxrSearchDirectories = reinterpret_cast<hostfxr_get_native_search_directories_fn>(GetProcAddress(m_hHostFxrDll, "hostfxr_get_native_search_directories"));
|
||||
if (pFnHostFxrSearchDirectories == nullptr)
|
||||
|
|
@ -225,20 +233,20 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
|
|||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_GENERAL_ERROR,
|
||||
ASPNETCORE_EVENT_HOSTFXR_DLL_INVALID_VERSION_MSG,
|
||||
hostfxrOptions->GetHostFxrLocation()
|
||||
hostfxrOptions.GetHostFxrLocation()
|
||||
);
|
||||
RETURN_IF_FAILED(E_FAIL);
|
||||
}
|
||||
|
||||
RETURN_LAST_ERROR_IF_NULL(pFnHostFxrSearchDirectories);
|
||||
RETURN_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize));
|
||||
struNativeSearchPaths.resize(dwBufferSize);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
auto intHostFxrExitCode = pFnHostFxrSearchDirectories(
|
||||
hostfxrOptions->GetArgc(),
|
||||
hostfxrOptions->GetArgv(),
|
||||
struNativeSearchPaths.QueryStr(),
|
||||
const auto intHostFxrExitCode = pFnHostFxrSearchDirectories(
|
||||
hostfxrOptions.GetArgc(),
|
||||
hostfxrOptions.GetArgv(),
|
||||
struNativeSearchPaths.data(),
|
||||
dwBufferSize,
|
||||
&dwRequiredBufferSize
|
||||
);
|
||||
|
|
@ -251,7 +259,7 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
|
|||
{
|
||||
dwBufferSize = dwRequiredBufferSize + 1; // for null terminator
|
||||
|
||||
RETURN_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize));
|
||||
struNativeSearchPaths.resize(dwBufferSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -260,26 +268,26 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
|
|||
}
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(struNativeSearchPaths.SyncWithBuffer());
|
||||
struNativeSearchPaths.resize(struNativeSearchPaths.find(L'\0'));
|
||||
|
||||
fFound = FALSE;
|
||||
auto fFound = FALSE;
|
||||
|
||||
// The native search directories are semicolon delimited.
|
||||
// Split on semicolons, append aspnetcorerh.dll, and check if the file exists.
|
||||
while ((intIndex = struNativeSearchPaths.IndexOf(L";", intPrevIndex)) != -1)
|
||||
while ((intIndex = struNativeSearchPaths.find(L';', intPrevIndex)) != std::wstring::npos)
|
||||
{
|
||||
RETURN_IF_FAILED(struNativeDllLocation.Copy(&struNativeSearchPaths.QueryStr()[intPrevIndex], intIndex - intPrevIndex));
|
||||
auto path = struNativeSearchPaths.substr(intPrevIndex, intIndex - intPrevIndex);
|
||||
|
||||
if (!struNativeDllLocation.EndsWith(L"\\"))
|
||||
if (!path.empty() && !(path[path.length() - 1] == L'\\'))
|
||||
{
|
||||
RETURN_IF_FAILED(struNativeDllLocation.Append(L"\\"));
|
||||
path.append(L"\\");
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(struNativeDllLocation.Append(libraryName));
|
||||
path.append(libraryName);
|
||||
|
||||
if (FILE_UTILITY::CheckIfFileExists(struNativeDllLocation.QueryStr()))
|
||||
if (std::filesystem::is_regular_file(path))
|
||||
{
|
||||
RETURN_IF_FAILED(struFilename->Copy(struNativeDllLocation));
|
||||
handlerDllPath = path;
|
||||
fFound = TRUE;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,47 +3,35 @@
|
|||
|
||||
#pragma once
|
||||
#include "aspnetcore_shim_config.h"
|
||||
#include "hostfxroptions.h"
|
||||
#include <memory>
|
||||
#include "iapplication.h"
|
||||
#include <string>
|
||||
#include "HandleWrapper.h"
|
||||
|
||||
typedef
|
||||
HRESULT
|
||||
(WINAPI * PFN_ASPNETCORE_CREATE_APPLICATION)(
|
||||
_In_ IHttpServer *pServer,
|
||||
_In_ IHttpApplication *pHttpApplication,
|
||||
_In_ APPLICATION_PARAMETER *pParameters,
|
||||
_In_ DWORD nParameters,
|
||||
_Out_ IAPPLICATION **pApplication
|
||||
);
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "hostfxroptions.h"
|
||||
#include "HandleWrapper.h"
|
||||
#include "ApplicationFactory.h"
|
||||
|
||||
class HandlerResolver
|
||||
{
|
||||
public:
|
||||
HandlerResolver(HMODULE hModule, IHttpServer &pServer);
|
||||
HRESULT GetApplicationFactory(IHttpApplication &pApplication, STRU& location, PFN_ASPNETCORE_CREATE_APPLICATION *pfnCreateApplication);
|
||||
HRESULT GetApplicationFactory(IHttpApplication &pApplication, std::unique_ptr<ApplicationFactory>& pApplicationFactory);
|
||||
void ResetHostingModel();
|
||||
|
||||
private:
|
||||
HRESULT LoadRequestHandlerAssembly(IHttpApplication &pApplication,STRU& location, ASPNETCORE_SHIM_CONFIG * pConfiguration);
|
||||
HRESULT FindNativeAssemblyFromGlobalLocation(ASPNETCORE_SHIM_CONFIG * pConfiguration, PCWSTR libraryName, STRU* location);
|
||||
HRESULT FindNativeAssemblyFromHostfxr(HOSTFXR_OPTIONS* hostfxrOptions, PCWSTR libraryName, STRU* location);
|
||||
HRESULT LoadRequestHandlerAssembly(IHttpApplication &pApplication, ASPNETCORE_SHIM_CONFIG& pConfiguration, std::unique_ptr<ApplicationFactory>& pApplicationFactory);
|
||||
HRESULT FindNativeAssemblyFromGlobalLocation(ASPNETCORE_SHIM_CONFIG& pConfiguration, PCWSTR libraryName, std::wstring& handlerDllPath);
|
||||
HRESULT FindNativeAssemblyFromHostfxr(HOSTFXR_OPTIONS& hostfxrOptions, PCWSTR libraryName, std::wstring& handlerDllPath);
|
||||
|
||||
HMODULE m_hModule;
|
||||
IHttpServer &m_pServer;
|
||||
|
||||
SRWLOCK m_requestHandlerLoadLock {};
|
||||
// S_FALSE - not loaded, S_OK - loaded, everything else - error
|
||||
HRESULT m_fAspnetcoreRHLoadResult;
|
||||
std::wstring m_loadedApplicationId;
|
||||
APP_HOSTING_MODEL m_loadedApplicationHostingModel;
|
||||
HandleWrapper<ModuleHandleTraits> m_hRequestHandlerDll;
|
||||
HandleWrapper<ModuleHandleTraits> m_hHostFxrDll;
|
||||
|
||||
PFN_ASPNETCORE_CREATE_APPLICATION m_pfnAspNetCoreCreateApplication;
|
||||
|
||||
static const PCWSTR s_pwzAspnetcoreInProcessRequestHandlerName;
|
||||
static const PCWSTR s_pwzAspnetcoreOutOfProcessRequestHandlerName;
|
||||
static const DWORD s_initialGetNativeSearchDirectoriesBufferSize = MAX_PATH * 4;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "applicationinfo.h"
|
||||
|
||||
#include <array>
|
||||
#include "proxymodule.h"
|
||||
#include "hostfxr_utility.h"
|
||||
#include "debugutil.h"
|
||||
|
|
@ -11,28 +10,14 @@
|
|||
#include "SRWExclusiveLock.h"
|
||||
#include "exceptions.h"
|
||||
#include "EventLog.h"
|
||||
#include "HandleWrapper.h"
|
||||
#include "ServerErrorApplication.h"
|
||||
#include "AppOfflineApplication.h"
|
||||
|
||||
APPLICATION_INFO::~APPLICATION_INFO()
|
||||
{
|
||||
ShutDownApplication();
|
||||
ShutDownApplication(/* fServerInitiated */ false);
|
||||
}
|
||||
|
||||
HRESULT
|
||||
APPLICATION_INFO::Initialize(
|
||||
_In_ IHttpApplication &pApplication,
|
||||
HandlerResolver * pHandlerResolver
|
||||
)
|
||||
{
|
||||
m_handlerResolver = pHandlerResolver;
|
||||
RETURN_IF_FAILED(m_struConfigPath.Copy(pApplication.GetAppConfigPath()));
|
||||
RETURN_IF_FAILED(m_struInfoKey.Copy(pApplication.GetApplicationId()));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT
|
||||
APPLICATION_INFO::GetOrCreateApplication(
|
||||
IHttpContext *pHttpContext,
|
||||
|
|
@ -54,6 +39,7 @@ APPLICATION_INFO::GetOrCreateApplication(
|
|||
// Call to wait for application to complete stopping
|
||||
m_pApplication->Stop(/* fServerInitiated */ false);
|
||||
m_pApplication = nullptr;
|
||||
m_pApplicationFactory = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -69,20 +55,13 @@ APPLICATION_INFO::GetOrCreateApplication(
|
|||
}
|
||||
else
|
||||
{
|
||||
STRU struExeLocation;
|
||||
PFN_ASPNETCORE_CREATE_APPLICATION pfnAspNetCoreCreateApplication;
|
||||
FINISHED_IF_FAILED(m_handlerResolver->GetApplicationFactory(httpApplication, struExeLocation, &pfnAspNetCoreCreateApplication));
|
||||
std::array<APPLICATION_PARAMETER, 1> parameters {
|
||||
{"InProcessExeLocation", struExeLocation.QueryStr()}
|
||||
};
|
||||
FINISHED_IF_FAILED(m_handlerResolver.GetApplicationFactory(httpApplication, m_pApplicationFactory));
|
||||
|
||||
LOG_INFO("Creating handler application");
|
||||
IAPPLICATION * newApplication;
|
||||
FINISHED_IF_FAILED(pfnAspNetCoreCreateApplication(
|
||||
FINISHED_IF_FAILED(m_pApplicationFactory->Execute(
|
||||
&m_pServer,
|
||||
&httpApplication,
|
||||
parameters.data(),
|
||||
static_cast<DWORD>(parameters.size()),
|
||||
&newApplication));
|
||||
|
||||
m_pApplication.reset(newApplication);
|
||||
|
|
@ -110,50 +89,17 @@ Finished:
|
|||
return hr;
|
||||
}
|
||||
|
||||
|
||||
VOID
|
||||
APPLICATION_INFO::RecycleApplication()
|
||||
APPLICATION_INFO::ShutDownApplication(bool fServerInitiated)
|
||||
{
|
||||
SRWExclusiveLock lock(m_applicationLock);
|
||||
|
||||
if (m_pApplication)
|
||||
{
|
||||
const auto pApplication = m_pApplication.release();
|
||||
|
||||
HandleWrapper<InvalidHandleTraits> hThread = CreateThread(
|
||||
NULL, // default security attributes
|
||||
0, // default stack size
|
||||
(LPTHREAD_START_ROUTINE)DoRecycleApplication,
|
||||
pApplication, // thread function arguments
|
||||
0, // default creation flags
|
||||
NULL); // receive thread identifier
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DWORD WINAPI
|
||||
APPLICATION_INFO::DoRecycleApplication(
|
||||
LPVOID lpParam)
|
||||
{
|
||||
auto pApplication = std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER>(static_cast<IAPPLICATION*>(lpParam));
|
||||
|
||||
if (pApplication)
|
||||
{
|
||||
// Recycle will call shutdown for out of process
|
||||
pApplication->Stop(/*fServerInitiated*/ false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
VOID
|
||||
APPLICATION_INFO::ShutDownApplication()
|
||||
{
|
||||
SRWExclusiveLock lock(m_applicationLock);
|
||||
|
||||
if (m_pApplication)
|
||||
{
|
||||
m_pApplication ->Stop(/* fServerInitiated */ true);
|
||||
LOG_ERRORF("Stopping application %S", QueryApplicationInfoKey().c_str());
|
||||
m_pApplication ->Stop(fServerInitiated);
|
||||
m_pApplication = nullptr;
|
||||
m_pApplicationFactory = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "hostfxroptions.h"
|
||||
#include "hashtable.h"
|
||||
#include "hashfn.h"
|
||||
#include "aspnetcore_shim_config.h"
|
||||
#include "iapplication.h"
|
||||
#include "SRWSharedLock.h"
|
||||
|
|
@ -19,61 +17,35 @@ class APPLICATION_INFO
|
|||
{
|
||||
public:
|
||||
|
||||
APPLICATION_INFO(IHttpServer &pServer) :
|
||||
APPLICATION_INFO(
|
||||
IHttpServer &pServer,
|
||||
IHttpApplication &pApplication,
|
||||
HandlerResolver &pHandlerResolver
|
||||
) :
|
||||
m_pServer(pServer),
|
||||
m_cRefs(1),
|
||||
m_handlerResolver(nullptr)
|
||||
m_handlerResolver(pHandlerResolver),
|
||||
m_strConfigPath(pApplication.GetAppConfigPath()),
|
||||
m_strInfoKey(pApplication.GetApplicationId())
|
||||
{
|
||||
InitializeSRWLock(&m_applicationLock);
|
||||
}
|
||||
|
||||
PCWSTR
|
||||
QueryApplicationInfoKey()
|
||||
{
|
||||
return m_struInfoKey.QueryStr();
|
||||
}
|
||||
|
||||
STRU*
|
||||
QueryConfigPath()
|
||||
{
|
||||
return &m_struConfigPath;
|
||||
}
|
||||
|
||||
virtual
|
||||
~APPLICATION_INFO();
|
||||
|
||||
static
|
||||
void
|
||||
StaticInitialize()
|
||||
std::wstring&
|
||||
QueryApplicationInfoKey()
|
||||
{
|
||||
return m_strInfoKey;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
Initialize(
|
||||
IHttpApplication &pApplication,
|
||||
HandlerResolver *pHandlerResolver
|
||||
);
|
||||
|
||||
VOID
|
||||
ReferenceApplicationInfo() const
|
||||
std::wstring&
|
||||
QueryConfigPath()
|
||||
{
|
||||
InterlockedIncrement(&m_cRefs);
|
||||
return m_strConfigPath;
|
||||
}
|
||||
|
||||
VOID
|
||||
DereferenceApplicationInfo() const
|
||||
{
|
||||
if (InterlockedDecrement(&m_cRefs) == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
VOID
|
||||
RecycleApplication();
|
||||
|
||||
VOID
|
||||
ShutDownApplication();
|
||||
ShutDownApplication(bool fServerInitiated);
|
||||
|
||||
HRESULT
|
||||
GetOrCreateApplication(
|
||||
|
|
@ -81,88 +53,30 @@ public:
|
|||
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER>& pApplication
|
||||
);
|
||||
|
||||
bool ConfigurationPathApplies(const std::wstring& path)
|
||||
{
|
||||
// We need to check that the last character of the config path
|
||||
// is either a null terminator or a slash.
|
||||
// This checks the case where the config path was
|
||||
// MACHINE/WEBROOT/site and your site path is MACHINE/WEBROOT/siteTest
|
||||
auto const changed = m_strConfigPath._Starts_with(path);
|
||||
if (changed)
|
||||
{
|
||||
const auto lastChar = m_strConfigPath[m_strConfigPath.length()];
|
||||
return lastChar == L'\0' || lastChar == L'/';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
static DWORD WINAPI DoRecycleApplication(LPVOID lpParam);
|
||||
|
||||
mutable LONG m_cRefs;
|
||||
STRU m_struConfigPath;
|
||||
STRU m_struInfoKey;
|
||||
SRWLOCK m_applicationLock;
|
||||
IHttpServer &m_pServer;
|
||||
HandlerResolver *m_handlerResolver;
|
||||
HandlerResolver &m_handlerResolver;
|
||||
|
||||
std::wstring m_strConfigPath;
|
||||
std::wstring m_strInfoKey;
|
||||
SRWLOCK m_applicationLock {};
|
||||
|
||||
std::unique_ptr<ApplicationFactory> m_pApplicationFactory;
|
||||
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER> m_pApplication;
|
||||
};
|
||||
|
||||
class APPLICATION_INFO_HASH :
|
||||
public HASH_TABLE<APPLICATION_INFO, PCWSTR>
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
APPLICATION_INFO_HASH()
|
||||
{}
|
||||
|
||||
PCWSTR
|
||||
ExtractKey(
|
||||
APPLICATION_INFO *pApplicationInfo
|
||||
)
|
||||
{
|
||||
return pApplicationInfo->QueryApplicationInfoKey();
|
||||
}
|
||||
|
||||
DWORD
|
||||
CalcKeyHash(
|
||||
PCWSTR pszApplicationId
|
||||
)
|
||||
{
|
||||
return HashStringNoCase(pszApplicationId);
|
||||
}
|
||||
|
||||
BOOL
|
||||
EqualKeys(
|
||||
PCWSTR pszKey1,
|
||||
PCWSTR pszKey2
|
||||
)
|
||||
{
|
||||
return CompareStringOrdinal(pszKey1,
|
||||
-1,
|
||||
pszKey2,
|
||||
-1,
|
||||
TRUE) == CSTR_EQUAL;
|
||||
}
|
||||
|
||||
VOID
|
||||
ReferenceRecord(
|
||||
APPLICATION_INFO *pApplicationInfo
|
||||
)
|
||||
{
|
||||
pApplicationInfo->ReferenceApplicationInfo();
|
||||
}
|
||||
|
||||
VOID
|
||||
DereferenceRecord(
|
||||
APPLICATION_INFO *pApplicationInfo
|
||||
)
|
||||
{
|
||||
pApplicationInfo->DereferenceApplicationInfo();
|
||||
}
|
||||
|
||||
static
|
||||
VOID
|
||||
ReferenceCopyToTable(
|
||||
APPLICATION_INFO * pEntry,
|
||||
PVOID pvData
|
||||
)
|
||||
{
|
||||
APPLICATION_INFO_HASH *pHash = static_cast<APPLICATION_INFO_HASH *>(pvData);
|
||||
DBG_ASSERT(pHash);
|
||||
pHash->InsertRecord(pEntry);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
APPLICATION_INFO_HASH(const APPLICATION_INFO_HASH &);
|
||||
void operator=(const APPLICATION_INFO_HASH &);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,25 +20,16 @@ APPLICATION_MANAGER* APPLICATION_MANAGER::sm_pApplicationManager = NULL;
|
|||
//
|
||||
HRESULT
|
||||
APPLICATION_MANAGER::GetOrCreateApplicationInfo(
|
||||
_In_ IHttpContext* pHttpContext,
|
||||
_Out_ APPLICATION_INFO ** ppApplicationInfo
|
||||
_In_ IHttpContext& pHttpContext,
|
||||
_Out_ std::shared_ptr<APPLICATION_INFO>& ppApplicationInfo
|
||||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
APPLICATION_INFO *pApplicationInfo = NULL;
|
||||
PCWSTR pszApplicationId = NULL;
|
||||
|
||||
STACK_STRU ( strEventMsg, 256 );
|
||||
|
||||
DBG_ASSERT(pHttpContext);
|
||||
DBG_ASSERT(ppApplicationInfo);
|
||||
|
||||
*ppApplicationInfo = NULL;
|
||||
IHttpApplication &pApplication = *pHttpContext->GetApplication();
|
||||
auto &pApplication = *pHttpContext.GetApplication();
|
||||
|
||||
// The configuration path is unique for each application and is used for the
|
||||
// key in the applicationInfoHash.
|
||||
pszApplicationId = pApplication.GetApplicationId();
|
||||
std::wstring pszApplicationId = pApplication.GetApplicationId();
|
||||
|
||||
{
|
||||
// When accessing the m_pApplicationInfoHash, we need to acquire the application manager
|
||||
|
|
@ -47,93 +38,41 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo(
|
|||
|
||||
if (g_fInShutdown)
|
||||
{
|
||||
FINISHED(HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS));
|
||||
return HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS);
|
||||
}
|
||||
|
||||
m_pApplicationInfoHash->FindKey(pszApplicationId, ppApplicationInfo);
|
||||
const auto pair = m_pApplicationInfoHash.find(pszApplicationId);
|
||||
if (pair != m_pApplicationInfoHash.end())
|
||||
{
|
||||
ppApplicationInfo = pair->second;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// It's important to release read lock here so exclusive lock
|
||||
// can be reacquired later as SRW lock doesn't allow upgrades
|
||||
}
|
||||
|
||||
if (*ppApplicationInfo == NULL)
|
||||
// Take exclusive lock before creating the application
|
||||
SRWExclusiveLock writeLock(m_srwLock);
|
||||
|
||||
if (!m_fDebugInitialize)
|
||||
{
|
||||
// Take exclusive lock before creating the application
|
||||
SRWExclusiveLock writeLock(m_srwLock);
|
||||
|
||||
if (!m_fDebugInitialize)
|
||||
{
|
||||
DebugInitializeFromConfig(m_pHttpServer, pApplication);
|
||||
m_fDebugInitialize = TRUE;
|
||||
}
|
||||
|
||||
// Check if other thread created the application
|
||||
m_pApplicationInfoHash->FindKey(pszApplicationId, ppApplicationInfo);
|
||||
if (*ppApplicationInfo != NULL)
|
||||
{
|
||||
FINISHED(S_OK);
|
||||
}
|
||||
|
||||
pApplicationInfo = new APPLICATION_INFO(m_pHttpServer);
|
||||
|
||||
FINISHED_IF_FAILED(pApplicationInfo->Initialize(pApplication, &m_handlerResolver));
|
||||
FINISHED_IF_FAILED(m_pApplicationInfoHash->InsertRecord(pApplicationInfo));
|
||||
|
||||
*ppApplicationInfo = pApplicationInfo;
|
||||
pApplicationInfo = NULL;
|
||||
}
|
||||
Finished:
|
||||
|
||||
if (pApplicationInfo != NULL)
|
||||
{
|
||||
pApplicationInfo->DereferenceApplicationInfo();
|
||||
pApplicationInfo = NULL;
|
||||
DebugInitializeFromConfig(m_pHttpServer, pApplication);
|
||||
m_fDebugInitialize = TRUE;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// If the application's configuration was changed,
|
||||
// append the configuration path to the config change context.
|
||||
//
|
||||
BOOL
|
||||
APPLICATION_MANAGER::FindConfigChangedApplication(
|
||||
_In_ APPLICATION_INFO * pEntry,
|
||||
_In_ PVOID pvContext)
|
||||
{
|
||||
DBG_ASSERT(pEntry);
|
||||
DBG_ASSERT(pvContext);
|
||||
|
||||
// Config Change context contains the original config path that changed
|
||||
// and a multiStr containing
|
||||
CONFIG_CHANGE_CONTEXT* pContext = static_cast<CONFIG_CHANGE_CONTEXT*>(pvContext);
|
||||
STRU* pstruConfigPath = pEntry->QueryConfigPath();
|
||||
|
||||
// check if the application path contains our app/subapp by seeing if the config path
|
||||
// starts with the notification path.
|
||||
BOOL fChanged = pstruConfigPath->StartsWith(pContext->pstrPath, true);
|
||||
if (fChanged)
|
||||
// Check if other thread created the application
|
||||
const auto pair = m_pApplicationInfoHash.find(pszApplicationId);
|
||||
if (pair != m_pApplicationInfoHash.end())
|
||||
{
|
||||
auto dwLen = wcslen(pContext->pstrPath);
|
||||
WCHAR wChar = pstruConfigPath->QueryStr()[dwLen];
|
||||
|
||||
// We need to check that the last character of the config path
|
||||
// is either a null terminator or a slash.
|
||||
// This checks the case where the config path was
|
||||
// MACHINE/WEBROOT/site and your site path is MACHINE/WEBROOT/siteTest
|
||||
if (wChar != L'\0' && wChar != L'/')
|
||||
{
|
||||
// not current app or sub app
|
||||
fChanged = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
pContext->MultiSz.Append(pEntry->QueryApplicationInfoKey());
|
||||
}
|
||||
ppApplicationInfo = pair->second;
|
||||
return S_OK;
|
||||
}
|
||||
return fChanged;
|
||||
|
||||
ppApplicationInfo = std::make_shared<APPLICATION_INFO>(m_pHttpServer, pApplication, m_handlerResolver);
|
||||
m_pApplicationInfoHash.emplace(pszApplicationId, ppApplicationInfo);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -148,101 +87,82 @@ APPLICATION_MANAGER::RecycleApplicationFromManager(
|
|||
_In_ LPCWSTR pszApplicationId
|
||||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
DWORD dwPreviousCounter = 0;
|
||||
APPLICATION_INFO_HASH* table = NULL;
|
||||
CONFIG_CHANGE_CONTEXT context;
|
||||
|
||||
if (g_fInShutdown)
|
||||
try
|
||||
{
|
||||
// We are already shutting down, ignore this event as a global configuration change event
|
||||
// can occur after global stop listening for some reason.
|
||||
return hr;
|
||||
}
|
||||
std::vector<std::shared_ptr<APPLICATION_INFO>> applicationsToRecycle;
|
||||
|
||||
{
|
||||
SRWExclusiveLock lock(m_srwLock);
|
||||
if (g_fInShutdown)
|
||||
{
|
||||
return hr;
|
||||
// We are already shutting down, ignore this event as a global configuration change event
|
||||
// can occur after global stop listening for some reason.
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Make a shallow copy of existing hashtable as we may need to remove nodes
|
||||
// This will be used for finding differences in which applications are affected by a config change.
|
||||
table = new APPLICATION_INFO_HASH();
|
||||
|
||||
// few application expected, small bucket size for hash table
|
||||
if (FAILED(hr = table->Initialize(17 /*prime*/)))
|
||||
{
|
||||
goto Finished;
|
||||
SRWExclusiveLock lock(m_srwLock);
|
||||
if (g_fInShutdown)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
const std::wstring configurationPath = pszApplicationId;
|
||||
|
||||
auto itr = m_pApplicationInfoHash.begin();
|
||||
while (itr != m_pApplicationInfoHash.end())
|
||||
{
|
||||
if (itr->second->ConfigurationPathApplies(configurationPath))
|
||||
{
|
||||
applicationsToRecycle.emplace_back(itr->second);
|
||||
itr = m_pApplicationInfoHash.erase(itr);
|
||||
}
|
||||
else
|
||||
{
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
// All applications were unloaded reset handler resolver validation logic
|
||||
if (m_pApplicationInfoHash.empty())
|
||||
{
|
||||
m_handlerResolver.ResetHostingModel();
|
||||
}
|
||||
}
|
||||
|
||||
context.pstrPath = pszApplicationId;
|
||||
// If we receive a request at this point.
|
||||
// OutOfProcess: we will create a new application with new configuration
|
||||
// InProcess: the request would have to be rejected, as we are about to call g_HttpServer->RecycleProcess
|
||||
// on the worker proocess
|
||||
|
||||
// Keep track of the preview count of applications to know whether there are applications to delete
|
||||
dwPreviousCounter = m_pApplicationInfoHash->Count();
|
||||
|
||||
// We don't want to hold the application manager lock for long time as it will block all incoming requests
|
||||
// Don't call application shutdown inside the lock
|
||||
m_pApplicationInfoHash->Apply(APPLICATION_INFO_HASH::ReferenceCopyToTable, static_cast<PVOID>(table));
|
||||
DBG_ASSERT(dwPreviousCounter == table->Count());
|
||||
|
||||
// Removed the applications which are impacted by the configurtion change
|
||||
m_pApplicationInfoHash->DeleteIf(FindConfigChangedApplication, (PVOID)&context);
|
||||
}
|
||||
|
||||
// If we receive a request at this point.
|
||||
// OutOfProcess: we will create a new application with new configuration
|
||||
// InProcess: the request would have to be rejected, as we are about to call g_HttpServer->RecycleProcess
|
||||
// on the worker proocess
|
||||
if (!context.MultiSz.IsEmpty())
|
||||
{
|
||||
PCWSTR path = context.MultiSz.First();
|
||||
// Iterate through each of the paths that were shut down,
|
||||
// calling RecycleApplication on each of them.
|
||||
while (path != NULL)
|
||||
if (!applicationsToRecycle.empty())
|
||||
{
|
||||
APPLICATION_INFO* pRecord;
|
||||
for (auto& application : applicationsToRecycle)
|
||||
{
|
||||
try
|
||||
{
|
||||
application->ShutDownApplication(/* fServerInitiated */ false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_ERRORF("Failed to stop application %S", application->QueryApplicationInfoKey().c_str());
|
||||
OBSERVE_CAUGHT_EXCEPTION();
|
||||
|
||||
// Application got recycled. Log an event
|
||||
EventLog::Info(
|
||||
ASPNETCORE_EVENT_RECYCLE_CONFIGURATION,
|
||||
ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG,
|
||||
path);
|
||||
|
||||
table->FindKey(path, &pRecord);
|
||||
DBG_ASSERT(pRecord != NULL);
|
||||
|
||||
// RecycleApplication is called on a separate thread.
|
||||
pRecord->RecycleApplication();
|
||||
pRecord->DereferenceApplicationInfo();
|
||||
path = context.MultiSz.Next(path);
|
||||
// Failed to recycle an application. Log an event
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_RECYCLE_APP_FAILURE,
|
||||
ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG,
|
||||
pszApplicationId);
|
||||
// Need to recycle the process as we cannot recycle the application
|
||||
if (!g_fRecycleProcessCalled)
|
||||
{
|
||||
g_fRecycleProcessCalled = TRUE;
|
||||
m_pHttpServer.RecycleProcess(L"AspNetCore Recycle Process on Demand Due Application Recycle Error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
Finished:
|
||||
if (table != NULL)
|
||||
{
|
||||
table->Clear();
|
||||
delete table;
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Failed to recycle an application. Log an event
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_RECYCLE_APP_FAILURE,
|
||||
ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG,
|
||||
pszApplicationId);
|
||||
// Need to recycle the process as we cannot recycle the application
|
||||
if (!g_fRecycleProcessCalled)
|
||||
{
|
||||
g_fRecycleProcessCalled = TRUE;
|
||||
m_pHttpServer.RecycleProcess(L"AspNetCore Recycle Process on Demand Due Application Recycle Error");
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -256,34 +176,12 @@ APPLICATION_MANAGER::ShutDown()
|
|||
// However, it is possible to receive multiple OnGlobalStopListening events
|
||||
// Protect against this by checking if we already shut down.
|
||||
g_fInShutdown = TRUE;
|
||||
if (m_pApplicationInfoHash != NULL)
|
||||
|
||||
// During shutdown we lock until we delete the application
|
||||
SRWExclusiveLock lock(m_srwLock);
|
||||
for (auto &pair : m_pApplicationInfoHash)
|
||||
{
|
||||
DBG_ASSERT(m_pApplicationInfoHash);
|
||||
|
||||
// During shutdown we lock until we delete the application
|
||||
SRWExclusiveLock lock(m_srwLock);
|
||||
|
||||
// Call shutdown on each application in the application manager
|
||||
m_pApplicationInfoHash->Apply(ShutdownApplication, NULL);
|
||||
m_pApplicationInfoHash->Clear();
|
||||
delete m_pApplicationInfoHash;
|
||||
m_pApplicationInfoHash = NULL;
|
||||
pair.second->ShutDownApplication(/* fServerInitiated */ true);
|
||||
pair.second = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Calls shutdown on each application. ApplicationManager's lock is held for duration of
|
||||
// each shutdown call, guaranteeing another application cannot be created.
|
||||
//
|
||||
// static
|
||||
VOID
|
||||
APPLICATION_MANAGER::ShutdownApplication(
|
||||
_In_ APPLICATION_INFO * pEntry,
|
||||
_In_ PVOID pvContext
|
||||
)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(pvContext);
|
||||
DBG_ASSERT(pEntry != NULL);
|
||||
|
||||
pEntry->ShutDownApplication();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "applicationinfo.h"
|
||||
#include "multisz.h"
|
||||
#include "exceptions.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#define DEFAULT_HASH_BUCKETS 17
|
||||
|
||||
|
|
@ -15,11 +16,6 @@
|
|||
// Should always call GetInstance to get the object instance
|
||||
//
|
||||
|
||||
struct CONFIG_CHANGE_CONTEXT
|
||||
{
|
||||
PCWSTR pstrPath;
|
||||
MULTISZ MultiSz;
|
||||
};
|
||||
|
||||
class APPLICATION_MANAGER
|
||||
{
|
||||
|
|
@ -35,9 +31,7 @@ public:
|
|||
|
||||
static
|
||||
VOID
|
||||
Cleanup(
|
||||
VOID
|
||||
)
|
||||
Cleanup()
|
||||
{
|
||||
if(sm_pApplicationManager != NULL)
|
||||
{
|
||||
|
|
@ -46,24 +40,10 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
static
|
||||
BOOL
|
||||
FindConfigChangedApplication(
|
||||
_In_ APPLICATION_INFO * pEntry,
|
||||
_In_ PVOID pvContext
|
||||
);
|
||||
|
||||
static
|
||||
VOID
|
||||
ShutdownApplication(
|
||||
_In_ APPLICATION_INFO * pEntry,
|
||||
_In_ PVOID pvContext
|
||||
);
|
||||
|
||||
HRESULT
|
||||
GetOrCreateApplicationInfo(
|
||||
_In_ IHttpContext* pHttpContext,
|
||||
_Out_ APPLICATION_INFO ** ppApplicationInfo
|
||||
_In_ IHttpContext& pHttpContext,
|
||||
_Out_ std::shared_ptr<APPLICATION_INFO>& ppApplicationInfo
|
||||
);
|
||||
|
||||
HRESULT
|
||||
|
|
@ -74,40 +54,13 @@ public:
|
|||
VOID
|
||||
ShutDown();
|
||||
|
||||
~APPLICATION_MANAGER()
|
||||
{
|
||||
|
||||
if(m_pApplicationInfoHash != NULL)
|
||||
{
|
||||
m_pApplicationInfoHash->Clear();
|
||||
delete m_pApplicationInfoHash;
|
||||
m_pApplicationInfoHash = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT StaticInitialize(HMODULE hModule, IHttpServer& pHttpServer)
|
||||
{
|
||||
assert(!sm_pApplicationManager);
|
||||
sm_pApplicationManager = new APPLICATION_MANAGER(hModule, pHttpServer);
|
||||
RETURN_IF_FAILED(sm_pApplicationManager->Initialize());
|
||||
|
||||
APPLICATION_INFO::StaticInitialize();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Initialize()
|
||||
{
|
||||
if(m_pApplicationInfoHash == NULL)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_pApplicationInfoHash = new APPLICATION_INFO_HASH();
|
||||
}
|
||||
CATCH_RETURN();
|
||||
RETURN_IF_FAILED(m_pApplicationInfoHash->Initialize(DEFAULT_HASH_BUCKETS));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
APPLICATION_MANAGER(HMODULE hModule, IHttpServer& pHttpServer) :
|
||||
|
|
@ -119,7 +72,7 @@ private:
|
|||
InitializeSRWLock(&m_srwLock);
|
||||
}
|
||||
|
||||
APPLICATION_INFO_HASH *m_pApplicationInfoHash;
|
||||
std::unordered_map<std::wstring, std::shared_ptr<APPLICATION_INFO>> m_pApplicationInfoHash;
|
||||
static APPLICATION_MANAGER *sm_pApplicationManager;
|
||||
SRWLOCK m_srwLock {};
|
||||
BOOL m_fDebugInitialize;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ ASPNETCORE_SHIM_CONFIG::Populate(
|
|||
)
|
||||
{
|
||||
STACK_STRU(strHostingModel, 12);
|
||||
STRU strApplicationFullPath;
|
||||
CComPtr<IAppHostElement> pAspNetCoreElement;
|
||||
|
||||
IAppHostAdminManager *pAdminManager = pHttpServer->GetAdminManager();
|
||||
|
|
@ -25,9 +24,11 @@ ASPNETCORE_SHIM_CONFIG::Populate(
|
|||
applicationConfigPath,
|
||||
&pAspNetCoreElement));
|
||||
|
||||
CComBSTR struProcessPath;
|
||||
RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement,
|
||||
CS_ASPNETCORE_PROCESS_EXE_PATH,
|
||||
&m_struProcessPath));
|
||||
&struProcessPath));
|
||||
m_strProcessPath = struProcessPath;
|
||||
|
||||
// Swallow this error for backward compatibility
|
||||
// Use default behavior for empty string
|
||||
|
|
@ -53,13 +54,18 @@ ASPNETCORE_SHIM_CONFIG::Populate(
|
|||
RETURN_HR(HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
|
||||
}
|
||||
|
||||
CComBSTR struArguments;
|
||||
RETURN_IF_FAILED(GetElementStringProperty(pAspNetCoreElement,
|
||||
CS_ASPNETCORE_PROCESS_ARGUMENTS,
|
||||
&m_struArguments));
|
||||
&struArguments));
|
||||
|
||||
m_strArguments = struArguments;
|
||||
|
||||
if (m_hostingModel == HOSTING_OUT_PROCESS)
|
||||
{
|
||||
RETURN_IF_FAILED(ConfigUtility::FindHandlerVersion(pAspNetCoreElement, m_struHandlerVersion));
|
||||
STRU struHandlerVersion;
|
||||
RETURN_IF_FAILED(ConfigUtility::FindHandlerVersion(pAspNetCoreElement, struHandlerVersion));
|
||||
m_strHandlerVersion = struHandlerVersion.QueryStr();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
#include <httpserv.h>
|
||||
|
||||
#include "stringu.h"
|
||||
|
||||
#define CS_ASPNETCORE_SECTION L"system.webServer/aspNetCore"
|
||||
#define CS_ASPNETCORE_PROCESS_EXE_PATH L"processPath"
|
||||
#define CS_ASPNETCORE_PROCESS_ARGUMENTS L"arguments"
|
||||
|
|
@ -34,16 +33,16 @@ public:
|
|||
IHttpApplication *pHttpApplication
|
||||
);
|
||||
|
||||
STRU*
|
||||
std::wstring&
|
||||
QueryProcessPath()
|
||||
{
|
||||
return &m_struProcessPath;
|
||||
return m_strProcessPath;
|
||||
}
|
||||
|
||||
STRU*
|
||||
std::wstring&
|
||||
QueryArguments()
|
||||
{
|
||||
return &m_struArguments;
|
||||
return m_strArguments;
|
||||
}
|
||||
|
||||
APP_HOSTING_MODEL
|
||||
|
|
@ -52,10 +51,10 @@ public:
|
|||
return m_hostingModel;
|
||||
}
|
||||
|
||||
STRU*
|
||||
std::wstring&
|
||||
QueryHandlerVersion()
|
||||
{
|
||||
return &m_struHandlerVersion;
|
||||
return m_strHandlerVersion;
|
||||
}
|
||||
|
||||
BOOL
|
||||
|
|
@ -77,10 +76,10 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
STRU m_struArguments;
|
||||
STRU m_struProcessPath;
|
||||
APP_HOSTING_MODEL m_hostingModel;
|
||||
STRU m_struHandlerVersion;
|
||||
std::wstring m_strArguments;
|
||||
std::wstring m_strProcessPath;
|
||||
APP_HOSTING_MODEL m_hostingModel;
|
||||
std::wstring m_strHandlerVersion;
|
||||
BOOL m_fStdoutLogEnabled;
|
||||
STRU m_struStdoutLogFile;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,19 +51,10 @@ Return value:
|
|||
}
|
||||
|
||||
ASPNET_CORE_PROXY_MODULE::ASPNET_CORE_PROXY_MODULE(
|
||||
) : m_pApplicationInfo(nullptr), m_pApplication(nullptr), m_pHandler(nullptr)
|
||||
) : m_pApplicationInfo(nullptr), m_pHandler(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ASPNET_CORE_PROXY_MODULE::~ASPNET_CORE_PROXY_MODULE()
|
||||
{
|
||||
if (m_pApplicationInfo != NULL)
|
||||
{
|
||||
m_pApplicationInfo->DereferenceApplicationInfo();
|
||||
m_pApplicationInfo = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
__override
|
||||
REQUEST_NOTIFICATION_STATUS
|
||||
ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
|
||||
|
|
@ -72,9 +63,7 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
|
|||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
APPLICATION_MANAGER *pApplicationManager = NULL;
|
||||
REQUEST_NOTIFICATION_STATUS retVal = RQ_NOTIFICATION_CONTINUE;
|
||||
STRU struExeLocation;
|
||||
try
|
||||
{
|
||||
|
||||
|
|
@ -83,13 +72,11 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
|
|||
FINISHED(HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS));
|
||||
}
|
||||
|
||||
pApplicationManager = APPLICATION_MANAGER::GetInstance();
|
||||
auto pApplicationManager = APPLICATION_MANAGER::GetInstance();
|
||||
|
||||
FINISHED_IF_FAILED(pApplicationManager->GetOrCreateApplicationInfo(
|
||||
pHttpContext,
|
||||
&m_pApplicationInfo));
|
||||
|
||||
DBG_ASSERT(pHttpContext);
|
||||
*pHttpContext,
|
||||
m_pApplicationInfo));
|
||||
|
||||
std::unique_ptr<IAPPLICATION, IAPPLICATION_DELETER> pApplication;
|
||||
FINISHED_IF_FAILED(m_pApplicationInfo->GetOrCreateApplication(pHttpContext, pApplication));
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class ASPNET_CORE_PROXY_MODULE : public CHttpModule
|
|||
|
||||
ASPNET_CORE_PROXY_MODULE();
|
||||
|
||||
~ASPNET_CORE_PROXY_MODULE();
|
||||
~ASPNET_CORE_PROXY_MODULE() = default;
|
||||
|
||||
void * operator new(size_t size, IModuleAllocator * pPlacement)
|
||||
{
|
||||
|
|
@ -46,9 +46,7 @@ class ASPNET_CORE_PROXY_MODULE : public CHttpModule
|
|||
);
|
||||
|
||||
private:
|
||||
|
||||
APPLICATION_INFO *m_pApplicationInfo;
|
||||
IAPPLICATION *m_pApplication;
|
||||
std::shared_ptr<APPLICATION_INFO> m_pApplicationInfo;
|
||||
std::unique_ptr<IREQUEST_HANDLER, IREQUEST_HANDLER_DELETER> m_pHandler;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,13 @@ public:
|
|||
|
||||
HandleType* operator&() { return &m_handle; }
|
||||
|
||||
HandleType release() noexcept
|
||||
{
|
||||
auto value = m_handle;
|
||||
m_handle = traits::DefaultHandle;
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
HandleType m_handle;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ void SetDebugFlags(const std::wstring &debugValue)
|
|||
{
|
||||
try
|
||||
{
|
||||
if (!debugValue.empty())
|
||||
if (!debugValue.empty() && debugValue.find_first_not_of(L"0123456789") == std::wstring::npos)
|
||||
{
|
||||
const auto value = std::stoi(debugValue);
|
||||
|
||||
|
|
|
|||
|
|
@ -165,34 +165,3 @@ FILE_UTILITY::EnsureDirectoryPathExist(
|
|||
Finished:
|
||||
return hr;
|
||||
}
|
||||
|
||||
BOOL
|
||||
FILE_UTILITY::CheckIfFileExists(
|
||||
_In_ PCWSTR pszFilePath
|
||||
)
|
||||
{
|
||||
HANDLE hFileHandle = INVALID_HANDLE_VALUE;
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
BOOL fFileExists = FALSE;
|
||||
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = TRUE;
|
||||
saAttr.lpSecurityDescriptor = NULL;
|
||||
|
||||
hFileHandle = CreateFile(pszFilePath,
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
&saAttr,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
fFileExists = hFileHandle != INVALID_HANDLE_VALUE || GetLastError() == ERROR_SHARING_VIOLATION;
|
||||
|
||||
if (fFileExists)
|
||||
{
|
||||
CloseHandle(hFileHandle);
|
||||
}
|
||||
|
||||
return fFileExists;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,12 +25,6 @@ public:
|
|||
EnsureDirectoryPathExist(
|
||||
_In_ LPCWSTR pszPath
|
||||
);
|
||||
|
||||
static
|
||||
BOOL
|
||||
CheckIfFileExists(
|
||||
PCWSTR pszFilePath
|
||||
);
|
||||
private:
|
||||
static
|
||||
HRESULT
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ enum APPLICATION_STATUS
|
|||
|
||||
struct APPLICATION_PARAMETER
|
||||
{
|
||||
LPCSTR pzName;
|
||||
PVOID pValue;
|
||||
LPCSTR pzName;
|
||||
const void *pValue;
|
||||
};
|
||||
|
||||
class IAPPLICATION
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ EnsureOutOfProcessInitializtion()
|
|||
FINISHED(S_OK);
|
||||
}
|
||||
|
||||
g_fOutOfProcessInitialize = TRUE;
|
||||
|
||||
g_hWinHttpModule = GetModuleHandle(TEXT("winhttp.dll"));
|
||||
|
||||
g_hAspNetCoreModule = GetModuleHandle(TEXT("aspnetcorev2.dll"));
|
||||
|
|
|
|||
|
|
@ -1,12 +1,18 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||
|
|
@ -14,6 +20,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
[Collection(PublishedSitesCollection.Name)]
|
||||
public class ConfigurationChangeTests : IISFunctionalTestBase
|
||||
{
|
||||
private static readonly TimeSpan RetryDelay = TimeSpan.FromMilliseconds(100);
|
||||
private readonly PublishedSitesFixture _fixture;
|
||||
|
||||
public ConfigurationChangeTests(PublishedSitesFixture fixture)
|
||||
|
|
@ -66,13 +73,54 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
await deploymentResult.AssertStarts();
|
||||
|
||||
deploymentResult.ModifyWebConfig(element => element
|
||||
.GetOrAdd("system.webServer")
|
||||
.Descendants("system.webServer")
|
||||
.Single()
|
||||
.GetOrAdd("aspNetCore")
|
||||
.SetAttributeValue("hostingModel", "inprocess"));
|
||||
|
||||
// Have to retry here to allow ANCM to receive notification and react to it
|
||||
// Verify that worker process gets restarted with new process id
|
||||
// Verify that inprocess application was created and tried to start
|
||||
await deploymentResult.HttpClient.RetryRequestAsync("/HelloWorld", r => r.StatusCode == HttpStatusCode.InternalServerError);
|
||||
|
||||
StopServer();
|
||||
EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, "Could not find the assembly 'aspnetcorev2_inprocess.dll'");
|
||||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[InlineData(HostingModel.InProcess)]
|
||||
[InlineData(HostingModel.OutOfProcess)]
|
||||
public async Task ConfigurationTouchedStress(HostingModel hostingModel)
|
||||
{
|
||||
var deploymentResult = await DeployAsync(_fixture.GetBaseDeploymentParameters(hostingModel, publish: true));
|
||||
|
||||
await deploymentResult.AssertStarts();
|
||||
var load = Helpers.StressLoad(deploymentResult.HttpClient, "/HelloWorld", response => {
|
||||
var statusCode = (int)response.StatusCode;
|
||||
Assert.True(statusCode == 200 || statusCode == 503, "Status code was " + statusCode);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
// ModifyWebConfig might fail if web.config is being read by IIS
|
||||
RetryHelper.RetryOperation(
|
||||
() => deploymentResult.ModifyWebConfig(element => {}),
|
||||
e => Logger.LogError($"Failed to touch web.config : {e.Message}"),
|
||||
retryCount: 3,
|
||||
retryDelayMilliseconds: RetryDelay.Milliseconds);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await load;
|
||||
}
|
||||
catch (HttpRequestException ex) when (ex.InnerException is IOException | ex.InnerException is SocketException)
|
||||
{
|
||||
// IOException in InProcess is fine, just means process stopped
|
||||
if (hostingModel != HostingModel.InProcess)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,11 +98,11 @@ TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
|
|||
|
||||
if (is64Bit)
|
||||
{
|
||||
fDotnetInProgramFiles = FILE_UTILITY::CheckIfFileExists(L"C:/Program Files/dotnet/dotnet.exe");
|
||||
fDotnetInProgramFiles = std::filesystem::is_regular_file(L"C:/Program Files/dotnet/dotnet.exe");
|
||||
}
|
||||
else
|
||||
{
|
||||
fDotnetInProgramFiles = FILE_UTILITY::CheckIfFileExists(L"C:/Program Files (x86)/dotnet/dotnet.exe");
|
||||
fDotnetInProgramFiles = std::filesystem::is_regular_file(L"C:/Program Files (x86)/dotnet/dotnet.exe");
|
||||
}
|
||||
|
||||
auto dotnetPath = HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles();
|
||||
|
|
|
|||
Loading…
Reference in New Issue