Cleanup application manager (#1250)

This commit is contained in:
Pavel Krymets 2018-08-22 08:23:40 -07:00 committed by GitHub
parent e23d9cb4ac
commit e26231b613
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 394 additions and 630 deletions

View File

@ -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;
};

View File

@ -227,6 +227,7 @@
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="ApplicationFactory.h" />
<ClInclude Include="applicationinfo.h" />
<ClInclude Include="AppOfflineApplication.h" />
<ClInclude Include="AppOfflineHandler.h" />

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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;
}
}

View File

@ -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 &);
};

View File

@ -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();
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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;
};

View File

@ -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));

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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);

View File

@ -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;
}

View File

@ -25,12 +25,6 @@ public:
EnsureDirectoryPathExist(
_In_ LPCWSTR pszPath
);
static
BOOL
CheckIfFileExists(
PCWSTR pszFilePath
);
private:
static
HRESULT

View File

@ -14,8 +14,8 @@ enum APPLICATION_STATUS
struct APPLICATION_PARAMETER
{
LPCSTR pzName;
PVOID pValue;
LPCSTR pzName;
const void *pValue;
};
class IAPPLICATION

View File

@ -146,6 +146,8 @@ EnsureOutOfProcessInitializtion()
FINISHED(S_OK);
}
g_fOutOfProcessInitialize = TRUE;
g_hWinHttpModule = GetModuleHandle(TEXT("winhttp.dll"));
g_hAspNetCoreModule = GetModuleHandle(TEXT("aspnetcorev2.dll"));

View File

@ -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;
}
}
}
}
}

View File

@ -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();