Add tests for processpath and arguments (#1331)

This commit is contained in:
Pavel Krymets 2018-08-24 22:20:28 -07:00 committed by GitHub
parent 6ddbfb64b9
commit 9b68c5ce89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 394 additions and 171 deletions

View File

@ -108,7 +108,7 @@ HandlerResolver::LoadRequestHandlerAssembly(IHttpApplication &pApplication, Shim
} }
} }
LOG_INFOF("Loading request handler: %S", handlerDllPath.c_str()); LOG_INFOF(L"Loading request handler: '%ls'", handlerDllPath.c_str());
hRequestHandlerDll = LoadLibrary(handlerDllPath.c_str()); hRequestHandlerDll = LoadLibrary(handlerDllPath.c_str());
if (preventUnload) if (preventUnload)

View File

@ -34,7 +34,7 @@ APPLICATION_INFO::GetOrCreateApplication(
{ {
if (m_pApplication->QueryStatus() == RECYCLED) if (m_pApplication->QueryStatus() == RECYCLED)
{ {
LOG_INFO("Application went offline"); LOG_INFO(L"Application went offline");
// Call to wait for application to complete stopping // Call to wait for application to complete stopping
m_pApplication->Stop(/* fServerInitiated */ false); m_pApplication->Stop(/* fServerInitiated */ false);
@ -50,14 +50,14 @@ APPLICATION_INFO::GetOrCreateApplication(
if (AppOfflineApplication::ShouldBeStarted(httpApplication)) if (AppOfflineApplication::ShouldBeStarted(httpApplication))
{ {
LOG_INFO("Detected app_offline file, creating polling application"); LOG_INFO(L"Detected app_offline file, creating polling application");
m_pApplication.reset(new AppOfflineApplication(httpApplication)); m_pApplication.reset(new AppOfflineApplication(httpApplication));
} }
else else
{ {
FINISHED_IF_FAILED(m_handlerResolver.GetApplicationFactory(httpApplication, m_pApplicationFactory)); FINISHED_IF_FAILED(m_handlerResolver.GetApplicationFactory(httpApplication, m_pApplicationFactory));
LOG_INFO("Creating handler application"); LOG_INFO(L"Creating handler application");
IAPPLICATION * newApplication; IAPPLICATION * newApplication;
FINISHED_IF_FAILED(m_pApplicationFactory->Execute( FINISHED_IF_FAILED(m_pApplicationFactory->Execute(
&m_pServer, &m_pServer,
@ -97,7 +97,7 @@ APPLICATION_INFO::ShutDownApplication(bool fServerInitiated)
if (m_pApplication) if (m_pApplication)
{ {
LOG_ERRORF("Stopping application %S", QueryApplicationInfoKey().c_str()); LOG_ERRORF(L"Stopping application '%ls'", QueryApplicationInfoKey().c_str());
m_pApplication ->Stop(fServerInitiated); m_pApplication ->Stop(fServerInitiated);
m_pApplication = nullptr; m_pApplication = nullptr;
m_pApplicationFactory = nullptr; m_pApplicationFactory = nullptr;

View File

@ -142,7 +142,7 @@ APPLICATION_MANAGER::RecycleApplicationFromManager(
} }
catch (...) catch (...)
{ {
LOG_ERRORF("Failed to stop application %S", application->QueryApplicationInfoKey().c_str()); LOG_ERRORF(L"Failed to stop application '%ls'", application->QueryApplicationInfoKey().c_str());
OBSERVE_CAUGHT_EXCEPTION(); OBSERVE_CAUGHT_EXCEPTION();
// Failed to recycle an application. Log an event // Failed to recycle an application. Log an event

View File

@ -22,7 +22,7 @@ ASPNET_CORE_GLOBAL_MODULE::OnGlobalStopListening(
{ {
UNREFERENCED_PARAMETER(pProvider); UNREFERENCED_PARAMETER(pProvider);
LOG_INFO("ASPNET_CORE_GLOBAL_MODULE::OnGlobalStopListening"); LOG_INFO(L"ASPNET_CORE_GLOBAL_MODULE::OnGlobalStopListening");
if (g_fInShutdown) if (g_fInShutdown)
{ {
@ -56,7 +56,7 @@ ASPNET_CORE_GLOBAL_MODULE::OnGlobalConfigurationChange(
// Retrieve the path that has changed. // Retrieve the path that has changed.
PCWSTR pwszChangePath = pProvider->GetChangePath(); PCWSTR pwszChangePath = pProvider->GetChangePath();
LOG_INFOF("ASPNET_CORE_GLOBAL_MODULE::OnGlobalConfigurationChange %S", pwszChangePath); LOG_INFOF(L"ASPNET_CORE_GLOBAL_MODULE::OnGlobalConfigurationChange '%ls'", pwszChangePath);
// Test for an error. // Test for an error.
if (NULL != pwszChangePath && if (NULL != pwszChangePath &&

View File

@ -20,7 +20,7 @@ public:
VOID Terminate() VOID Terminate()
{ {
LOG_INFO("ASPNET_CORE_GLOBAL_MODULE::Terminate"); LOG_INFO(L"ASPNET_CORE_GLOBAL_MODULE::Terminate");
// Remove the class from memory. // Remove the class from memory.
delete this; delete this;
} }

View File

@ -42,7 +42,7 @@ EventLog::LogEvent(
); );
} }
DebugPrintf(dwEventInfoType == EVENTLOG_ERROR_TYPE ? ASPNETCORE_DEBUG_FLAG_ERROR : ASPNETCORE_DEBUG_FLAG_INFO, "Event Log: %S \r\nEnd Event Log Message.", pstrMsg); DebugPrintfW(dwEventInfoType == EVENTLOG_ERROR_TYPE ? ASPNETCORE_DEBUG_FLAG_ERROR : ASPNETCORE_DEBUG_FLAG_INFO, L"Event Log: '%ls' \r\nEnd Event Log Message.", pstrMsg);
} }
VOID VOID

View File

@ -132,7 +132,7 @@ HRESULT PipeOutputManager::Stop()
if (!LOG_LAST_ERROR_IF(GetExitCodeThread(m_hErrThread, &dwThreadStatus) == 0) && if (!LOG_LAST_ERROR_IF(GetExitCodeThread(m_hErrThread, &dwThreadStatus) == 0) &&
dwThreadStatus == STILL_ACTIVE) dwThreadStatus == STILL_ACTIVE)
{ {
LOG_WARN("Thread reading stdout/err hit timeout, forcibly closing thread."); LOG_WARN(L"Thread reading stdout/err hit timeout, forcibly closing thread.");
TerminateThread(m_hErrThread, STATUS_CONTROL_C_EXIT); TerminateThread(m_hErrThread, STATUS_CONTROL_C_EXIT);
} }
} }

View File

@ -23,7 +23,7 @@ HRESULT
PrintDebugHeader() PrintDebugHeader()
{ {
// Major, minor are stored in dwFileVersionMS field and patch, build in dwFileVersionLS field as pair of 32 bit numbers // Major, minor are stored in dwFileVersionMS field and patch, build in dwFileVersionLS field as pair of 32 bit numbers
DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, "Initializing logs for %S. %S. %S.", DebugPrintfW(ASPNETCORE_DEBUG_FLAG_INFO, L"Initializing logs for '%ls'. %ls. %ls.",
GetModuleName().c_str(), GetModuleName().c_str(),
GetProcessIdString().c_str(), GetProcessIdString().c_str(),
GetVersionInfoString().c_str() GetVersionInfoString().c_str()
@ -86,7 +86,7 @@ std::wstring
GetModuleName() GetModuleName()
{ {
WCHAR path[MAX_PATH]; WCHAR path[MAX_PATH];
LOG_LAST_ERROR_IF(GetModuleFileName(g_hModule, path, sizeof(path))); LOG_LAST_ERROR_IF(!GetModuleFileName(g_hModule, path, sizeof(path)));
return path; return path;
} }
@ -146,7 +146,7 @@ bool CreateDebugLogFile(const std::wstring &debugOutputFile)
{ {
if (g_logFile != INVALID_HANDLE_VALUE) if (g_logFile != INVALID_HANDLE_VALUE)
{ {
LOG_INFOF("Switching debug log files to %S", debugOutputFile.c_str()); LOG_INFOF(L"Switching debug log files to '%ls'", debugOutputFile.c_str());
} }
SRWExclusiveLock lock(g_logFileLock); SRWExclusiveLock lock(g_logFileLock);
@ -296,19 +296,29 @@ IsEnabled(
return ( dwFlag & DEBUG_FLAGS_VAR ); return ( dwFlag & DEBUG_FLAGS_VAR );
} }
void WriteFileEncoded(UINT codePage, HANDLE hFile, const LPCWSTR szString)
{
DWORD nBytesWritten = 0;
auto const encodedByteCount = WideCharToMultiByte(codePage, 0, szString, -1, nullptr, 0, nullptr, nullptr);
auto encodedBytes = std::shared_ptr<CHAR[]>(new CHAR[encodedByteCount]);
WideCharToMultiByte(codePage, 0, szString, -1, encodedBytes.get(), encodedByteCount, nullptr, nullptr);
WriteFile(hFile, encodedBytes.get(), encodedByteCount - 1, &nBytesWritten, nullptr);
}
VOID VOID
DebugPrint( DebugPrintW(
DWORD dwFlag, DWORD dwFlag,
const LPCSTR szString const LPCWSTR szString
) )
{ {
STACK_STRA (strOutput, 256); STACK_STRU (strOutput, 256);
HRESULT hr = S_OK; HRESULT hr = S_OK;
if ( IsEnabled( dwFlag ) ) if ( IsEnabled( dwFlag ) )
{ {
hr = strOutput.SafeSnprintf( hr = strOutput.SafeSnwprintf(
"[%s] %s\r\n", L"[%S] %s\r\n",
DEBUG_LABEL_VAR, szString ); DEBUG_LABEL_VAR, szString );
if (FAILED (hr)) if (FAILED (hr))
@ -316,22 +326,68 @@ DebugPrint(
return; return;
} }
OutputDebugStringA( strOutput.QueryStr() ); OutputDebugString( strOutput.QueryStr() );
DWORD nBytesWritten = 0;
if (IsEnabled(ASPNETCORE_DEBUG_FLAG_CONSOLE)) if (IsEnabled(ASPNETCORE_DEBUG_FLAG_CONSOLE) || g_logFile != INVALID_HANDLE_VALUE)
{ {
auto outputHandle = GetStdHandle(STD_OUTPUT_HANDLE); if (IsEnabled(ASPNETCORE_DEBUG_FLAG_CONSOLE))
WriteFile(outputHandle, strOutput.QueryStr(), strOutput.QueryCB(), &nBytesWritten, nullptr); {
WriteFileEncoded(GetConsoleOutputCP(), GetStdHandle(STD_OUTPUT_HANDLE), strOutput.QueryStr());
}
if (g_logFile != INVALID_HANDLE_VALUE)
{
SRWExclusiveLock lock(g_logFileLock);
SetFilePointer(g_logFile, 0, nullptr, FILE_END);
WriteFileEncoded(CP_UTF8, g_logFile, strOutput.QueryStr());
FlushFileBuffers(g_logFile);
}
}
}
}
VOID
DebugPrintfW(
DWORD dwFlag,
const LPCWSTR szFormat,
...
)
{
STACK_STRU (strCooked,256);
va_list args;
HRESULT hr = S_OK;
if ( IsEnabled( dwFlag ) )
{
va_start( args, szFormat );
hr = strCooked.SafeVsnwprintf(szFormat, args );
va_end( args );
if (FAILED (hr))
{
return;
} }
if (g_logFile != INVALID_HANDLE_VALUE) DebugPrintW( dwFlag, strCooked.QueryStr() );
{ }
SRWExclusiveLock lock(g_logFileLock); }
SetFilePointer(g_logFile, 0, nullptr, FILE_END); VOID
WriteFile(g_logFile, strOutput.QueryStr(), strOutput.QueryCB(), &nBytesWritten, nullptr); DebugPrint(
FlushFileBuffers(g_logFile); DWORD dwFlag,
} const LPCSTR szString
)
{
STACK_STRU (strOutput, 256);
if ( IsEnabled( dwFlag ) )
{
strOutput.CopyA(szString);
DebugPrintW(dwFlag, strOutput.QueryStr());
} }
} }

View File

@ -14,14 +14,14 @@
#define ASPNETCORE_DEBUG_FLAG_CONSOLE 0x00000008 #define ASPNETCORE_DEBUG_FLAG_CONSOLE 0x00000008
#define ASPNETCORE_DEBUG_FLAG_FILE 0x00000010 #define ASPNETCORE_DEBUG_FLAG_FILE 0x00000010
#define LOG_INFO(...) DebugPrint(ASPNETCORE_DEBUG_FLAG_INFO, __VA_ARGS__) #define LOG_INFO(...) DebugPrintW(ASPNETCORE_DEBUG_FLAG_INFO, __VA_ARGS__)
#define LOG_INFOF(...) DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, __VA_ARGS__) #define LOG_INFOF(...) DebugPrintfW(ASPNETCORE_DEBUG_FLAG_INFO, __VA_ARGS__)
#define LOG_WARN(...) DebugPrint(ASPNETCORE_DEBUG_FLAG_WARNING, __VA_ARGS__) #define LOG_WARN(...) DebugPrintW(ASPNETCORE_DEBUG_FLAG_WARNING, __VA_ARGS__)
#define LOG_WARNF(...) DebugPrintf(ASPNETCORE_DEBUG_FLAG_WARNING, __VA_ARGS__) #define LOG_WARNF(...) DebugPrintfW(ASPNETCORE_DEBUG_FLAG_WARNING, __VA_ARGS__)
#define LOG_ERROR(...) DebugPrint(ASPNETCORE_DEBUG_FLAG_ERROR, __VA_ARGS__) #define LOG_ERROR(...) DebugPrintW(ASPNETCORE_DEBUG_FLAG_ERROR, __VA_ARGS__)
#define LOG_ERRORF(...) DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, __VA_ARGS__) #define LOG_ERRORF(...) DebugPrintfW(ASPNETCORE_DEBUG_FLAG_ERROR, __VA_ARGS__)
VOID VOID
DebugInitialize(HMODULE hModule); DebugInitialize(HMODULE hModule);
@ -37,6 +37,20 @@ IsEnabled(
DWORD dwFlag DWORD dwFlag
); );
VOID
DebugPrintW(
DWORD dwFlag,
LPCWSTR szString
);
VOID
DebugPrintfW(
DWORD dwFlag,
LPCWSTR szFormat,
...
);
VOID VOID
DebugPrint( DebugPrint(
DWORD dwFlag, DWORD dwFlag,

View File

@ -23,19 +23,16 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
std::vector<std::wstring> &arguments std::vector<std::wstring> &arguments
) )
{ {
LOG_INFOF("Resolving hostfxr parameters for application: '%S' arguments: '%S' path: '%S'", LOG_INFOF(L"Resolving hostfxr parameters for application: '%ls' arguments: '%ls' path: '%ls'",
processPath.c_str(), processPath.c_str(),
applicationArguments.c_str(), applicationArguments.c_str(),
applicationPhysicalPath.c_str()); applicationPhysicalPath.c_str());
arguments = std::vector<std::wstring>();
fs::path expandedProcessPath = Environment::ExpandEnvironmentVariables(processPath); fs::path expandedProcessPath = Environment::ExpandEnvironmentVariables(processPath);
const auto expandedApplicationArguments = Environment::ExpandEnvironmentVariables(applicationArguments); const auto expandedApplicationArguments = Environment::ExpandEnvironmentVariables(applicationArguments);
LOG_INFOF("Expanded hostfxr parameters for application: '%S' arguments: '%S'", LOG_INFOF(L"Known dotnet.exe location: '%ls'", dotnetExePath.c_str());
expandedProcessPath.c_str(),
expandedApplicationArguments.c_str());
LOG_INFOF("Known dotnet.exe location: '%S'", dotnetExePath.c_str());
if (!expandedProcessPath.has_extension()) if (!expandedProcessPath.has_extension())
{ {
@ -50,7 +47,12 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
// Check if the absolute path is to dotnet or not. // Check if the absolute path is to dotnet or not.
if (IsDotnetExecutable(expandedProcessPath)) if (IsDotnetExecutable(expandedProcessPath))
{ {
LOG_INFOF("Process path '%S' is dotnet, treating application as portable", expandedProcessPath.c_str()); LOG_INFOF(L"Process path '%ls' is dotnet, treating application as portable", expandedProcessPath.c_str());
if (applicationArguments.empty())
{
throw StartupParametersResolutionException(L"Application arguments are empty.");
}
if (dotnetExePath.empty()) if (dotnetExePath.empty())
{ {
@ -59,16 +61,16 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
hostFxrDllPath = GetAbsolutePathToHostFxr(dotnetExePath); hostFxrDllPath = GetAbsolutePathToHostFxr(dotnetExePath);
ParseHostfxrArguments( arguments.push_back(dotnetExePath);
AppendArguments(
expandedApplicationArguments, expandedApplicationArguments,
dotnetExePath,
applicationPhysicalPath, applicationPhysicalPath,
arguments, arguments,
true); true);
} }
else else
{ {
LOG_INFOF("Process path '%S' is not dotnet, treating application as standalone or portable with bootstrapper", expandedProcessPath.c_str()); LOG_INFOF(L"Process path '%ls' is not dotnet, treating application as standalone or portable with bootstrapper", expandedProcessPath.c_str());
auto executablePath = expandedProcessPath; auto executablePath = expandedProcessPath;
@ -87,21 +89,23 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
auto applicationDllPath = executablePath; auto applicationDllPath = executablePath;
applicationDllPath.replace_extension(".dll"); applicationDllPath.replace_extension(".dll");
LOG_INFOF("Checking application.dll at %S", applicationDllPath.c_str()); LOG_INFOF(L"Checking application.dll at '%ls'", applicationDllPath.c_str());
if (!is_regular_file(applicationDllPath)) if (!is_regular_file(applicationDllPath))
{ {
throw StartupParametersResolutionException(format(L"Application .dll was not found at %s", applicationDllPath.c_str())); throw StartupParametersResolutionException(format(L"Application .dll was not found at %s", applicationDllPath.c_str()));
} }
hostFxrDllPath = executablePath.parent_path() / "hostfxr.dll"; hostFxrDllPath = executablePath.parent_path() / "hostfxr.dll";
LOG_INFOF("Checking hostfxr.dll at %S", hostFxrDllPath.c_str()); LOG_INFOF(L"Checking hostfxr.dll at '%ls'", hostFxrDllPath.c_str());
if (is_regular_file(hostFxrDllPath)) if (is_regular_file(hostFxrDllPath))
{ {
LOG_INFOF("hostfxr.dll found app local at '%S', treating application as standalone", hostFxrDllPath.c_str()); LOG_INFOF(L"hostfxr.dll found app local at '%ls', treating application as standalone", hostFxrDllPath.c_str());
// For standalone apps we need .exe to be argv[0], dll would be discovered next to it
arguments.push_back(executablePath);
} }
else else
{ {
LOG_INFOF("hostfxr.dll found app local at '%S', treating application as portable with launcher", hostFxrDllPath.c_str()); LOG_INFOF(L"hostfxr.dll found app local at '%ls', treating application as portable with launcher", hostFxrDllPath.c_str());
// passing "dotnet" here because we don't know where dotnet.exe should come from // passing "dotnet" here because we don't know where dotnet.exe should come from
// so trying all fallbacks is appropriate // so trying all fallbacks is appropriate
@ -109,13 +113,15 @@ HOSTFXR_UTILITY::GetHostFxrParameters(
{ {
dotnetExePath = GetAbsolutePathToDotnet(applicationPhysicalPath, L"dotnet"); dotnetExePath = GetAbsolutePathToDotnet(applicationPhysicalPath, L"dotnet");
} }
executablePath = dotnetExePath;
hostFxrDllPath = GetAbsolutePathToHostFxr(dotnetExePath); hostFxrDllPath = GetAbsolutePathToHostFxr(dotnetExePath);
// For portable with launcher apps we need dotnet.exe to be argv[0] and .dll be argv[1]
arguments.push_back(dotnetExePath);
arguments.push_back(applicationDllPath);
} }
ParseHostfxrArguments( AppendArguments(
applicationDllPath.generic_wstring() + L" " + expandedApplicationArguments, expandedApplicationArguments,
executablePath,
applicationPhysicalPath, applicationPhysicalPath,
arguments); arguments);
} }
@ -137,29 +143,49 @@ HOSTFXR_UTILITY::IsDotnetExecutable(const std::filesystem::path & dotnetPath)
} }
void void
HOSTFXR_UTILITY::ParseHostfxrArguments( HOSTFXR_UTILITY::AppendArguments(
const std::wstring &applicationArguments, const std::wstring &applicationArguments,
const fs::path &applicationExePath,
const fs::path &applicationPhysicalPath, const fs::path &applicationPhysicalPath,
std::vector<std::wstring> &arguments, std::vector<std::wstring> &arguments,
bool expandDllPaths bool expandDllPaths
) )
{ {
LOG_INFOF("Resolving hostfxr_main arguments application: '%S' arguments: '%S' path: %S", applicationExePath.c_str(), applicationArguments.c_str(), applicationPhysicalPath.c_str());
arguments = std::vector<std::wstring>();
arguments.push_back(applicationExePath);
if (applicationArguments.empty()) if (applicationArguments.empty())
{ {
throw StartupParametersResolutionException(L"Application arguments are empty."); return;
}
// don't throw while trying to expand arguments
std::error_code ec;
// Try to treat entire arguments section as a single path
if (expandDllPaths)
{
fs::path argumentAsPath = applicationArguments;
if (is_regular_file(argumentAsPath, ec))
{
LOG_INFOF(L"Treating '%ls' as a single path argument", applicationArguments.c_str());
arguments.push_back(argumentAsPath);
return;
}
if (argumentAsPath.is_relative())
{
argumentAsPath = applicationPhysicalPath / argumentAsPath;
if (is_regular_file(argumentAsPath, ec))
{
LOG_INFOF(L"Converted argument '%ls' to '%ls'", applicationArguments.c_str(), argumentAsPath.c_str());
arguments.push_back(argumentAsPath);
return;
}
}
} }
int argc = 0; int argc = 0;
auto pwzArgs = std::unique_ptr<LPWSTR[], LocalFreeDeleter>(CommandLineToArgvW(applicationArguments.c_str(), &argc)); auto pwzArgs = std::unique_ptr<LPWSTR[], LocalFreeDeleter>(CommandLineToArgvW(applicationArguments.c_str(), &argc));
if (!pwzArgs) if (!pwzArgs)
{ {
throw StartupParametersResolutionException(format(L"Unable parse command line arguments '%s' or '%s'", applicationArguments.c_str())); throw StartupParametersResolutionException(format(L"Unable parse command line arguments '%s'", applicationArguments.c_str()));
} }
for (int intArgsProcessed = 0; intArgsProcessed < argc; intArgsProcessed++) for (int intArgsProcessed = 0; intArgsProcessed < argc; intArgsProcessed++)
@ -173,9 +199,9 @@ HOSTFXR_UTILITY::ParseHostfxrArguments(
if (argumentAsPath.is_relative()) if (argumentAsPath.is_relative())
{ {
argumentAsPath = applicationPhysicalPath / argumentAsPath; argumentAsPath = applicationPhysicalPath / argumentAsPath;
if (exists(argumentAsPath)) if (is_regular_file(argumentAsPath, ec))
{ {
LOG_INFOF("Converted argument '%S' to %S", argument.c_str(), argumentAsPath.c_str()); LOG_INFOF(L"Converted argument '%ls' to '%ls'", argument.c_str(), argumentAsPath.c_str());
argument = argumentAsPath; argument = argumentAsPath;
} }
} }
@ -183,11 +209,6 @@ HOSTFXR_UTILITY::ParseHostfxrArguments(
arguments.push_back(argument); arguments.push_back(argument);
} }
for (size_t i = 0; i < arguments.size(); i++)
{
LOG_INFOF("Argument[%d] = %S", i, arguments[i].c_str());
}
} }
// The processPath ends with dotnet.exe or dotnet // The processPath ends with dotnet.exe or dotnet
@ -199,7 +220,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
const fs::path & requestedPath const fs::path & requestedPath
) )
{ {
LOG_INFOF("Resolving absolute path to dotnet.exe from %S", requestedPath.c_str()); LOG_INFOF(L"Resolving absolute path to dotnet.exe from '%ls'", requestedPath.c_str());
auto processPath = requestedPath; auto processPath = requestedPath;
if (processPath.is_relative()) if (processPath.is_relative())
@ -212,7 +233,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
// //
if (is_regular_file(processPath)) if (is_regular_file(processPath))
{ {
LOG_INFOF("Found dotnet.exe at %S", processPath.c_str()); LOG_INFOF(L"Found dotnet.exe at '%ls'", processPath.c_str());
return processPath; return processPath;
} }
@ -223,7 +244,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
// Only do it if no path is specified // Only do it if no path is specified
if (requestedPath.has_parent_path()) if (requestedPath.has_parent_path())
{ {
LOG_INFOF("Absolute path to dotnet.exe was not found at %S", requestedPath.c_str()); LOG_INFOF(L"Absolute path to dotnet.exe was not found at '%ls'", requestedPath.c_str());
throw StartupParametersResolutionException(format(L"Could not find dotnet.exe at '%s'", processPath.c_str())); throw StartupParametersResolutionException(format(L"Could not find dotnet.exe at '%s'", processPath.c_str()));
} }
@ -231,7 +252,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
const auto dotnetViaWhere = InvokeWhereToFindDotnet(); const auto dotnetViaWhere = InvokeWhereToFindDotnet();
if (dotnetViaWhere.has_value()) if (dotnetViaWhere.has_value())
{ {
LOG_INFOF("Found dotnet.exe via where.exe invocation at %S", dotnetViaWhere.value().c_str()); LOG_INFOF(L"Found dotnet.exe via where.exe invocation at '%ls'", dotnetViaWhere.value().c_str());
return dotnetViaWhere.value(); return dotnetViaWhere.value();
} }
@ -239,12 +260,12 @@ HOSTFXR_UTILITY::GetAbsolutePathToDotnet(
const auto programFilesLocation = GetAbsolutePathToDotnetFromProgramFiles(); const auto programFilesLocation = GetAbsolutePathToDotnetFromProgramFiles();
if (programFilesLocation.has_value()) if (programFilesLocation.has_value())
{ {
LOG_INFOF("Found dotnet.exe in Program Files at %S", programFilesLocation.value().c_str()); LOG_INFOF(L"Found dotnet.exe in Program Files at '%ls'", programFilesLocation.value().c_str());
return programFilesLocation.value(); return programFilesLocation.value();
} }
LOG_INFOF("dotnet.exe not found"); LOG_INFOF(L"dotnet.exe not found");
throw StartupParametersResolutionException(format( throw StartupParametersResolutionException(format(
L"Could not find dotnet.exe at '%s' or using the system PATH environment variable." 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.", " Check that a valid path to dotnet is on the PATH and the bitness of dotnet matches the bitness of the IIS worker process.",
@ -259,7 +280,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
std::vector<std::wstring> versionFolders; std::vector<std::wstring> versionFolders;
const auto hostFxrBase = dotnetPath.parent_path() / "host" / "fxr"; const auto hostFxrBase = dotnetPath.parent_path() / "host" / "fxr";
LOG_INFOF("Resolving absolute path to hostfxr.dll from %S", dotnetPath.c_str()); LOG_INFOF(L"Resolving absolute path to hostfxr.dll from '%ls'", dotnetPath.c_str());
if (!is_directory(hostFxrBase)) if (!is_directory(hostFxrBase))
{ {
@ -281,7 +302,7 @@ HOSTFXR_UTILITY::GetAbsolutePathToHostFxr(
throw StartupParametersResolutionException(format(L"hostfxr.dll not found at '%s'", hostFxrPath.c_str())); throw StartupParametersResolutionException(format(L"hostfxr.dll not found at '%s'", hostFxrPath.c_str()));
} }
LOG_INFOF("hostfxr.dll located at %S", hostFxrPath.c_str()); LOG_INFOF(L"hostfxr.dll located at '%ls'", hostFxrPath.c_str());
return hostFxrPath; return hostFxrPath;
} }
@ -323,7 +344,7 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
securityAttributes.lpSecurityDescriptor = NULL; securityAttributes.lpSecurityDescriptor = NULL;
securityAttributes.bInheritHandle = TRUE; securityAttributes.bInheritHandle = TRUE;
LOG_INFO("Invoking where.exe to find dotnet.exe"); LOG_INFO(L"Invoking where.exe to find dotnet.exe");
// Create a read/write pipe that will be used for reading the result of where.exe // Create a read/write pipe that will be used for reading the result of where.exe
FINISHED_LAST_ERROR_IF(!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0)); FINISHED_LAST_ERROR_IF(!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0));
@ -398,7 +419,7 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
FINISHED_IF_FAILED(struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead)); FINISHED_IF_FAILED(struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead));
LOG_INFOF("where.exe invocation returned: %S", struDotnetLocationsString.QueryStr()); LOG_INFOF(L"where.exe invocation returned: '%ls'", struDotnetLocationsString.QueryStr());
// Check the bitness of the currently running process // Check the bitness of the currently running process
// matches the dotnet.exe found. // matches the dotnet.exe found.
@ -417,7 +438,7 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
fIsCurrentProcess64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; fIsCurrentProcess64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
} }
LOG_INFOF("Current process bitness type detected as isX64=%d", fIsCurrentProcess64Bit); LOG_INFOF(L"Current process bitness type detected as isX64=%d", fIsCurrentProcess64Bit);
while (TRUE) while (TRUE)
{ {
@ -431,14 +452,14 @@ HOSTFXR_UTILITY::InvokeWhereToFindDotnet()
// \r\n is two wchars, so add 2 here. // \r\n is two wchars, so add 2 here.
prevIndex = index + 2; prevIndex = index + 2;
LOG_INFOF("Processing entry %S", struDotnetSubstring.QueryStr()); LOG_INFOF(L"Processing entry '%ls'", struDotnetSubstring.QueryStr());
if (LOG_LAST_ERROR_IF(!GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType))) if (LOG_LAST_ERROR_IF(!GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType)))
{ {
continue; continue;
} }
LOG_INFOF("Binary type %d", dwBinaryType); LOG_INFOF(L"Binary type %d", dwBinaryType);
if (fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY)) if (fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY))
{ {

View File

@ -45,9 +45,8 @@ public:
static static
void void
ParseHostfxrArguments( AppendArguments(
const std::wstring &arugments, const std::wstring &arugments,
const std::filesystem::path &exePath,
const std::filesystem::path &applicationPhysicalPath, const std::filesystem::path &applicationPhysicalPath,
std::vector<std::wstring> &arguments, std::vector<std::wstring> &arguments,
bool expandDllPaths = false bool expandDllPaths = false

View File

@ -33,6 +33,11 @@ HRESULT HOSTFXR_OPTIONS::Create(
knownDotnetLocation, knownDotnetLocation,
arguments); arguments);
LOG_INFOF(L"Parsed hostfxr options: dotnet location: '%ls' hostfxr path: '%ls' arguments:", knownDotnetLocation.c_str(), hostFxrDllPath.c_str());
for (size_t i = 0; i < arguments.size(); i++)
{
LOG_INFOF(L"Argument[%d] = '%ls'", i, arguments[i].c_str());
}
ppWrapper = std::make_unique<HOSTFXR_OPTIONS>(knownDotnetLocation, hostFxrDllPath, arguments); ppWrapper = std::make_unique<HOSTFXR_OPTIONS>(knownDotnetLocation, hostFxrDllPath, arguments);
} }
catch (HOSTFXR_UTILITY::StartupParametersResolutionException &resolutionException) catch (HOSTFXR_UTILITY::StartupParametersResolutionException &resolutionException)

View File

@ -443,7 +443,7 @@ IN_PROCESS_APPLICATION::ExecuteApplication(
FINISHED_IF_FAILED(SetEnvironementVariablesOnWorkerProcess()); FINISHED_IF_FAILED(SetEnvironementVariablesOnWorkerProcess());
} }
LOG_INFO("Starting managed application"); LOG_INFO(L"Starting managed application");
if (m_pLoggerProvider == NULL) if (m_pLoggerProvider == NULL)
{ {
@ -547,12 +547,12 @@ IN_PROCESS_APPLICATION::RunDotnetApplication(DWORD argc, CONST PCWSTR* argv, hos
hr = HRESULT_FROM_WIN32(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError());
} }
LOG_INFOF("Managed application exited with code %d", m_ProcessExitCode); LOG_INFOF(L"Managed application exited with code %d", m_ProcessExitCode);
} }
__except(GetExceptionCode() != 0) __except(GetExceptionCode() != 0)
{ {
LOG_INFOF("Managed threw an exception %d", GetExceptionCode()); LOG_INFOF(L"Managed threw an exception %d", GetExceptionCode());
hr = HRESULT_FROM_WIN32(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError());
} }

View File

@ -8,7 +8,7 @@
HRESULT AppOfflineTrackingApplication::StartMonitoringAppOffline() HRESULT AppOfflineTrackingApplication::StartMonitoringAppOffline()
{ {
LOG_INFOF("Starting app_offline monitoring in application %S", m_applicationPath.c_str()); LOG_INFOF(L"Starting app_offline monitoring in application '%ls'", m_applicationPath.c_str());
HRESULT hr = StartMonitoringAppOflineImpl(); HRESULT hr = StartMonitoringAppOflineImpl();
if (FAILED_LOG(hr)) if (FAILED_LOG(hr))
@ -56,7 +56,7 @@ void AppOfflineTrackingApplication::OnAppOffline()
return; return;
} }
LOG_INFOF("Received app_offline notification in application %S", m_applicationPath.c_str()); LOG_INFOF(L"Received app_offline notification in application '%ls'", m_applicationPath.c_str());
EventLog::Info( EventLog::Info(
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE, ASPNETCORE_EVENT_RECYCLE_APPOFFLINE,
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG, ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG,

View File

@ -137,7 +137,7 @@ Win32 error
DWORD dwErrorStatus; DWORD dwErrorStatus;
ULONG_PTR completionKey; ULONG_PTR completionKey;
LOG_INFO("Starting file watcher thread"); LOG_INFO(L"Starting file watcher thread");
pFileMonitor = (FILE_WATCHER*)pvArg; pFileMonitor = (FILE_WATCHER*)pvArg;
DBG_ASSERT(pFileMonitor != NULL); DBG_ASSERT(pFileMonitor != NULL);
@ -177,7 +177,7 @@ Win32 error
pFileMonitor->m_fThreadExit = TRUE; pFileMonitor->m_fThreadExit = TRUE;
LOG_INFO("Stopping file watcher thread"); LOG_INFO(L"Stopping file watcher thread");
ExitThread(0); ExitThread(0);
} }

View File

@ -4,6 +4,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests;
using Microsoft.AspNetCore.Server.IntegrationTesting.IIS; using Microsoft.AspNetCore.Server.IntegrationTesting.IIS;
using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Logging.Testing;
using Xunit.Abstractions; using Xunit.Abstractions;
@ -37,6 +38,13 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
return (IISDeploymentResult)await _deployer.DeployAsync(); return (IISDeploymentResult)await _deployer.DeployAsync();
} }
protected virtual async Task<IISDeploymentResult> StartAsync(IISDeploymentParameters parameters)
{
var result = await DeployAsync(parameters);
await result.AssertStarts();
return result;
}
public override void Dispose() public override void Dispose()
{ {
StopServer(false); StopServer(false);

View File

@ -65,12 +65,15 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
{ {
foreach (DirectoryInfo directoryInfo in source.GetDirectories()) foreach (DirectoryInfo directoryInfo in source.GetDirectories())
{ {
CopyFiles(directoryInfo, target.CreateSubdirectory(directoryInfo.Name), logger); if (directoryInfo.FullName != target.FullName)
{
CopyFiles(directoryInfo, target.CreateSubdirectory(directoryInfo.Name), logger);
}
} }
logger.LogDebug($"Processing {target.FullName}"); logger?.LogDebug($"Processing {target.FullName}");
foreach (FileInfo fileInfo in source.GetFiles()) foreach (FileInfo fileInfo in source.GetFiles())
{ {
logger.LogDebug($" Copying {fileInfo.Name}"); logger?.LogDebug($" Copying {fileInfo.Name}");
var destFileName = Path.Combine(target.FullName, fileInfo.Name); var destFileName = Path.Combine(target.FullName, fileInfo.Name);
fileInfo.CopyTo(destFileName); fileInfo.CopyTo(destFileName);
} }

View File

@ -11,64 +11,58 @@
TEST(ParseHostFxrArguments, BasicHostFxrArguments) TEST(ParseHostFxrArguments, BasicHostFxrArguments)
{ {
std::vector<std::wstring> bstrArray; std::vector<std::wstring> bstrArray;
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
HOSTFXR_UTILITY::ParseHostfxrArguments( HOSTFXR_UTILITY::AppendArguments(
L"exec \"test.dll\"", // args L"exec \"test.dll\"", // args
exeStr, // exe path
L"invalid", // physical path to application L"invalid", // physical path to application
bstrArray); // args array. bstrArray); // args array.
EXPECT_EQ(3, bstrArray.size()); EXPECT_EQ(2, bstrArray.size());
ASSERT_STREQ(exeStr, bstrArray[0].c_str()); ASSERT_STREQ(L"exec", bstrArray[0].c_str());
ASSERT_STREQ(L"exec", bstrArray[1].c_str()); ASSERT_STREQ(L"test.dll", bstrArray[1].c_str());
ASSERT_STREQ(L"test.dll", bstrArray[2].c_str());
} }
TEST(ParseHostFxrArguments, NoExecProvided) TEST(ParseHostFxrArguments, NoExecProvided)
{ {
std::vector<std::wstring> bstrArray; std::vector<std::wstring> bstrArray;
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
HOSTFXR_UTILITY::ParseHostfxrArguments( HOSTFXR_UTILITY::AppendArguments(
L"test.dll", // args L"test.dll", // args
exeStr, // exe path
L"ignored", // physical path to application L"ignored", // physical path to application
bstrArray); // args array. bstrArray); // args array.
EXPECT_EQ(DWORD(2), bstrArray.size()); EXPECT_EQ(1, bstrArray.size());
ASSERT_STREQ(exeStr, bstrArray[0].c_str()); ASSERT_STREQ(L"test.dll", bstrArray[0].c_str());
ASSERT_STREQ(L"test.dll", bstrArray[1].c_str());
} }
TEST(ParseHostFxrArguments, ConvertDllToAbsolutePath) TEST(ParseHostFxrArguments, ConvertDllToAbsolutePath)
{ {
std::vector<std::wstring> bstrArray; std::vector<std::wstring> bstrArray;
PCWSTR exeStr = L"C:/Program Files/dotnet.exe";
// we need to use existing dll so let's use ntdll that we know exists everywhere // we need to use existing dll so let's use ntdll that we know exists everywhere
auto system32 = Environment::ExpandEnvironmentVariables(L"%WINDIR%\\System32"); auto system32 = Environment::ExpandEnvironmentVariables(L"%WINDIR%\\System32");
HOSTFXR_UTILITY::ParseHostfxrArguments( HOSTFXR_UTILITY::AppendArguments(
L"exec \"ntdll.dll\"", // args L"exec \"ntdll.dll\"", // args
exeStr, // exe path
system32, // physical path to application system32, // physical path to application
bstrArray, // args array. bstrArray, // args array.
true); // expandDllPaths true); // expandDllPaths
EXPECT_EQ(DWORD(3), bstrArray.size()); EXPECT_EQ(2, bstrArray.size());
ASSERT_STREQ(exeStr, bstrArray[0].c_str()); ASSERT_STREQ(L"exec", bstrArray[0].c_str());
ASSERT_STREQ(L"exec", bstrArray[1].c_str()); ASSERT_STREQ((system32 + L"\\ntdll.dll").c_str(), bstrArray[1].c_str());
ASSERT_STREQ((system32 + L"\\ntdll.dll").c_str(), bstrArray[2].c_str());
} }
TEST(ParseHostFxrArguments, ProvideNoArgs_InvalidArgs) TEST(ParseHostFxrArguments, ProvideNoArgs_InvalidArgs)
{ {
std::vector<std::wstring> bstrArray; std::vector<std::wstring> bstrArray;
PCWSTR exeStr = L"C:/Program Files/dotnet.exe"; std::filesystem::path struHostFxrDllLocation;
std::filesystem::path struExeLocation;
ASSERT_THROW(HOSTFXR_UTILITY::ParseHostfxrArguments( EXPECT_THROW(HOSTFXR_UTILITY::GetHostFxrParameters(
L"", // args L"dotnet", // processPath
exeStr, // exe path L"some\\path", // application physical path, ignored.
L"ignored", // physical path to application L"", //arguments
struHostFxrDllLocation,
struExeLocation,
bstrArray), // args array. bstrArray), // args array.
HOSTFXR_UTILITY::StartupParametersResolutionException); HOSTFXR_UTILITY::StartupParametersResolutionException);
} }

View File

@ -33,13 +33,10 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
public async Task ExpandEnvironmentVariableInWebConfig() public async Task ExpandEnvironmentVariableInWebConfig()
{ {
// Point to dotnet installed in user profile. // Point to dotnet installed in user profile.
await AssertStarts( var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
deploymentParameters => deploymentParameters.EnvironmentVariables["DotnetPath"] = _dotnetLocation;
{ deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", "%DotnetPath%"));
deploymentParameters.EnvironmentVariables["DotnetPath"] = _dotnetLocation; await StartAsync(deploymentParameters);
deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", "%DotnetPath%"));
}
);
} }
[ConditionalTheory] [ConditionalTheory]
@ -68,26 +65,23 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
[ConditionalFact] [ConditionalFact]
public async Task StartsWithDotnetLocationWithoutExe() public async Task StartsWithDotnetLocationWithoutExe()
{ {
var dotnetLocationWithoutExtension = _dotnetLocation.Substring(0, _dotnetLocation.LastIndexOf(".")); var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
await AssertStarts( var dotnetLocationWithoutExtension = _dotnetLocation.Substring(0, _dotnetLocation.LastIndexOf("."));
deploymentParameters => deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", dotnetLocationWithoutExtension));
{
deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", dotnetLocationWithoutExtension)); await StartAsync(deploymentParameters);
}
);
} }
[ConditionalFact] [ConditionalFact]
public async Task StartsWithDotnetLocationUppercase() public async Task StartsWithDotnetLocationUppercase()
{ {
var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
var dotnetLocationWithoutExtension = _dotnetLocation.Substring(0, _dotnetLocation.LastIndexOf(".")).ToUpperInvariant(); var dotnetLocationWithoutExtension = _dotnetLocation.Substring(0, _dotnetLocation.LastIndexOf(".")).ToUpperInvariant();
await AssertStarts( deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", dotnetLocationWithoutExtension));
deploymentParameters =>
{ await StartAsync(deploymentParameters);
deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", dotnetLocationWithoutExtension));
}
);
} }
[ConditionalTheory] [ConditionalTheory]
@ -95,32 +89,18 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
[InlineData("dotnet.EXE")] [InlineData("dotnet.EXE")]
public async Task StartsWithDotnetOnThePath(string path) public async Task StartsWithDotnetOnThePath(string path)
{ {
await AssertStarts( var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
deploymentParameters =>
{ deploymentParameters.EnvironmentVariables["PATH"] = Path.GetDirectoryName(_dotnetLocation);
deploymentParameters.EnvironmentVariables["PATH"] = Path.GetDirectoryName(_dotnetLocation); deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", path));
deploymentParameters.WebConfigActionList.Add(WebConfigHelpers.AddOrModifyAspNetCoreSection("processPath", path));
} var deploymentResult = await DeployAsync(deploymentParameters);
); await deploymentResult.AssertStarts();
// Verify that in this scenario where.exe was invoked only once by shim and request handler uses cached value // Verify that in this scenario where.exe was invoked only once by shim and request handler uses cached value
Assert.Equal(1, TestSink.Writes.Count(w => w.Message.Contains("Invoking where.exe to find dotnet.exe"))); Assert.Equal(1, TestSink.Writes.Count(w => w.Message.Contains("Invoking where.exe to find dotnet.exe")));
} }
private async Task AssertStarts(Action<IISDeploymentParameters> preDeploy = null)
{
var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
preDeploy?.Invoke(deploymentParameters);
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", responseText);
}
public static TestMatrix TestVariants public static TestMatrix TestVariants
=> TestMatrix.ForServers(DeployerSelector.ServerType) => TestMatrix.ForServers(DeployerSelector.ServerType)
.WithTfms(Tfm.NetCoreApp22) .WithTfms(Tfm.NetCoreApp22)
@ -132,13 +112,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
public async Task HelloWorld(TestVariant variant) public async Task HelloWorld(TestVariant variant)
{ {
var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant, publish: true); var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant, publish: true);
await StartAsync(deploymentParameters);
var deploymentResult = await DeployAsync(deploymentParameters);
var response = await deploymentResult.HttpClient.GetAsync("/HelloWorld");
var responseText = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", responseText);
} }
[ConditionalFact] [ConditionalFact]
@ -194,7 +168,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
[ConditionalTheory] [ConditionalTheory]
[MemberData(nameof(InvalidConfigTransformationsScenarios))] [MemberData(nameof(InvalidConfigTransformationsScenarios))]
public async Task StartsWithWebConfigVariationsPortable(string scenario) public async Task ReportsWebConfigAuthoringErrors(string scenario)
{ {
var (expectedError, action) = InvalidConfigTransformations[scenario]; var (expectedError, action) = InvalidConfigTransformations[scenario];
var iisDeploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true); var iisDeploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
@ -227,5 +201,148 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
)); ));
return dictionary; return dictionary;
} }
private static Dictionary<string, Func<IISDeploymentParameters, string>> PortableConfigTransformations = InitPortableWebConfigTransformations();
public static IEnumerable<object[]> PortableConfigTransformationsScenarios => PortableConfigTransformations.ToTheoryData();
[ConditionalTheory]
[MemberData(nameof(PortableConfigTransformationsScenarios))]
public async Task StartsWithWebConfigVariationsPortable(string scenario)
{
var action = PortableConfigTransformations[scenario];
var iisDeploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
var expectedArguments = action(iisDeploymentParameters);
var result = await DeployAsync(iisDeploymentParameters);
Assert.Equal(expectedArguments, await result.HttpClient.GetStringAsync("/CommandLineArgs"));
}
public static Dictionary<string, Func<IISDeploymentParameters, string>> InitPortableWebConfigTransformations()
{
var dictionary = new Dictionary<string, Func<IISDeploymentParameters, string>>();
var pathWithSpace = "\u03c0 \u2260 3\u00b714";
dictionary.Add("App in bin subdirectory full path to dll using exec and quotes",
parameters => {
MoveApplication(parameters, "bin");
TransformArguments(parameters, (arguments, root) => "exec " + Path.Combine(root, "bin", arguments));
return "";
});
dictionary.Add("App in subdirectory with space",
parameters => {
MoveApplication(parameters, pathWithSpace);
TransformArguments(parameters, (arguments, root) => Path.Combine(pathWithSpace, arguments));
return "";
});
dictionary.Add("App in subdirectory with space and full path to dll",
parameters => {
MoveApplication(parameters, pathWithSpace);
TransformArguments(parameters, (arguments, root) => Path.Combine(root, pathWithSpace, arguments));
return "";
});
dictionary.Add("App in bin subdirectory with space full path to dll using exec and quotes",
parameters => {
MoveApplication(parameters, pathWithSpace);
TransformArguments(parameters, (arguments, root) => "exec \"" + Path.Combine(root, pathWithSpace, arguments) + "\" extra arguments");
return "extra|arguments";
});
dictionary.Add("App in bin subdirectory and quoted argument",
parameters => {
MoveApplication(parameters, "bin");
TransformArguments(parameters, (arguments, root) => Path.Combine("bin", arguments) + " \"extra argument\"");
return "extra argument";
});
dictionary.Add("App in bin subdirectory full path to dll",
parameters => {
MoveApplication(parameters, "bin");
TransformArguments(parameters, (arguments, root) => Path.Combine(root, "bin", arguments) + " extra arguments");
return "extra|arguments";
});
return dictionary;
}
private static Dictionary<string, Func<IISDeploymentParameters, string>> StandaloneConfigTransformations = InitStandaloneConfigTransformations();
public static IEnumerable<object[]> StandaloneConfigTransformationsScenarios => StandaloneConfigTransformations.ToTheoryData();
[ConditionalTheory]
[MemberData(nameof(StandaloneConfigTransformationsScenarios))]
public async Task StartsWithWebConfigVariationsStandalone(string scenario)
{
var action = StandaloneConfigTransformations[scenario];
var iisDeploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true);
iisDeploymentParameters.ApplicationType = ApplicationType.Standalone;
var expectedArguments = action(iisDeploymentParameters);
var result = await DeployAsync(iisDeploymentParameters);
Assert.Equal(expectedArguments, await result.HttpClient.GetStringAsync("/CommandLineArgs"));
}
public static Dictionary<string, Func<IISDeploymentParameters, string>> InitStandaloneConfigTransformations()
{
var dictionary = new Dictionary<string, Func<IISDeploymentParameters, string>>();
var pathWithSpace = "\u03c0 \u2260 3\u00b714";
dictionary.Add("App in subdirectory",
parameters => {
MoveApplication(parameters, pathWithSpace);
TransformPath(parameters, (path, root) => Path.Combine(pathWithSpace, path));
TransformArguments(parameters, (arguments, root) => "\"additional argument\"");
return "additional argument";
});
dictionary.Add("App in bin subdirectory full path",
parameters => {
MoveApplication(parameters, pathWithSpace);
TransformPath(parameters, (path, root) => Path.Combine(root, pathWithSpace, path));
TransformArguments(parameters, (arguments, root) => "additional arguments");
return "additional|arguments";
});
return dictionary;
}
private static void MoveApplication(
IISDeploymentParameters parameters,
string subdirectory)
{
parameters.WebConfigActionList.Add((config, contentRoot) =>
{
var source = new DirectoryInfo(contentRoot);
var subDirectoryPath = source.CreateSubdirectory(subdirectory);
// Copy everything into a subfolder
Helpers.CopyFiles(source, subDirectoryPath, null);
// Cleanup files
foreach (var fileSystemInfo in source.GetFiles())
{
fileSystemInfo.Delete();
}
});
}
private static void TransformPath(IISDeploymentParameters parameters, Func<string, string, string> transformation)
{
parameters.WebConfigActionList.Add(
(config, contentRoot) =>
{
var aspNetCoreElement = config.Descendants("aspNetCore").Single();
aspNetCoreElement.SetAttributeValue("processPath", transformation((string)aspNetCoreElement.Attribute("processPath"), contentRoot));
});
}
private static void TransformArguments(IISDeploymentParameters parameters, Func<string, string, string> transformation)
{
parameters.WebConfigActionList.Add(
(config, contentRoot) =>
{
var aspNetCoreElement = config.Descendants("aspNetCore").Single();
aspNetCoreElement.SetAttributeValue("arguments", transformation((string)aspNetCoreElement.Attribute("arguments"), contentRoot));
});
}
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -46,7 +47,7 @@ namespace IISTestSite
private async Task HostingEnvironment(HttpContext context) private async Task HostingEnvironment(HttpContext context)
{ {
var hostingEnv = context.RequestServices.GetService<IHostingEnvironment>(); var hostingEnv = context.RequestServices.GetService<IHostingEnvironment>();
await context.Response.WriteAsync("ContentRootPath "+hostingEnv.ContentRootPath + Environment.NewLine); await context.Response.WriteAsync("ContentRootPath "+hostingEnv.ContentRootPath + Environment.NewLine);
await context.Response.WriteAsync("WebRootPath "+hostingEnv.WebRootPath + Environment.NewLine); await context.Response.WriteAsync("WebRootPath "+hostingEnv.WebRootPath + Environment.NewLine);
await context.Response.WriteAsync("CurrentDirectory "+Environment.CurrentDirectory); await context.Response.WriteAsync("CurrentDirectory "+Environment.CurrentDirectory);
@ -762,5 +763,10 @@ namespace IISTestSite
} }
await context.Response.WriteAsync("Response End"); await context.Response.WriteAsync("Response End");
} }
private async Task CommandLineArgs(HttpContext context)
{
await context.Response.WriteAsync(string.Join("|", Environment.GetCommandLineArgs().Skip(1)));
}
} }
} }