Merge pull request #1292 from dotnet-maestro-bot/merge/release/2.2-to-master
[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
commit
be5fb58c76
|
|
@ -48,7 +48,7 @@
|
|||
</Target>
|
||||
|
||||
<!-- Deps file injection-->
|
||||
<ItemGroup>
|
||||
<ItemGroup Condition="('$(InProcessTestSite)' == 'true') AND ('$(ReferenceTestTasks)' != 'false')">
|
||||
<ProjectReference Include="$(MSBuildThisFileDirectory)..\test\TestTasks\TestTasks.csproj">
|
||||
<ReferenceOutputAssembly>False</ReferenceOutputAssembly>
|
||||
</ProjectReference>
|
||||
|
|
|
|||
|
|
@ -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->GetDotnetExeLocation();
|
||||
|
||||
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().c_str()));
|
||||
|
||||
auto pFnHostFxrSearchDirectories = reinterpret_cast<hostfxr_get_native_search_directories_fn>(GetProcAddress(m_hHostFxrDll, "hostfxr_get_native_search_directories"));
|
||||
if (pFnHostFxrSearchDirectories == nullptr)
|
||||
|
|
@ -225,20 +233,24 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
|
|||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_GENERAL_ERROR,
|
||||
ASPNETCORE_EVENT_HOSTFXR_DLL_INVALID_VERSION_MSG,
|
||||
hostfxrOptions->GetHostFxrLocation()
|
||||
hostfxrOptions.GetHostFxrLocation().c_str()
|
||||
);
|
||||
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(),
|
||||
DWORD hostfxrArgc;
|
||||
std::unique_ptr<PCWSTR[]> hostfxrArgv;
|
||||
|
||||
hostfxrOptions.GetArguments(hostfxrArgc, hostfxrArgv);
|
||||
const auto intHostFxrExitCode = pFnHostFxrSearchDirectories(
|
||||
hostfxrArgc,
|
||||
hostfxrArgv.get(),
|
||||
struNativeSearchPaths.data(),
|
||||
dwBufferSize,
|
||||
&dwRequiredBufferSize
|
||||
);
|
||||
|
|
@ -251,7 +263,7 @@ HandlerResolver::FindNativeAssemblyFromHostfxr(
|
|||
{
|
||||
dwBufferSize = dwRequiredBufferSize + 1; // for null terminator
|
||||
|
||||
RETURN_IF_FAILED(struNativeSearchPaths.Resize(dwBufferSize));
|
||||
struNativeSearchPaths.resize(dwBufferSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -260,26 +272,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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@
|
|||
<ClInclude Include="SRWExclusiveLock.h" />
|
||||
<ClInclude Include="SRWSharedLock.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="StringHelpers.h" />
|
||||
<ClInclude Include="sttimer.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
@ -243,6 +244,7 @@
|
|||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StringHelpers.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IISLib\IISLib.vcxproj">
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@
|
|||
|
||||
// Workaround for VC++ bug https://developercommunity.visualstudio.com/content/problem/33928/constexpr-failing-on-nullptr-v141-compiler-regress.html
|
||||
const HANDLE InvalidHandleTraits::DefaultHandle = INVALID_HANDLE_VALUE;
|
||||
const HANDLE FindFileHandleTraits::DefaultHandle = INVALID_HANDLE_VALUE;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <ntassert.h>
|
||||
#include "ntassert.h"
|
||||
|
||||
struct InvalidHandleTraits
|
||||
{
|
||||
|
|
@ -20,6 +20,13 @@ struct NullHandleTraits
|
|||
static void Close(HANDLE handle) { CloseHandle(handle); }
|
||||
};
|
||||
|
||||
struct FindFileHandleTraits
|
||||
{
|
||||
using HandleType = HANDLE;
|
||||
static const HANDLE DefaultHandle;
|
||||
static void Close(HANDLE handle) { FindClose(handle); }
|
||||
};
|
||||
|
||||
struct ModuleHandleTraits
|
||||
{
|
||||
using HandleType = HMODULE;
|
||||
|
|
@ -52,6 +59,13 @@ public:
|
|||
|
||||
HandleType* operator&() { return &m_handle; }
|
||||
|
||||
HandleType release() noexcept
|
||||
{
|
||||
auto value = m_handle;
|
||||
m_handle = traits::DefaultHandle;
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
HandleType m_handle;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
// 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.
|
||||
|
||||
#include "StringHelpers.h"
|
||||
|
||||
bool ends_with(const std::wstring &source, const std::wstring &suffix, bool ignoreCase)
|
||||
{
|
||||
if (source.length() < suffix.length())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto offset = source.length() - suffix.length();
|
||||
return CSTR_EQUAL == CompareStringOrdinal(source.c_str() + offset, static_cast<int>(suffix.length()), suffix.c_str(), static_cast<int>(suffix.length()), ignoreCase);
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
[[nodiscard]]
|
||||
bool ends_with(const std::wstring &source, const std::wstring &suffix, bool ignoreCase = false);
|
||||
|
||||
template<typename ... Args>
|
||||
[[nodiscard]]
|
||||
std::wstring format(const std::wstring& format, Args ... args)
|
||||
{
|
||||
const size_t size = swprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra char for '\0'
|
||||
std::unique_ptr<wchar_t[]> formattedBuffer(new wchar_t[size]);
|
||||
swprintf(formattedBuffer.get(), size, format.c_str(), args ... );
|
||||
return std::wstring(formattedBuffer.get(), formattedBuffer.get() + size - 1);
|
||||
}
|
||||
|
||||
|
|
@ -38,12 +38,6 @@ Language=English
|
|||
%1
|
||||
.
|
||||
|
||||
Messageid=1002
|
||||
SymbolicName=ASPNETCORE_EVENT_PROCESS_CRASH
|
||||
Language=English
|
||||
%1
|
||||
.
|
||||
|
||||
Messageid=1003
|
||||
SymbolicName=ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED
|
||||
Language=English
|
||||
|
|
@ -110,42 +104,12 @@ Language=English
|
|||
%1
|
||||
.
|
||||
|
||||
Messageid=1014
|
||||
SymbolicName=ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP
|
||||
Language=English
|
||||
%1
|
||||
.
|
||||
|
||||
Messageid=1015
|
||||
SymbolicName=ASPNETCORE_EVENT_PORTABLE_APP_DOTNET_MISSING
|
||||
Language=English
|
||||
%1
|
||||
.
|
||||
|
||||
Messageid=1016
|
||||
SymbolicName=ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND
|
||||
Language=English
|
||||
%1
|
||||
.
|
||||
|
||||
Messageid=1017
|
||||
SymbolicName=ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND
|
||||
Language=English
|
||||
%1
|
||||
.
|
||||
|
||||
Messageid=1018
|
||||
SymbolicName=ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION
|
||||
Language=English
|
||||
%1
|
||||
.
|
||||
|
||||
Messageid=1019
|
||||
SymbolicName=ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND
|
||||
Language=English
|
||||
%1
|
||||
.
|
||||
|
||||
Messageid=1020
|
||||
SymbolicName=ASPNETCORE_EVENT_PROCESS_START_FAILURE
|
||||
Language=English
|
||||
|
|
@ -164,12 +128,6 @@ Language=English
|
|||
%1
|
||||
.
|
||||
|
||||
Messageid=1023
|
||||
SymbolicName=ASPNETCORE_EVENT_APP_IN_SHUTDOWN
|
||||
Language=English
|
||||
%1
|
||||
.
|
||||
|
||||
Messageid=1024
|
||||
SymbolicName=ASPNETCORE_EVENT_MONITOR_APPOFFLINE_ERROR
|
||||
Language=English
|
||||
|
|
@ -213,7 +171,7 @@ Language=English
|
|||
.
|
||||
|
||||
Messageid=1031
|
||||
SymbolicName=ASPNETCORE_EVENT_INVALID_PROCESS_PATH
|
||||
SymbolicName=ASPNETCORE_EVENT_INPROCESS_START_ERROR
|
||||
Language=English
|
||||
%1
|
||||
.
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
#include "atlbase.h"
|
||||
#include "config_utility.h"
|
||||
|
||||
inline HANDLE g_hStandardOutput = INVALID_HANDLE_VALUE;
|
||||
inline HANDLE g_logFile = INVALID_HANDLE_VALUE;
|
||||
inline HMODULE g_hModule;
|
||||
inline SRWLOCK g_logFileLock;
|
||||
|
|
@ -65,7 +64,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);
|
||||
|
||||
|
|
@ -149,7 +148,6 @@ VOID
|
|||
DebugInitialize(HMODULE hModule)
|
||||
{
|
||||
g_hModule = hModule;
|
||||
g_hStandardOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
HKEY hKey;
|
||||
InitializeSRWLock(&g_logFileLock);
|
||||
|
|
@ -290,10 +288,10 @@ DebugPrint(
|
|||
|
||||
OutputDebugStringA( strOutput.QueryStr() );
|
||||
DWORD nBytesWritten = 0;
|
||||
|
||||
if (IsEnabled(ASPNETCORE_DEBUG_FLAG_CONSOLE))
|
||||
{
|
||||
WriteFile(g_hStandardOutput, strOutput.QueryStr(), strOutput.QueryCB(), &nBytesWritten, nullptr);
|
||||
auto outputHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
WriteFile(outputHandle, strOutput.QueryStr(), strOutput.QueryCB(), &nBytesWritten, nullptr);
|
||||
}
|
||||
|
||||
if (g_logFile != INVALID_HANDLE_VALUE)
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
#define CATCH_RETURN() catch (...) { RETURN_CAUGHT_EXCEPTION(); }
|
||||
#define LOG_IF_FAILED(hr) LogHResultFailed(LOCATION_INFO, hr)
|
||||
#define LOG_LAST_ERROR() LogLastErrorIf(LOCATION_INFO, true)
|
||||
#define LOG_LAST_ERROR_IF(condition) LogLastErrorIf(LOCATION_INFO, condition)
|
||||
#define SUCCEEDED_LOG(hr) SUCCEEDED(LOG_IF_FAILED(hr))
|
||||
#define FAILED_LOG(hr) FAILED(LOG_IF_FAILED(hr))
|
||||
|
|
@ -74,7 +75,7 @@
|
|||
|
||||
__declspec(noinline) inline VOID ReportException(LOCATION_ARGUMENTS std::exception& exception)
|
||||
{
|
||||
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, LOCATION_FORMAT "Unhandled exception: %s", LOCATION_CALL exception.what());
|
||||
DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, "Exception : %s caught at" LOCATION_FORMAT, exception.what(), LOCATION_CALL_ONLY);
|
||||
}
|
||||
|
||||
__declspec(noinline) inline HRESULT LogHResultFailed(LOCATION_ARGUMENTS HRESULT hr)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -3,185 +3,78 @@
|
|||
|
||||
#include "hostfxr_utility.h"
|
||||
|
||||
#include <string>
|
||||
#include <atlcomcli.h>
|
||||
#include "EventLog.h"
|
||||
#include "ntassert.h"
|
||||
#include "fx_ver.h"
|
||||
#include "debugutil.h"
|
||||
#include "exceptions.h"
|
||||
#include "HandleWrapper.h"
|
||||
#include "Environment.h"
|
||||
#include "file_utility.h"
|
||||
#include "StringHelpers.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
//
|
||||
// Runs a standalone appliction.
|
||||
// The folder structure looks like this:
|
||||
// Application/
|
||||
// hostfxr.dll
|
||||
// Application.exe
|
||||
// Application.dll
|
||||
// etc.
|
||||
// We get the full path to hostfxr.dll and Application.dll and run hostfxr_main,
|
||||
// passing in Application.dll.
|
||||
// Assuming we don't need Application.exe as the dll is the actual application.
|
||||
//
|
||||
HRESULT
|
||||
HOSTFXR_UTILITY::GetStandaloneHostfxrParameters(
|
||||
PCWSTR pwzExeAbsolutePath, // includes .exe file extension.
|
||||
PCWSTR pcwzApplicationPhysicalPath,
|
||||
PCWSTR pcwzArguments,
|
||||
_Inout_ STRU* pStruHostFxrDllLocation,
|
||||
_Out_ DWORD* pdwArgCount,
|
||||
_Out_ BSTR** ppwzArgv
|
||||
)
|
||||
{
|
||||
LOG_INFOF("Resolving standalone hostfxr parameters for application: %S arguments: %S path: %S",
|
||||
pwzExeAbsolutePath,
|
||||
pcwzArguments,
|
||||
pwzExeAbsolutePath);
|
||||
|
||||
const fs::path exePath(pwzExeAbsolutePath);
|
||||
|
||||
if (!exePath.has_extension())
|
||||
{
|
||||
LOG_INFOF("Exe path has not extension, returning");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const fs::path physicalPath(pcwzApplicationPhysicalPath);
|
||||
const fs::path hostFxrLocation = physicalPath / "hostfxr.dll";
|
||||
|
||||
LOG_INFOF("Checking hostfxr.dll at %S", hostFxrLocation.c_str());
|
||||
|
||||
if (!is_regular_file(hostFxrLocation))
|
||||
{
|
||||
fs::path runtimeConfigLocation = exePath;
|
||||
runtimeConfigLocation.replace_extension(L".runtimeconfig.json");
|
||||
|
||||
LOG_INFOF("Checking runtimeconfig.json at %S", runtimeConfigLocation.c_str());
|
||||
if (!is_regular_file(runtimeConfigLocation))
|
||||
{
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP,
|
||||
ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP_MSG,
|
||||
pcwzApplicationPhysicalPath,
|
||||
0);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND,
|
||||
ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG,
|
||||
pcwzApplicationPhysicalPath,
|
||||
0);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
fs::path dllPath = exePath;
|
||||
dllPath.replace_extension(".dll");
|
||||
|
||||
if (!is_regular_file(dllPath))
|
||||
{
|
||||
LOG_INFOF("Application dll at %S was not found", dllPath.c_str());
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
auto arguments = std::wstring(dllPath) + L" " + pcwzArguments;
|
||||
|
||||
RETURN_IF_FAILED(pStruHostFxrDllLocation->Copy(hostFxrLocation.c_str()));
|
||||
|
||||
RETURN_IF_FAILED(ParseHostfxrArguments(
|
||||
arguments.c_str(),
|
||||
pwzExeAbsolutePath,
|
||||
pcwzApplicationPhysicalPath,
|
||||
pdwArgCount,
|
||||
ppwzArgv));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
BOOL
|
||||
HOSTFXR_UTILITY::IsDotnetExecutable(const std::filesystem::path & dotnetPath)
|
||||
{
|
||||
auto name = dotnetPath.filename();
|
||||
name.replace_extension("");
|
||||
return _wcsnicmp(name.c_str(), L"dotnet", 6) == 0;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
void
|
||||
HOSTFXR_UTILITY::GetHostFxrParameters(
|
||||
_In_ PCWSTR pcwzProcessPath,
|
||||
_In_ PCWSTR pcwzApplicationPhysicalPath,
|
||||
_In_ PCWSTR pcwzArguments,
|
||||
_Inout_ STRU *pStruHostFxrDllLocation,
|
||||
_Inout_ STRU *pStruExeAbsolutePath,
|
||||
_Out_ DWORD *pdwArgCount,
|
||||
_Out_ BSTR **pbstrArgv
|
||||
const fs::path &processPath,
|
||||
const fs::path &applicationPhysicalPath,
|
||||
const std::wstring &applicationArguments,
|
||||
fs::path &hostFxrDllPath,
|
||||
fs::path &dotnetExePath,
|
||||
std::vector<std::wstring> &arguments
|
||||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
LOG_INFOF("Resolving hostfxr parameters for application: '%S' arguments: '%S' path: '%S'",
|
||||
processPath.c_str(),
|
||||
applicationArguments.c_str(),
|
||||
applicationPhysicalPath.c_str());
|
||||
|
||||
LOG_INFOF("Resolving hostfxr parameters for application: %S arguments: %S path: %S",
|
||||
pcwzProcessPath,
|
||||
pcwzArguments,
|
||||
pcwzApplicationPhysicalPath);
|
||||
fs::path expandedProcessPath = Environment::ExpandEnvironmentVariables(processPath);
|
||||
const auto expandedApplicationArguments = Environment::ExpandEnvironmentVariables(applicationArguments);
|
||||
|
||||
const fs::path applicationPhysicalPath = pcwzApplicationPhysicalPath;
|
||||
fs::path processPath = Environment::ExpandEnvironmentVariables(pcwzProcessPath);
|
||||
std::wstring arguments = Environment::ExpandEnvironmentVariables(pcwzArguments);
|
||||
LOG_INFOF("Expanded hostfxr parameters for application: '%S' arguments: '%S'",
|
||||
expandedProcessPath.c_str(),
|
||||
expandedApplicationArguments.c_str());
|
||||
|
||||
LOG_INFOF("Known dotnet.exe location: '%S'", dotnetExePath.c_str());
|
||||
|
||||
if (!expandedProcessPath.has_extension())
|
||||
{
|
||||
// The only executable extension inprocess supports
|
||||
expandedProcessPath.replace_extension(".exe");
|
||||
}
|
||||
else if (!ends_with(expandedProcessPath, L".exe", true))
|
||||
{
|
||||
throw StartupParametersResolutionException(format(L"Process path '%s' doesn't have '.exe' extension.", expandedProcessPath.c_str()));
|
||||
}
|
||||
|
||||
// Check if the absolute path is to dotnet or not.
|
||||
if (IsDotnetExecutable(processPath))
|
||||
if (IsDotnetExecutable(expandedProcessPath))
|
||||
{
|
||||
LOG_INFOF("Process path %S is dotnet, treating application as portable", processPath.c_str());
|
||||
//
|
||||
// The processPath ends with dotnet.exe or dotnet
|
||||
// like: C:\Program Files\dotnet\dotnet.exe, C:\Program Files\dotnet\dotnet, dotnet.exe, or dotnet.
|
||||
// Get the absolute path to dotnet. If the path is already an absolute path, it will return that path
|
||||
//
|
||||
// Make sure to append the dotnet.exe path correctly here (pass in regular path)?
|
||||
auto fullProcessPath = GetAbsolutePathToDotnet(applicationPhysicalPath, processPath);
|
||||
if (!fullProcessPath.has_value())
|
||||
LOG_INFOF("Process path '%S' is dotnet, treating application as portable", expandedProcessPath.c_str());
|
||||
|
||||
if (dotnetExePath.empty())
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_INVALID_PROCESS_PATH,
|
||||
ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG,
|
||||
processPath.c_str(),
|
||||
hr);
|
||||
return hr;
|
||||
dotnetExePath = GetAbsolutePathToDotnet(applicationPhysicalPath, expandedProcessPath);
|
||||
}
|
||||
|
||||
processPath = fullProcessPath.value();
|
||||
hostFxrDllPath = GetAbsolutePathToHostFxr(dotnetExePath);
|
||||
|
||||
auto hostFxrPath = GetAbsolutePathToHostFxr(processPath);
|
||||
if (!hostFxrPath.has_value())
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||||
return hr;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
arguments.c_str(),
|
||||
processPath.c_str(),
|
||||
pcwzApplicationPhysicalPath,
|
||||
pdwArgCount,
|
||||
pbstrArgv));
|
||||
|
||||
RETURN_IF_FAILED(pStruHostFxrDllLocation->Copy(hostFxrPath->c_str()));
|
||||
RETURN_IF_FAILED(pStruExeAbsolutePath->Copy(processPath.c_str()));
|
||||
ParseHostfxrArguments(
|
||||
expandedApplicationArguments,
|
||||
dotnetExePath,
|
||||
applicationPhysicalPath,
|
||||
arguments,
|
||||
true);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFOF("Process path %S is not dotnet, treating application as standalone", processPath.c_str());
|
||||
LOG_INFOF("Process path '%S' is not dotnet, treating application as standalone or portable with bootstrapper", expandedProcessPath.c_str());
|
||||
|
||||
if (processPath.is_relative())
|
||||
auto executablePath = expandedProcessPath;
|
||||
|
||||
if (executablePath.is_relative())
|
||||
{
|
||||
processPath = applicationPhysicalPath / processPath;
|
||||
executablePath = applicationPhysicalPath / expandedProcessPath;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -189,17 +82,42 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
|
|||
// like: C:\test\MyApp.Exe or MyApp.Exe
|
||||
// Check if the file exists, and if it does, get the parameters for a standalone application
|
||||
//
|
||||
if (is_regular_file(processPath))
|
||||
if (is_regular_file(executablePath))
|
||||
{
|
||||
RETURN_IF_FAILED(GetStandaloneHostfxrParameters(
|
||||
processPath.c_str(),
|
||||
pcwzApplicationPhysicalPath,
|
||||
arguments.c_str(),
|
||||
pStruHostFxrDllLocation,
|
||||
pdwArgCount,
|
||||
pbstrArgv));
|
||||
auto applicationDllPath = executablePath;
|
||||
applicationDllPath.replace_extension(".dll");
|
||||
|
||||
RETURN_IF_FAILED(pStruExeAbsolutePath->Copy(processPath.c_str()));
|
||||
LOG_INFOF("Checking application.dll at %S", applicationDllPath.c_str());
|
||||
if (!is_regular_file(applicationDllPath))
|
||||
{
|
||||
throw StartupParametersResolutionException(format(L"Application .dll was not found at %s", applicationDllPath.c_str()));
|
||||
}
|
||||
|
||||
hostFxrDllPath = executablePath.parent_path() / "hostfxr.dll";
|
||||
LOG_INFOF("Checking hostfxr.dll at %S", hostFxrDllPath.c_str());
|
||||
if (is_regular_file(hostFxrDllPath))
|
||||
{
|
||||
LOG_INFOF("hostfxr.dll found app local at '%S', treating application as standalone", hostFxrDllPath.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFOF("hostfxr.dll found app local at '%S', treating application as portable with launcher", hostFxrDllPath.c_str());
|
||||
|
||||
// passing "dotnet" here because we don't know where dotnet.exe should come from
|
||||
// so trying all fallbacks is appropriate
|
||||
if (dotnetExePath.empty())
|
||||
{
|
||||
dotnetExePath = GetAbsolutePathToDotnet(applicationPhysicalPath, L"dotnet");
|
||||
}
|
||||
executablePath = dotnetExePath;
|
||||
hostFxrDllPath = GetAbsolutePathToHostFxr(dotnetExePath);
|
||||
}
|
||||
|
||||
ParseHostfxrArguments(
|
||||
applicationDllPath.generic_wstring() + L" " + expandedApplicationArguments,
|
||||
executablePath,
|
||||
applicationPhysicalPath,
|
||||
arguments);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -207,140 +125,75 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
|
|||
// If the processPath file does not exist and it doesn't include dotnet.exe or dotnet
|
||||
// then it is an invalid argument.
|
||||
//
|
||||
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_INVALID_PROCESS_PATH,
|
||||
ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG,
|
||||
processPath.c_str(),
|
||||
hr);
|
||||
return hr;
|
||||
throw StartupParametersResolutionException(format(L"Executable was not found at '%s'", executablePath.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// Forms the argument list in HOSTFXR_PARAMETERS.
|
||||
// Sets the ArgCount and Arguments.
|
||||
// Arg structure:
|
||||
// argv[0] = Path to exe activating hostfxr.
|
||||
// argv[1] = L"exec"
|
||||
// argv[2] = absolute path to dll.
|
||||
//
|
||||
HRESULT
|
||||
BOOL
|
||||
HOSTFXR_UTILITY::IsDotnetExecutable(const std::filesystem::path & dotnetPath)
|
||||
{
|
||||
return ends_with(dotnetPath, L"dotnet.exe", true);
|
||||
}
|
||||
|
||||
void
|
||||
HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
PCWSTR pwzArgumentsFromConfig,
|
||||
PCWSTR pwzExePath,
|
||||
PCWSTR pcwzApplicationPhysicalPath,
|
||||
_Out_ DWORD* pdwArgCount,
|
||||
_Out_ BSTR** pbstrArgv
|
||||
const std::wstring &applicationArguments,
|
||||
const fs::path &applicationExePath,
|
||||
const fs::path &applicationPhysicalPath,
|
||||
std::vector<std::wstring> &arguments,
|
||||
bool expandDllPaths
|
||||
)
|
||||
{
|
||||
DBG_ASSERT(pdwArgCount != NULL);
|
||||
DBG_ASSERT(pbstrArgv != NULL);
|
||||
DBG_ASSERT(pwzExePath != NULL);
|
||||
LOG_INFOF("Resolving hostfxr_main arguments application: '%S' arguments: '%S' path: %S", applicationExePath.c_str(), applicationArguments.c_str(), applicationPhysicalPath.c_str());
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
INT argc = 0;
|
||||
BSTR* argv = NULL;
|
||||
LPWSTR* pwzArgs = NULL;
|
||||
STRU struTempPath;
|
||||
INT intArgsProcessed = 0;
|
||||
arguments = std::vector<std::wstring>();
|
||||
arguments.push_back(applicationExePath);
|
||||
|
||||
LOG_INFOF("Resolving hostfxr_main arguments application: %S arguments: %S path: %S",
|
||||
pwzExePath,
|
||||
pwzArgumentsFromConfig,
|
||||
pcwzApplicationPhysicalPath);
|
||||
|
||||
// If we call CommandLineToArgvW with an empty string, argc is 5 for some interesting reason.
|
||||
// Protectively guard against this by check if the string is null or empty.
|
||||
if (pwzArgumentsFromConfig == NULL || wcscmp(pwzArgumentsFromConfig, L"") == 0)
|
||||
if (applicationArguments.empty())
|
||||
{
|
||||
hr = E_INVALIDARG;
|
||||
goto Finished;
|
||||
throw StartupParametersResolutionException(L"Application arguments are empty.");
|
||||
}
|
||||
|
||||
pwzArgs = CommandLineToArgvW(pwzArgumentsFromConfig, &argc);
|
||||
|
||||
if (pwzArgs == NULL)
|
||||
int argc = 0;
|
||||
auto pwzArgs = std::unique_ptr<LPWSTR[], LocalFreeDeleter>(CommandLineToArgvW(applicationArguments.c_str(), &argc));
|
||||
if (!pwzArgs)
|
||||
{
|
||||
hr = LOG_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
|
||||
goto Failure;
|
||||
throw StartupParametersResolutionException(format(L"Unable parse command line argumens '%s' or '%s'", applicationArguments.c_str()));
|
||||
}
|
||||
|
||||
argv = new BSTR[argc + 1];
|
||||
for (int intArgsProcessed = 0; intArgsProcessed < argc; intArgsProcessed++)
|
||||
{
|
||||
std::wstring argument = pwzArgs[intArgsProcessed];
|
||||
|
||||
argv[0] = SysAllocString(pwzExePath);
|
||||
if (argv[0] == NULL)
|
||||
{
|
||||
hr = E_OUTOFMEMORY;
|
||||
goto Failure;
|
||||
}
|
||||
// Try to convert the application dll from a relative to an absolute path
|
||||
// Don't record this failure as pwzArgs[0] may already be an absolute path to the dll.
|
||||
for (intArgsProcessed = 0; intArgsProcessed < argc; intArgsProcessed++)
|
||||
{
|
||||
DBG_ASSERT(pwzArgs[intArgsProcessed] != NULL);
|
||||
struTempPath.Copy(pwzArgs[intArgsProcessed]);
|
||||
if (struTempPath.EndsWith(L".dll"))
|
||||
// Try expanding arguments ending in .dll to a full paths
|
||||
if (expandDllPaths && ends_with(argument, L".dll", true))
|
||||
{
|
||||
if (SUCCEEDED(FILE_UTILITY::ConvertPathToFullPath(pwzArgs[intArgsProcessed], pcwzApplicationPhysicalPath, &struTempPath)))
|
||||
fs::path argumentAsPath = argument;
|
||||
if (argumentAsPath.is_relative())
|
||||
{
|
||||
argv[intArgsProcessed + 1] = SysAllocString(struTempPath.QueryStr());
|
||||
}
|
||||
else
|
||||
{
|
||||
argv[intArgsProcessed + 1] = SysAllocString(pwzArgs[intArgsProcessed]);
|
||||
}
|
||||
if (argv[intArgsProcessed + 1] == NULL)
|
||||
{
|
||||
hr = E_OUTOFMEMORY;
|
||||
goto Failure;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
argv[intArgsProcessed + 1] = SysAllocString(pwzArgs[intArgsProcessed]);
|
||||
if (argv[intArgsProcessed + 1] == NULL)
|
||||
{
|
||||
hr = E_OUTOFMEMORY;
|
||||
goto Failure;
|
||||
argumentAsPath = applicationPhysicalPath / argumentAsPath;
|
||||
if (exists(argumentAsPath))
|
||||
{
|
||||
LOG_INFOF("Converted argument '%S' to %S", argument.c_str(), argumentAsPath.c_str());
|
||||
argument = argumentAsPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFOF("Argument[%d] = %S",
|
||||
intArgsProcessed + 1,
|
||||
argv[intArgsProcessed + 1]);
|
||||
arguments.push_back(argument);
|
||||
}
|
||||
|
||||
*pbstrArgv = argv;
|
||||
*pdwArgCount = argc + 1;
|
||||
|
||||
goto Finished;
|
||||
|
||||
Failure:
|
||||
if (argv != NULL)
|
||||
for (size_t i = 0; i < arguments.size(); i++)
|
||||
{
|
||||
// intArgsProcess - 1 here as if we fail to allocated the ith string
|
||||
// we don't want to free it.
|
||||
for (INT i = 0; i < intArgsProcessed - 1; i++)
|
||||
{
|
||||
SysFreeString(argv[i]);
|
||||
}
|
||||
LOG_INFOF("Argument[%d] = %S", i, arguments[i].c_str());
|
||||
}
|
||||
|
||||
delete[] argv;
|
||||
|
||||
Finished:
|
||||
if (pwzArgs != NULL)
|
||||
{
|
||||
LocalFree(pwzArgs);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
std::optional<fs::path>
|
||||
// The processPath ends with dotnet.exe or dotnet
|
||||
// like: C:\Program Files\dotnet\dotnet.exe, C:\Program Files\dotnet\dotnet, dotnet.exe, or dotnet.
|
||||
// Get the absolute path to dotnet. If the path is already an absolute path, it will return that path
|
||||
fs::path
|
||||
HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
|
||||
const fs::path & applicationPath,
|
||||
const fs::path & requestedPath
|
||||
|
|
@ -357,21 +210,11 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
|
|||
//
|
||||
// If we are given an absolute path to dotnet.exe, we are done
|
||||
//
|
||||
if (is_regular_file(requestedPath))
|
||||
if (is_regular_file(processPath))
|
||||
{
|
||||
LOG_INFOF("Found dotnet.exe at %S", requestedPath.c_str());
|
||||
LOG_INFOF("Found dotnet.exe at %S", processPath.c_str());
|
||||
|
||||
return std::make_optional(requestedPath);
|
||||
}
|
||||
|
||||
auto pathWithExe = requestedPath;
|
||||
pathWithExe.concat(L".exe");
|
||||
|
||||
if (is_regular_file(pathWithExe))
|
||||
{
|
||||
LOG_INFOF("Found dotnet.exe at %S", pathWithExe.c_str());
|
||||
|
||||
return std::make_optional(pathWithExe);
|
||||
return processPath;
|
||||
}
|
||||
|
||||
// At this point, we are calling where.exe to find dotnet.
|
||||
|
|
@ -382,7 +225,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
|
|||
{
|
||||
LOG_INFOF("Absolute path to dotnet.exe was not found at %S", requestedPath.c_str());
|
||||
|
||||
return std::nullopt;
|
||||
throw StartupParametersResolutionException(format(L"Could not find dotnet.exe at '%s'", processPath.c_str()));
|
||||
}
|
||||
|
||||
const auto dotnetViaWhere = InvokeWhereToFindDotnet();
|
||||
|
|
@ -390,7 +233,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
|
|||
{
|
||||
LOG_INFOF("Found dotnet.exe via where.exe invocation at %S", dotnetViaWhere.value().c_str());
|
||||
|
||||
return dotnetViaWhere;
|
||||
return dotnetViaWhere.value();
|
||||
}
|
||||
|
||||
const auto programFilesLocation = GetAbsolutePathToDotnetFromProgramFiles();
|
||||
|
|
@ -398,14 +241,17 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
|
|||
{
|
||||
LOG_INFOF("Found dotnet.exe in Program Files at %S", programFilesLocation.value().c_str());
|
||||
|
||||
return programFilesLocation;
|
||||
return programFilesLocation.value();
|
||||
}
|
||||
|
||||
LOG_INFOF("dotnet.exe not found");
|
||||
return std::nullopt;
|
||||
throw StartupParametersResolutionException(format(
|
||||
L"Could not find dotnet.exe at '%s' or using the system PATH environment variable."
|
||||
" Check that a valid path to dotnet is on the PATH and the bitness of dotnet matches the bitness of the IIS worker process.",
|
||||
processPath.c_str()));
|
||||
}
|
||||
|
||||
std::optional<fs::path>
|
||||
fs::path
|
||||
HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
|
||||
const fs::path & dotnetPath
|
||||
)
|
||||
|
|
@ -417,23 +263,14 @@ HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
|
|||
|
||||
if (!is_directory(hostFxrBase))
|
||||
{
|
||||
EventLog::Error(ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND, ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG, hostFxrBase.c_str(), HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT));
|
||||
|
||||
return std::nullopt;
|
||||
throw StartupParametersResolutionException(format(L"Unable to find hostfxr directory at %s", hostFxrBase.c_str()));
|
||||
}
|
||||
|
||||
auto searchPattern = std::wstring(hostFxrBase) + L"\\*";
|
||||
FindDotNetFolders(searchPattern.c_str(), versionFolders);
|
||||
FindDotNetFolders(hostFxrBase, versionFolders);
|
||||
|
||||
if (versionFolders.empty())
|
||||
{
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND,
|
||||
ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG,
|
||||
hostFxrBase.c_str(),
|
||||
HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT));
|
||||
|
||||
return std::nullopt;
|
||||
throw StartupParametersResolutionException(format(L"Hostfxr directory '%s' doesn't contain any version subdirectories", hostFxrBase.c_str()));
|
||||
}
|
||||
|
||||
const auto highestVersion = FindHighestDotNetVersion(versionFolders);
|
||||
|
|
@ -441,24 +278,18 @@ HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
|
|||
|
||||
if (!is_regular_file(hostFxrPath))
|
||||
{
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND,
|
||||
ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG,
|
||||
hostFxrPath.c_str(),
|
||||
HRESULT_FROM_WIN32(ERROR_FILE_INVALID));
|
||||
|
||||
return std::nullopt;
|
||||
throw StartupParametersResolutionException(format(L"hostfxr.dll not found at '%s'", hostFxrPath.c_str()));
|
||||
}
|
||||
|
||||
LOG_INFOF("hostfxr.dll located at %S", hostFxrPath.c_str());
|
||||
return std::make_optional(hostFxrPath);
|
||||
return hostFxrPath;
|
||||
}
|
||||
|
||||
//
|
||||
// Tries to call where.exe to find the location of dotnet.exe.
|
||||
// Will check that the bitness of dotnet matches the current
|
||||
// worker process bitness.
|
||||
// Returns true if a valid dotnet was found, else false.
|
||||
// Returns true if a valid dotnet was found, else false.R
|
||||
//
|
||||
std::optional<fs::path>
|
||||
HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
|
||||
|
|
@ -638,7 +469,6 @@ HOSTFXR_UTILITY::FindHighestDotNetVersion(
|
|||
fx_ver_t fx_ver(-1, -1, -1);
|
||||
if (fx_ver_t::parse(dir, &fx_ver, false))
|
||||
{
|
||||
// TODO using max instead of std::max works
|
||||
max_ver = max(max_ver, fx_ver);
|
||||
}
|
||||
}
|
||||
|
|
@ -648,16 +478,16 @@ HOSTFXR_UTILITY::FindHighestDotNetVersion(
|
|||
|
||||
VOID
|
||||
HOSTFXR_UTILITY::FindDotNetFolders(
|
||||
_In_ PCWSTR pszPath,
|
||||
_Out_ std::vector<std::wstring> & pvFolders
|
||||
const std::filesystem::path &path,
|
||||
_Out_ std::vector<std::wstring> &pvFolders
|
||||
)
|
||||
{
|
||||
HANDLE handle = NULL;
|
||||
WIN32_FIND_DATAW data = { 0 };
|
||||
|
||||
handle = FindFirstFileExW(pszPath, FindExInfoStandard, &data, FindExSearchNameMatch, NULL, 0);
|
||||
WIN32_FIND_DATAW data = {};
|
||||
const auto searchPattern = std::wstring(path) + L"\\*";
|
||||
HandleWrapper<FindFileHandleTraits> handle = FindFirstFileExW(searchPattern.c_str(), FindExInfoStandard, &data, FindExSearchNameMatch, nullptr, 0);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
LOG_LAST_ERROR();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -665,6 +495,4 @@ HOSTFXR_UTILITY::FindDotNetFolders(
|
|||
{
|
||||
pvFolders.emplace_back(data.cFileName);
|
||||
} while (FindNextFileW(handle, &data));
|
||||
|
||||
FindClose(handle);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include "stringu.h"
|
||||
#include <string>
|
||||
|
||||
typedef INT(*hostfxr_get_native_search_directories_fn) (CONST INT argc, CONST PCWSTR* argv, PWSTR buffer, DWORD buffer_size, DWORD* required_buffer_size);
|
||||
typedef INT(*hostfxr_main_fn) (CONST DWORD argc, CONST PCWSTR argv[]);
|
||||
|
|
@ -19,76 +19,88 @@ class HOSTFXR_UTILITY
|
|||
public:
|
||||
|
||||
static
|
||||
HRESULT
|
||||
GetStandaloneHostfxrParameters(
|
||||
PCWSTR pwzExeAbsolutePath, // includes .exe file extension.
|
||||
PCWSTR pcwzApplicationPhysicalPath,
|
||||
PCWSTR pcwzArguments,
|
||||
_Inout_ STRU* pStruHostFxrDllLocation,
|
||||
_Out_ DWORD* pdwArgCount,
|
||||
_Out_ BSTR** ppwzArgv
|
||||
);
|
||||
|
||||
static
|
||||
HRESULT
|
||||
ParseHostfxrArguments(
|
||||
PCWSTR pwzArgumentsFromConfig,
|
||||
PCWSTR pwzExePath,
|
||||
PCWSTR pcwzApplicationPhysicalPath,
|
||||
_Out_ DWORD* pdwArgCount,
|
||||
_Out_ BSTR** pbstrArgv
|
||||
);
|
||||
|
||||
static
|
||||
BOOL
|
||||
IsDotnetExecutable(
|
||||
_In_ const std::filesystem::path & dotnetPath
|
||||
);
|
||||
|
||||
static
|
||||
HRESULT
|
||||
void
|
||||
GetHostFxrParameters(
|
||||
_In_ PCWSTR pcwzProcessPath,
|
||||
_In_ PCWSTR pcwzApplicationPhysicalPath,
|
||||
_In_ PCWSTR pcwzArguments,
|
||||
_Inout_ STRU *pStruHostFxrDllLocation,
|
||||
_Inout_ STRU *struExeAbsolutePath,
|
||||
_Out_ DWORD *pdwArgCount,
|
||||
_Out_ BSTR **ppwzArgv
|
||||
const std::filesystem::path &processPath,
|
||||
const std::filesystem::path &applicationPhysicalPath,
|
||||
const std::wstring &applicationArguments,
|
||||
std::filesystem::path &hostFxrDllPath,
|
||||
std::filesystem::path &dotnetExePath,
|
||||
std::vector<std::wstring> &arguments
|
||||
);
|
||||
|
||||
static
|
||||
VOID
|
||||
FindDotNetFolders(
|
||||
_In_ PCWSTR pszPath,
|
||||
_Out_ std::vector<std::wstring> & pvFolders
|
||||
);
|
||||
class StartupParametersResolutionException: public std::runtime_error
|
||||
{
|
||||
public:
|
||||
StartupParametersResolutionException(std::wstring msg)
|
||||
: runtime_error("Startup parameter resulution error occured"), message(std::move(msg))
|
||||
{
|
||||
}
|
||||
|
||||
std::wstring get_message() const { return message; }
|
||||
|
||||
private:
|
||||
std::wstring message;
|
||||
};
|
||||
|
||||
static
|
||||
std::wstring
|
||||
FindHighestDotNetVersion(
|
||||
_In_ std::vector<std::wstring> & vFolders
|
||||
);
|
||||
|
||||
static
|
||||
std::optional<std::filesystem::path>
|
||||
GetAbsolutePathToHostFxr(
|
||||
_In_ const std::filesystem::path & dotnetPath
|
||||
void
|
||||
ParseHostfxrArguments(
|
||||
const std::wstring &arugments,
|
||||
const std::filesystem::path &exePath,
|
||||
const std::filesystem::path &applicationPhysicalPath,
|
||||
std::vector<std::wstring> &arguments,
|
||||
bool expandDllPaths = false
|
||||
);
|
||||
|
||||
static
|
||||
std::optional<std::filesystem::path>
|
||||
GetAbsolutePathToDotnetFromProgramFiles();
|
||||
private:
|
||||
|
||||
static
|
||||
BOOL
|
||||
IsDotnetExecutable(
|
||||
const std::filesystem::path & dotnetPath
|
||||
);
|
||||
|
||||
static
|
||||
VOID
|
||||
FindDotNetFolders(
|
||||
const std::filesystem::path& path,
|
||||
std::vector<std::wstring> & pvFolders
|
||||
);
|
||||
|
||||
static
|
||||
std::wstring
|
||||
FindHighestDotNetVersion(
|
||||
std::vector<std::wstring> & vFolders
|
||||
);
|
||||
|
||||
static
|
||||
std::filesystem::path
|
||||
GetAbsolutePathToHostFxr(
|
||||
const std::filesystem::path & dotnetPath
|
||||
);
|
||||
|
||||
|
||||
static
|
||||
std::optional<std::filesystem::path>
|
||||
InvokeWhereToFindDotnet();
|
||||
|
||||
static
|
||||
std::optional<std::filesystem::path>
|
||||
std::filesystem::path
|
||||
GetAbsolutePathToDotnet(
|
||||
_In_ const std::filesystem::path & applicationPath,
|
||||
_In_ const std::filesystem::path & requestedPath
|
||||
const std::filesystem::path & applicationPath,
|
||||
const std::filesystem::path & requestedPath
|
||||
);
|
||||
|
||||
struct LocalFreeDeleter
|
||||
{
|
||||
void operator ()(LPWSTR* ptr) const
|
||||
{
|
||||
LocalFree(ptr);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,82 +6,48 @@
|
|||
#include "hostfxr_utility.h"
|
||||
#include "debugutil.h"
|
||||
#include "exceptions.h"
|
||||
#include "EventLog.h"
|
||||
|
||||
HRESULT HOSTFXR_OPTIONS::Create(
|
||||
_In_ PCWSTR pcwzExeLocation,
|
||||
_In_ PCWSTR pcwzDotnetExePath,
|
||||
_In_ PCWSTR pcwzProcessPath,
|
||||
_In_ PCWSTR pcwzApplicationPhysicalPath,
|
||||
_In_ PCWSTR pcwzArguments,
|
||||
_Out_ std::unique_ptr<HOSTFXR_OPTIONS>& ppWrapper)
|
||||
{
|
||||
STRU struHostFxrDllLocation;
|
||||
STRU struExeAbsolutePath;
|
||||
STRU struExeLocation;
|
||||
BSTR* pwzArgv;
|
||||
DWORD dwArgCount;
|
||||
std::filesystem::path knownDotnetLocation;
|
||||
|
||||
if (pcwzExeLocation != NULL)
|
||||
if (pcwzDotnetExePath != nullptr)
|
||||
{
|
||||
RETURN_IF_FAILED(struExeLocation.Copy(pcwzExeLocation));
|
||||
knownDotnetLocation = pcwzDotnetExePath;
|
||||
}
|
||||
try
|
||||
{
|
||||
std::filesystem::path hostFxrDllPath;
|
||||
std::vector<std::wstring> arguments;
|
||||
HOSTFXR_UTILITY::GetHostFxrParameters(
|
||||
pcwzProcessPath,
|
||||
pcwzApplicationPhysicalPath,
|
||||
pcwzArguments,
|
||||
hostFxrDllPath,
|
||||
knownDotnetLocation,
|
||||
arguments);
|
||||
|
||||
// If the exe was not provided by the shim, reobtain the hostfxr parameters (which finds dotnet).
|
||||
if (struExeLocation.IsEmpty())
|
||||
{
|
||||
RETURN_IF_FAILED(HOSTFXR_UTILITY::GetHostFxrParameters(
|
||||
pcwzProcessPath,
|
||||
pcwzApplicationPhysicalPath,
|
||||
pcwzArguments,
|
||||
&struHostFxrDllLocation,
|
||||
&struExeAbsolutePath,
|
||||
&dwArgCount,
|
||||
&pwzArgv));
|
||||
ppWrapper = std::make_unique<HOSTFXR_OPTIONS>(knownDotnetLocation, hostFxrDllPath, arguments);
|
||||
}
|
||||
else if (HOSTFXR_UTILITY::IsDotnetExecutable(struExeLocation.QueryStr()))
|
||||
catch (HOSTFXR_UTILITY::StartupParametersResolutionException &resolutionException)
|
||||
{
|
||||
RETURN_IF_FAILED(HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
pcwzArguments,
|
||||
pcwzExeLocation,
|
||||
pcwzApplicationPhysicalPath,
|
||||
&dwArgCount,
|
||||
&pwzArgv));
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_IF_FAILED(HOSTFXR_UTILITY::GetStandaloneHostfxrParameters(
|
||||
pcwzExeLocation,
|
||||
pcwzApplicationPhysicalPath,
|
||||
pcwzArguments,
|
||||
&struHostFxrDllLocation,
|
||||
&dwArgCount,
|
||||
&pwzArgv));
|
||||
}
|
||||
OBSERVE_CAUGHT_EXCEPTION();
|
||||
|
||||
ppWrapper = std::make_unique<HOSTFXR_OPTIONS>();
|
||||
RETURN_IF_FAILED(ppWrapper->Populate(struHostFxrDllLocation.QueryStr(), struExeAbsolutePath.QueryStr(), dwArgCount, pwzArgv));
|
||||
EventLog::Error(
|
||||
ASPNETCORE_EVENT_INPROCESS_START_ERROR,
|
||||
ASPNETCORE_EVENT_INPROCESS_START_ERROR_MSG,
|
||||
pcwzApplicationPhysicalPath,
|
||||
resolutionException.get_message().c_str());
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT HOSTFXR_OPTIONS::Populate(PCWSTR hostFxrLocation, PCWSTR struExeLocation, DWORD argc, BSTR argv[])
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
m_argc = argc;
|
||||
m_argv = argv;
|
||||
|
||||
if (FAILED(hr = m_hostFxrLocation.Copy(hostFxrLocation)))
|
||||
{
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
if (FAILED(hr = m_exeLocation.Copy(struExeLocation)))
|
||||
{
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
Finished:
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,41 +5,45 @@
|
|||
|
||||
#include <Windows.h>
|
||||
#include <memory>
|
||||
|
||||
#include "stringu.h"
|
||||
#include <filesystem>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class HOSTFXR_OPTIONS
|
||||
{
|
||||
public:
|
||||
HOSTFXR_OPTIONS() {}
|
||||
HOSTFXR_OPTIONS(
|
||||
std::filesystem::path dotnetExeLocation,
|
||||
std::filesystem::path hostFxrLocation,
|
||||
std::vector<std::wstring> arguments
|
||||
)
|
||||
: m_dotnetExeLocation(std::move(dotnetExeLocation)),
|
||||
m_hostFxrLocation(std::move(hostFxrLocation)),
|
||||
m_arguments(std::move(arguments))
|
||||
{}
|
||||
|
||||
~HOSTFXR_OPTIONS()
|
||||
void
|
||||
GetArguments(DWORD &hostfxrArgc, std::unique_ptr<PCWSTR[]> &hostfxrArgv) const
|
||||
{
|
||||
delete[] m_argv;
|
||||
hostfxrArgc = static_cast<DWORD>(m_arguments.size());
|
||||
hostfxrArgv = std::unique_ptr<PCWSTR[]>(new PCWSTR[hostfxrArgc]);
|
||||
for (DWORD i = 0; i < hostfxrArgc; ++i)
|
||||
{
|
||||
hostfxrArgv[i] = m_arguments[i].c_str();
|
||||
}
|
||||
}
|
||||
|
||||
DWORD
|
||||
GetArgc() const
|
||||
{
|
||||
return m_argc;
|
||||
}
|
||||
|
||||
BSTR*
|
||||
GetArgv() const
|
||||
{
|
||||
return m_argv;
|
||||
}
|
||||
|
||||
PCWSTR
|
||||
const std::filesystem::path&
|
||||
GetHostFxrLocation() const
|
||||
{
|
||||
return m_hostFxrLocation.QueryStr();
|
||||
return m_hostFxrLocation;
|
||||
}
|
||||
|
||||
PCWSTR
|
||||
GetExeLocation() const
|
||||
const std::filesystem::path&
|
||||
GetDotnetExeLocation() const
|
||||
{
|
||||
return m_exeLocation.QueryStr();
|
||||
return m_dotnetExeLocation;
|
||||
}
|
||||
|
||||
static
|
||||
|
|
@ -51,12 +55,7 @@ public:
|
|||
_Out_ std::unique_ptr<HOSTFXR_OPTIONS>& ppWrapper);
|
||||
|
||||
private:
|
||||
|
||||
HRESULT Populate(PCWSTR hostFxrLocation, PCWSTR struExeLocation, DWORD argc, BSTR argv[]);
|
||||
|
||||
STRU m_exeLocation;
|
||||
STRU m_hostFxrLocation;
|
||||
|
||||
DWORD m_argc;
|
||||
BSTR* m_argv;
|
||||
const std::filesystem::path m_dotnetExeLocation;
|
||||
const std::filesystem::path m_hostFxrLocation;
|
||||
const std::vector<std::wstring> m_arguments;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ enum APPLICATION_STATUS
|
|||
|
||||
struct APPLICATION_PARAMETER
|
||||
{
|
||||
LPCSTR pzName;
|
||||
PVOID pValue;
|
||||
LPCSTR pzName;
|
||||
const void *pValue;
|
||||
};
|
||||
|
||||
class IAPPLICATION
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
#define ASPNETCORE_EVENT_PROVIDER L"IIS AspNetCore Module V2"
|
||||
#define ASPNETCORE_IISEXPRESS_EVENT_PROVIDER L"IIS Express AspNetCore Module V2"
|
||||
|
||||
#define ASPNETCORE_EVENT_MSG_BUFFER_SIZE 256
|
||||
#define ASPNETCORE_EVENT_PROCESS_START_SUCCESS_MSG L"Application '%s' started process '%d' successfully and process '%d' is listening on port '%d'."
|
||||
#define ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED_MSG L"Maximum rapid fail count per minute of '%d' exceeded."
|
||||
#define ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s' at stage '%s', ErrorCode = '0x%x', assigned port %d, retryCounter '%d'."
|
||||
|
|
@ -39,15 +38,9 @@
|
|||
#define ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG L"Application '%s' was recycled due to configuration change"
|
||||
#define ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG L"Failed to recycle application due to a configuration change at '%s'. Recycling worker process."
|
||||
#define ASPNETCORE_EVENT_MODULE_DISABLED_MSG L"AspNetCore Module is disabled"
|
||||
#define ASPNETCORE_EVENT_INPROCESS_FULL_FRAMEWORK_APP_MSG L"Application '%s' was compiled for .NET Framework. Please compile for .NET core to run the inprocess application or change the process mode to out of process. ErrorCode = '0x%x'."
|
||||
#define ASPNETCORE_EVENT_PORTABLE_APP_DOTNET_MISSING_MSG L"Could not find dotnet.exe on the system PATH environment variable for portable application '%s'. Check that a valid path to dotnet is on the PATH and the bitness of dotnet matches the bitness of the IIS worker process. ErrorCode = '0x%x'."
|
||||
#define ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG L"Could not find the hostfxr directory '%s' in the dotnet directory. ErrorCode = '0x%x'."
|
||||
#define ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG L"Could not find hostfxr.dll in '%s'. ErrorCode = '0x%x'."
|
||||
#define ASPNETCORE_EVENT_HOSTFXR_DLL_INVALID_VERSION_MSG L"Hostfxr version used does not support 'hostfxr_get_native_search_directories', update the version of hostfxr to a higher version. Path to hostfxr: '%s'."
|
||||
#define ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_LEVEL EVENTLOG_ERROR_TYPE
|
||||
#define ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG L"Could not find application executable in '%s'. ErrorCode = '0x%x'."
|
||||
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, ErrorCode = '0x%x. Please check the stderr logs for more information."
|
||||
#define ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG L"Invalid or unknown processPath provided in web.config: processPath = '%s', ErrorCode = '0x%x'."
|
||||
#define ASPNETCORE_EVENT_INPROCESS_RH_ERROR_MSG L"Could not find the assembly '%s' for in-process application. Please confirm the Microsoft.AspNetCore.Server.IIS package is referenced in your application. Captured output: %s"
|
||||
#define ASPNETCORE_EVENT_OUT_OF_PROCESS_RH_MISSING_MSG L"Could not find the assembly '%s' for out-of-process application. Please confirm the assembly is installed correctly for IIS or IISExpress."
|
||||
#define ASPNETCORE_EVENT_INPROCESS_START_SUCCESS_MSG L"Application '%s' started the coreclr in-process successfully."
|
||||
#define ASPNETCORE_EVENT_INPROCESS_START_ERROR_MSG L"Application '%s' wasn't able to start. %s"
|
||||
|
|
|
|||
|
|
@ -392,11 +392,10 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
|
|||
{
|
||||
HRESULT hr;
|
||||
HMODULE hModule = nullptr;
|
||||
DWORD hostfxrArgc = 0;
|
||||
BSTR *hostfxrArgv = NULL;
|
||||
hostfxr_main_fn pProc;
|
||||
std::unique_ptr<HOSTFXR_OPTIONS> hostFxrOptions = NULL;
|
||||
|
||||
DWORD hostfxrArgc = 0;
|
||||
std::unique_ptr<PCWSTR[]> hostfxrArgv;
|
||||
DBG_ASSERT(m_status == MANAGED_APPLICATION_STATUS::STARTING);
|
||||
|
||||
pProc = s_fMainCallback;
|
||||
|
|
@ -428,10 +427,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
|
|||
m_pConfig->QueryArguments()->QueryStr(),
|
||||
hostFxrOptions
|
||||
));
|
||||
|
||||
hostfxrArgc = hostFxrOptions->GetArgc();
|
||||
hostfxrArgv = hostFxrOptions->GetArgv();
|
||||
|
||||
hostFxrOptions->GetArguments(hostfxrArgc, hostfxrArgv);
|
||||
FINISHED_IF_FAILED(SetEnvironementVariablesOnWorkerProcess());
|
||||
}
|
||||
|
||||
|
|
@ -457,7 +453,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
|
|||
// set the callbacks
|
||||
s_Application = this;
|
||||
|
||||
hr = RunDotnetApplication(hostfxrArgc, hostfxrArgv, pProc);
|
||||
hr = RunDotnetApplication(hostfxrArgc, hostfxrArgv.get(), pProc);
|
||||
|
||||
Finished:
|
||||
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ EnsureOutOfProcessInitializtion()
|
|||
FINISHED(S_OK);
|
||||
}
|
||||
|
||||
g_fOutOfProcessInitialize = TRUE;
|
||||
|
||||
g_hWinHttpModule = GetModuleHandle(TEXT("winhttp.dll"));
|
||||
|
||||
g_hAspNetCoreModule = GetModuleHandle(TEXT("aspnetcorev2.dll"));
|
||||
|
|
|
|||
|
|
@ -344,6 +344,10 @@ Failure:
|
|||
{
|
||||
pResponse->SetStatus(400, "Bad Request", 0, hr);
|
||||
}
|
||||
else if (hr == E_APPLICATION_EXITING)
|
||||
{
|
||||
pResponse->SetStatus(503, "Service Unavailable", 0, S_OK, nullptr, TRUE);
|
||||
}
|
||||
else if (fFailedToStartKestrel && !m_pApplication->QueryConfig()->QueryDisableStartUpErrorPage())
|
||||
{
|
||||
HTTP_DATA_CHUNK DataChunk;
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
|
|||
DeletePublishOutput(deploymentResult);
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[ConditionalFact(Skip = "https://github.com/aspnet/IISIntegration/issues/933")]
|
||||
public async Task AppOfflineDroppedWhileSiteFailedToStartInRequestHandler_SiteStops_InProcess()
|
||||
{
|
||||
var deploymentResult = await DeployApp(HostingModel.InProcess);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// 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.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
|
||||
|
|
@ -12,6 +11,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
{
|
||||
public class EventLogHelpers
|
||||
{
|
||||
private static readonly Regex EventLogRegex = new Regex("Event Log: (?<EventLogMessage>.+?)End Event Log Message.", RegexOptions.Singleline | RegexOptions.Compiled);
|
||||
public static void VerifyEventLogEvent(IISDeploymentResult deploymentResult, ITestSink testSink, string expectedRegexMatchString)
|
||||
{
|
||||
Assert.True(deploymentResult.HostProcess.HasExited);
|
||||
|
|
@ -23,9 +23,19 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
builder.Append(context.Message);
|
||||
}
|
||||
|
||||
var eventLogRegex = new Regex($"Event Log: (.*?){expectedRegexMatchString}(.*?)End Event Log Message.", RegexOptions.Singleline);
|
||||
var count = 0;
|
||||
var expectedRegex = new Regex(expectedRegexMatchString, RegexOptions.Singleline);
|
||||
foreach (Match match in EventLogRegex.Matches(builder.ToString()))
|
||||
{
|
||||
var eventLogText = match.Groups["EventLogMessage"].Value;
|
||||
if (expectedRegex.IsMatch(eventLogText))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Matches(eventLogRegex, builder.ToString());
|
||||
Assert.True(count > 0, $"'{expectedRegexMatchString}' didn't match any event log messaged");
|
||||
Assert.True(count < 2, $"'{expectedRegexMatchString}' matched more then one event log message");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,11 +28,6 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
|||
parameters.EnvironmentVariables[DebugEnvironmentVariable] = "console";
|
||||
}
|
||||
|
||||
if (parameters.ApplicationPublisher == null)
|
||||
{
|
||||
throw new InvalidOperationException("All tests should use ApplicationPublisher");
|
||||
}
|
||||
|
||||
return IISApplicationDeployerFactory.Create(parameters, LoggerFactory);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,80 +2,75 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "file_utility.h"
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "hostfxr_utility.h"
|
||||
#include "Environment.h"
|
||||
|
||||
TEST(ParseHostFxrArguments, BasicHostFxrArguments)
|
||||
{
|
||||
DWORD retVal = 0;
|
||||
BSTR* bstrArray;
|
||||
std::vector<std::wstring> bstrArray;
|
||||
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
|
||||
HRESULT hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
|
||||
HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
L"exec \"test.dll\"", // args
|
||||
exeStr, // exe path
|
||||
L"invalid", // physical path to application
|
||||
&retVal, // arg count
|
||||
&bstrArray); // args array.
|
||||
bstrArray); // args array.
|
||||
|
||||
EXPECT_EQ(hr, S_OK);
|
||||
EXPECT_EQ(DWORD(3), retVal);
|
||||
ASSERT_STREQ(exeStr, bstrArray[0]);
|
||||
ASSERT_STREQ(L"exec", bstrArray[1]);
|
||||
ASSERT_STREQ(L"test.dll", bstrArray[2]);
|
||||
EXPECT_EQ(3, bstrArray.size());
|
||||
ASSERT_STREQ(exeStr, bstrArray[0].c_str());
|
||||
ASSERT_STREQ(L"exec", bstrArray[1].c_str());
|
||||
ASSERT_STREQ(L"test.dll", bstrArray[2].c_str());
|
||||
}
|
||||
|
||||
TEST(ParseHostFxrArguments, NoExecProvided)
|
||||
{
|
||||
DWORD retVal = 0;
|
||||
BSTR* bstrArray;
|
||||
std::vector<std::wstring> bstrArray;
|
||||
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
|
||||
|
||||
HRESULT hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
L"test.dll", // args
|
||||
exeStr, // exe path
|
||||
L"ignored", // physical path to application
|
||||
&retVal, // arg count
|
||||
&bstrArray); // args array.
|
||||
bstrArray); // args array.
|
||||
|
||||
EXPECT_EQ(hr, S_OK);
|
||||
EXPECT_EQ(DWORD(2), retVal);
|
||||
ASSERT_STREQ(exeStr, bstrArray[0]);
|
||||
ASSERT_STREQ(L"test.dll", bstrArray[1]);
|
||||
EXPECT_EQ(DWORD(2), bstrArray.size());
|
||||
ASSERT_STREQ(exeStr, bstrArray[0].c_str());
|
||||
ASSERT_STREQ(L"test.dll", bstrArray[1].c_str());
|
||||
}
|
||||
|
||||
TEST(ParseHostFxrArguments, ConvertDllToAbsolutePath)
|
||||
{
|
||||
DWORD retVal = 0;
|
||||
BSTR* bstrArray;
|
||||
std::vector<std::wstring> bstrArray;
|
||||
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
|
||||
|
||||
HRESULT hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
L"exec \"test.dll\"", // args
|
||||
// we need to use existing dll so let's use ntdll that we know exists everywhere
|
||||
auto system32 = Environment::ExpandEnvironmentVariables(L"%WINDIR%\\System32");
|
||||
HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
L"exec \"ntdll.dll\"", // args
|
||||
exeStr, // exe path
|
||||
L"C:/test", // physical path to application
|
||||
&retVal, // arg count
|
||||
&bstrArray); // args array.
|
||||
system32, // physical path to application
|
||||
bstrArray, // args array.
|
||||
true); // expandDllPaths
|
||||
|
||||
EXPECT_EQ(hr, S_OK);
|
||||
EXPECT_EQ(DWORD(3), retVal);
|
||||
ASSERT_STREQ(exeStr, bstrArray[0]);
|
||||
ASSERT_STREQ(L"exec", bstrArray[1]);
|
||||
ASSERT_STREQ(L"C:\\test\\test.dll", bstrArray[2]);
|
||||
EXPECT_EQ(DWORD(3), bstrArray.size());
|
||||
ASSERT_STREQ(exeStr, bstrArray[0].c_str());
|
||||
ASSERT_STREQ(L"exec", bstrArray[1].c_str());
|
||||
ASSERT_STREQ((system32 + L"\\ntdll.dll").c_str(), bstrArray[2].c_str());
|
||||
}
|
||||
|
||||
TEST(ParseHostFxrArguments, ProvideNoArgs_InvalidArgs)
|
||||
{
|
||||
DWORD retVal = 0;
|
||||
BSTR* bstrArray;
|
||||
std::vector<std::wstring> bstrArray;
|
||||
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
|
||||
|
||||
HRESULT hr = HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
ASSERT_THROW(HOSTFXR_UTILITY::ParseHostfxrArguments(
|
||||
L"", // args
|
||||
exeStr, // exe path
|
||||
L"ignored", // physical path to application
|
||||
&retVal, // arg count
|
||||
&bstrArray); // args array.
|
||||
|
||||
EXPECT_EQ(E_INVALIDARG, hr);
|
||||
bstrArray), // args array.
|
||||
HOSTFXR_UTILITY::StartupParametersResolutionException);
|
||||
}
|
||||
|
||||
TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
|
||||
|
|
@ -98,11 +93,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();
|
||||
|
|
@ -118,19 +113,16 @@ TEST(GetAbsolutePathToDotnetFromProgramFiles, BackupWorks)
|
|||
|
||||
TEST(GetHostFxrArguments, InvalidParams)
|
||||
{
|
||||
DWORD retVal = 0;
|
||||
BSTR* bstrArray;
|
||||
STRU struHostFxrDllLocation;
|
||||
STRU struExeLocation;
|
||||
std::vector<std::wstring> bstrArray;
|
||||
std::filesystem::path struHostFxrDllLocation;
|
||||
std::filesystem::path struExeLocation;
|
||||
|
||||
HRESULT hr = HOSTFXR_UTILITY::GetHostFxrParameters(
|
||||
EXPECT_THROW(HOSTFXR_UTILITY::GetHostFxrParameters(
|
||||
L"bogus", // processPath
|
||||
L"", // application physical path, ignored.
|
||||
L"ignored", //arguments
|
||||
NULL,
|
||||
&struExeLocation,
|
||||
&retVal, // arg count
|
||||
&bstrArray); // args array.
|
||||
|
||||
EXPECT_EQ(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), hr);
|
||||
struHostFxrDllLocation,
|
||||
struExeLocation,
|
||||
bstrArray), // args array.
|
||||
HOSTFXR_UTILITY::StartupParametersResolutionException);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||
|
|
@ -40,14 +41,16 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
}
|
||||
|
||||
[ConditionalTheory]
|
||||
[InlineData("bogus")]
|
||||
[InlineData("c:\\random files\\dotnet.exe")]
|
||||
[InlineData(".\\dotnet.exe")]
|
||||
public async Task InvalidProcessPath_ExpectServerError(string path)
|
||||
[InlineData("bogus", "", @"Executable was not found at '.*?\\bogus.exe")]
|
||||
[InlineData("c:\\random files\\dotnet.exe", "something.dll", @"Could not find dotnet.exe at '.*?\\dotnet.exe'")]
|
||||
[InlineData(".\\dotnet.exe", "something.dll", @"Could not find dotnet.exe at '.*?\\.\\dotnet.exe'")]
|
||||
[InlineData("dotnet.exe", "", @"Application arguments are empty.")]
|
||||
[InlineData("dotnet.zip", "", @"Process path 'dotnet.zip' doesn't have '.exe' extension.")]
|
||||
public async Task InvalidProcessPath_ExpectServerError(string path, string arguments, string subError)
|
||||
{
|
||||
var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
|
||||
deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", path));
|
||||
|
||||
deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("arguments", arguments));
|
||||
|
||||
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||
|
||||
|
|
@ -57,7 +60,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
|
||||
StopServer();
|
||||
|
||||
EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, @"Invalid or unknown processPath provided in web\.config: processPath = '.+', ErrorCode = '0x80070002'\.");
|
||||
EventLogHelpers.VerifyEventLogEvent(deploymentResult, TestSink, $@"Application '{Regex.Escape(deploymentResult.ContentRoot)}\\' wasn't able to start. {subError}");
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
|
|
@ -136,6 +139,24 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
|||
Assert.Equal("Hello World", responseText);
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task StartsWithPortableAndBootstraperExe()
|
||||
{
|
||||
var deploymentParameters = _fixture.GetBaseDeploymentParameters(_fixture.InProcessTestSite, publish: true);
|
||||
// rest publisher as it doesn't support additional parameters
|
||||
deploymentParameters.ApplicationPublisher = null;
|
||||
// ReferenceTestTasks is workaround for https://github.com/dotnet/sdk/issues/2482
|
||||
deploymentParameters.AdditionalPublishParameters = "-p:RuntimeIdentifier=win7-x64 -p:UseAppHost=true -p:SelfContained=false -p:ReferenceTestTasks=false";
|
||||
deploymentParameters.RestoreOnPublish = true;
|
||||
var deploymentResult = await DeployAsync(deploymentParameters);
|
||||
|
||||
Assert.True(File.Exists(Path.Combine(deploymentResult.ContentRoot, "InProcessWebSite.exe")));
|
||||
Assert.False(File.Exists(Path.Combine(deploymentResult.ContentRoot, "hostfxr.dll")));
|
||||
Assert.Contains("InProcessWebSite.exe", File.ReadAllText(Path.Combine(deploymentResult.ContentRoot, "web.config")));
|
||||
|
||||
await deploymentResult.AssertStarts();
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
public async Task DetectsOveriddenServer()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ param($Mode)
|
|||
function Setup-appverif($application)
|
||||
{
|
||||
appverif.exe -enable Exceptions Handles Heaps Leak Locks Memory Threadpool TLS SRWLock -for $application
|
||||
$onlyLog = 0x181;
|
||||
$onlyLog = 0x1E1;
|
||||
$codes = @(
|
||||
# Exceptions
|
||||
0x650,
|
||||
|
|
@ -62,8 +62,8 @@ if (!(Test-Path $cdb))
|
|||
|
||||
if ($Mode -eq "Setup")
|
||||
{
|
||||
#Setup-appverif w3wp.exe
|
||||
#Setup-appverif iisexpress.exe
|
||||
Setup-appverif w3wp.exe
|
||||
Setup-appverif iisexpress.exe
|
||||
|
||||
if (!(Test-Path $ldHive ))
|
||||
{
|
||||
|
|
@ -81,14 +81,15 @@ if ($Mode -eq "Shutdown")
|
|||
{
|
||||
Remove-Item $ldHive -Recurse -Force
|
||||
|
||||
#Shutdown-appverif w3wp.exe
|
||||
#Shutdown-appverif iisexpress.exe
|
||||
Shutdown-appverif w3wp.exe
|
||||
Shutdown-appverif iisexpress.exe
|
||||
|
||||
foreach ($dump in (Get-ChildItem -Path $DumpFolder -Filter "*.dmp"))
|
||||
{
|
||||
if (Test-Path $cdb)
|
||||
{
|
||||
& $cdb -z $dump.FullName -y "https://msdl.microsoft.com/download/symbols" -c ".loadby sos coreclr;!sym noisy;.reload /f;.dumpcab -a $($dump.FullName).cab;q;"
|
||||
Remove-Item $dump.FullName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue