From a7b783724efc6c83306951ed8d116e397d2d14ed Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 28 Dec 2018 15:48:20 -0800 Subject: [PATCH] Fix ANCM environment variables bugs (#6083) --- .azure/pipelines/ci.yml | 2 +- .../CommonLib/ConfigurationSection.cpp | 12 + .../CommonLib/ConfigurationSection.h | 3 + .../CommonLib/StringHelpers.cpp | 9 +- .../CommonLib/StringHelpers.h | 9 + .../AspNetCoreModuleV2/CommonLib/exceptions.h | 11 + .../InProcessOptions.cpp | 2 +- .../InProcessOptions.h | 5 +- .../inprocessapplication.cpp | 35 +- .../serverprocess.cpp | 42 +- .../serverprocess.h | 5 +- .../environmentvariablehash.h | 9 +- .../environmentvariablehelpers.h | 409 ++---------------- .../requesthandler_config.cpp | 112 +---- .../RequestHandlerLib/requesthandler_config.h | 5 +- .../Inprocess/EnvironmentVariableTests.cs | 128 +++++- .../Utilities/FunctionalTestsBase.cs | 6 + .../testassets/InProcessWebSite/Startup.cs | 23 - .../testassets/InProcessWebSite/web.config | 6 - .../shared/SharedStartup/Startup.shared.cs | 5 + 20 files changed, 246 insertions(+), 592 deletions(-) diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml index 11b06b530b..cb73aafae5 100644 --- a/.azure/pipelines/ci.yml +++ b/.azure/pipelines/ci.yml @@ -37,7 +37,7 @@ jobs: - template: jobs/iisintegration-job.yml parameters: TestGroupName: IISExpress - skipArgs: "/p:SkipIISBackwardsCompatibilityTests=false /p:SkipIISTests=true /p:SkipIISExpressTests=false /p:SkipIISForwardsCompatibilityTests=true" + skipArgs: "/p:SkipIISBackwardsCompatibilityTests=true /p:SkipIISTests=true /p:SkipIISExpressTests=false /p:SkipIISForwardsCompatibilityTests=true" - template: jobs/iisintegration-job.yml parameters: TestGroupName: IISForwardCompat diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/ConfigurationSection.cpp b/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/ConfigurationSection.cpp index 3d05424490..5850a479b4 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/ConfigurationSection.cpp +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/ConfigurationSection.cpp @@ -5,6 +5,7 @@ #include "StringHelpers.h" #include "ConfigurationLoadException.h" +#include std::wstring ConfigurationSection::GetRequiredString(const std::wstring& name) const { @@ -63,6 +64,17 @@ std::vector> ConfigurationSection::GetKeyV return pairs; } +std::map ConfigurationSection::GetMap(const std::wstring& name) const +{ + std::map pairs; + + for (auto const element : GetRequiredSection(name)->GetCollection()) + { + pairs.insert_or_assign(element->GetRequiredString(CS_ASPNETCORE_COLLECTION_ITEM_NAME), element->GetString(CS_ASPNETCORE_COLLECTION_ITEM_VALUE).value_or(L"")); + } + return pairs; +} + std::shared_ptr ConfigurationSection::GetRequiredSection(const std::wstring& name) const { auto section = GetSection(name); diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h b/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h index 6ca4e475e3..a3a1f52398 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h @@ -6,8 +6,10 @@ #include #include #include +#include #include "NonCopyable.h" +#include "StringHelpers.h" #define CS_ASPNETCORE_COLLECTION_ITEM_NAME L"name" #define CS_ASPNETCORE_COLLECTION_ITEM_VALUE L"value" @@ -46,6 +48,7 @@ public: DWORD GetRequiredTimespan(const std::wstring& name) const; virtual std::vector> GetKeyValuePairs(const std::wstring& name) const; + virtual std::map GetMap(const std::wstring& name) const; virtual std::shared_ptr GetRequiredSection(const std::wstring & name) const; diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/StringHelpers.cpp b/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/StringHelpers.cpp index 736597b9a5..c6ffbbf171 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/StringHelpers.cpp +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/StringHelpers.cpp @@ -17,13 +17,18 @@ bool ends_with(const std::wstring &source, const std::wstring &suffix, bool igno bool equals_ignore_case(const std::wstring& s1, const std::wstring& s2) { - return CSTR_EQUAL == CompareStringOrdinal(s1.c_str(), static_cast(s1.length()), s2.c_str(), static_cast(s2.length()), true); + return compare_ignore_case(s1, s2) == 0; +} + +int compare_ignore_case(const std::wstring& s1, const std::wstring& s2) +{ + return CompareStringOrdinal(s1.c_str(), static_cast(s1.length()), s2.c_str(), static_cast(s2.length()), true) - CSTR_EQUAL; } std::wstring to_wide_string(const std::string &source, const unsigned int codePage) { // MultiByteToWideChar returns 0 on failure, which is also the same return value - // for empty strings. Preemptive return. + // for empty strings. Preemptive return. if (source.length() == 0) { return L""; diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/StringHelpers.h b/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/StringHelpers.h index 3adc8863a4..5fa59fee55 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/StringHelpers.h +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/StringHelpers.h @@ -11,6 +11,9 @@ bool ends_with(const std::wstring &source, const std::wstring &suffix, bool igno [[nodiscard]] bool equals_ignore_case(const std::wstring& s1, const std::wstring& s2); +[[nodiscard]] +int compare_ignore_case(const std::wstring& s1, const std::wstring& s2); + [[nodiscard]] std::wstring to_wide_string(const std::string &source, const unsigned int codePage); @@ -48,3 +51,9 @@ std::string format(const std::string& format, Args ... args) return result; } +struct ignore_case_comparer +{ + bool operator() (const std::wstring & s1, const std::wstring & s2) const { + return compare_ignore_case(s1, s2) == -1; + } +}; diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/exceptions.h b/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/exceptions.h index 18115c0e48..5d2b59b463 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/exceptions.h +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/CommonLib/exceptions.h @@ -134,6 +134,12 @@ private: return condition; } + __declspec(noinline) inline VOID ReportException(LOCATION_ARGUMENTS const InvalidOperationException& exception) +{ + TraceException(LOCATION_CALL exception); + DebugPrintf(ASPNETCORE_DEBUG_FLAG_ERROR, "InvalidOperationException '%ls' caught at " LOCATION_FORMAT, exception.as_wstring().c_str(), LOCATION_CALL_ONLY); +} + __declspec(noinline) inline VOID ReportException(LOCATION_ARGUMENTS const std::exception& exception) { TraceException(LOCATION_CALL exception); @@ -165,6 +171,11 @@ __declspec(noinline) inline HRESULT CaughtExceptionHResult(LOCATION_ARGUMENTS_ON ReportException(LOCATION_CALL exception); return exception.GetResult(); } + catch (const InvalidOperationException& exception) + { + ReportException(LOCATION_CALL exception); + return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); + } catch (const std::exception& exception) { ReportException(LOCATION_CALL exception); diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp b/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp index 48e401b2b8..568939e84a 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp @@ -53,7 +53,7 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc m_fStdoutLogEnabled = aspNetCoreSection->GetRequiredBool(CS_ASPNETCORE_STDOUT_LOG_ENABLED); m_struStdoutLogFile = aspNetCoreSection->GetRequiredString(CS_ASPNETCORE_STDOUT_LOG_FILE); m_fDisableStartUpErrorPage = aspNetCoreSection->GetRequiredBool(CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE); - m_environmentVariables = aspNetCoreSection->GetKeyValuePairs(CS_ASPNETCORE_ENVIRONMENT_VARIABLES); + m_environmentVariables = aspNetCoreSection->GetMap(CS_ASPNETCORE_ENVIRONMENT_VARIABLES); const auto handlerSettings = aspNetCoreSection->GetKeyValuePairs(CS_ASPNETCORE_HANDLER_SETTINGS); m_fSetCurrentDirectory = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY).value_or(L"true"), L"true"); diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h b/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h index 3068936eca..e1df82aedd 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h @@ -7,6 +7,7 @@ #include "BindingInformation.h" #include "ConfigurationSource.h" #include "WebConfigConfigurationSource.h" +#include class InProcessOptions: NonCopyable { @@ -87,7 +88,7 @@ public: return m_dwShutdownTimeLimitInMS; } - const std::vector>& + const std::map& QueryEnvironmentVariables() const { return m_environmentVariables; @@ -120,7 +121,7 @@ private: bool m_fAnonymousAuthEnabled; DWORD m_dwStartupTimeLimitInMS; DWORD m_dwShutdownTimeLimitInMS; - std::vector> m_environmentVariables; + std::map m_environmentVariables; std::vector m_bindingInformation; protected: diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp b/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp index 4563965d73..227795918b 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp @@ -425,37 +425,20 @@ IN_PROCESS_APPLICATION::ClrThreadEntryPoint(const std::shared_ptrQueryEnvironmentVariables(); - - auto inputTable = std::unique_ptr(new ENVIRONMENT_VAR_HASH()); - RETURN_IF_FAILED(inputTable->Initialize(37 /*prime*/)); - // Copy environment variables to old style hash table - for (auto & variable : variables) - { - auto pNewEntry = std::unique_ptr(new ENVIRONMENT_VAR_ENTRY()); - RETURN_IF_FAILED(pNewEntry->Initialize((variable.first + L"=").c_str(), variable.second.c_str())); - RETURN_IF_FAILED(inputTable->InsertRecord(pNewEntry.get())); - } - - ENVIRONMENT_VAR_HASH* pHashTable = NULL; - std::unique_ptr table; - RETURN_IF_FAILED(ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable( - inputTable.get(), + auto variables = ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable( + m_pConfig->QueryEnvironmentVariables(), m_pConfig->QueryWindowsAuthEnabled(), m_pConfig->QueryBasicAuthEnabled(), m_pConfig->QueryAnonymousAuthEnabled(), + false, // fAddHostingStartup QueryApplicationPhysicalPath().c_str(), - nullptr, /* pHttpsPort */ - &pHashTable)); + nullptr); - table.reset(pHashTable); - - HRESULT hr = S_OK; - table->Apply(ENVIRONMENT_VAR_HELPERS::AppendEnvironmentVariables, &hr); - RETURN_IF_FAILED(hr); - - table->Apply(ENVIRONMENT_VAR_HELPERS::SetEnvironmentVariables, &hr); - RETURN_IF_FAILED(hr); + for (const auto & variable : variables) + { + LOG_INFOF(L"Setting environment variable %ls=%ls", variable.first.c_str(), variable.second.c_str()); + SetEnvironmentVariable(variable.first.c_str(), variable.second.c_str()); + } return S_OK; } diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp b/src/Servers/IIS/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp index 5446f97780..69b11cd15d 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp @@ -24,7 +24,7 @@ SERVER_PROCESS::Initialize( BOOL fWindowsAuthEnabled, BOOL fBasicAuthEnabled, BOOL fAnonymousAuthEnabled, - ENVIRONMENT_VAR_HASH *pEnvironmentVariables, + std::map& pEnvironmentVariables, BOOL fStdoutLogEnabled, BOOL fWebSocketSupported, STRU *pstruStdoutLogFile, @@ -761,6 +761,8 @@ SERVER_PROCESS::StartProcess( ENVIRONMENT_VAR_HASH *pHashTable = NULL; PWSTR pStrStage = NULL; BOOL fCriticalError = FALSE; + std::map variables; + GetStartupInfoW(&startupInfo); // @@ -782,27 +784,30 @@ SERVER_PROCESS::StartProcess( goto Failure; } - if (FAILED_LOG(hr = ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable( - m_pEnvironmentVarTable, - m_fWindowsAuthEnabled, - m_fBasicAuthEnabled, - m_fAnonymousAuthEnabled, - m_struAppFullPath.QueryStr(), - m_struHttpsPort.QueryStr(), - &pHashTable))) + try { - pStrStage = L"InitEnvironmentVariablesTable"; - goto Failure; + variables = ENVIRONMENT_VAR_HELPERS::InitEnvironmentVariablesTable( + m_pEnvironmentVarTable, + m_fWindowsAuthEnabled, + m_fBasicAuthEnabled, + m_fAnonymousAuthEnabled, + true, // fAddHostingStartup + m_struAppFullPath.QueryStr(), + m_struHttpsPort.QueryStr()); + + variables = ENVIRONMENT_VAR_HELPERS::AddWebsocketEnabledToEnvironmentVariables(variables, m_fWebSocketSupported); } + CATCH_RETURN(); - if (FAILED_LOG(hr = ENVIRONMENT_VAR_HELPERS::AddWebsocketEnabledToEnvironmentVariables( - pHashTable, - m_fWebSocketSupported - ))) + + pHashTable = new ENVIRONMENT_VAR_HASH(); + RETURN_IF_FAILED(pHashTable->Initialize(37 /*prime*/)); + // Copy environment variables to old style hash table + for (auto & variable : variables) { - pStrStage = L"AddWebsocketEnabledToEnvironmentVariables"; - goto Failure; - + auto pNewEntry = std::unique_ptr(new ENVIRONMENT_VAR_ENTRY()); + RETURN_IF_FAILED(pNewEntry->Initialize((variable.first + L"=").c_str(), variable.second.c_str())); + RETURN_IF_FAILED(pHashTable->InsertRecord(pNewEntry.get())); } // @@ -1793,7 +1798,6 @@ SERVER_PROCESS::~SERVER_PROCESS() CleanUp(); - m_pEnvironmentVarTable = NULL; // no need to free m_pEnvironmentVarTable, as it references to // the same hash table hold by configuration. // the hashtable memory will be freed once onfiguration got recycled diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.h b/src/Servers/IIS/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.h index 028acd6e48..36d6b6ab72 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.h +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.h @@ -4,6 +4,7 @@ #pragma once #include +#include #define MIN_PORT 1025 #define MAX_PORT 48000 @@ -33,7 +34,7 @@ public: _In_ BOOL fWindowsAuthEnabled, _In_ BOOL fBasicAuthEnabled, _In_ BOOL fAnonymousAuthEnabled, - _In_ ENVIRONMENT_VAR_HASH* pEnvironmentVariables, + _In_ std::map& pEnvironmentVariables, _In_ BOOL fStdoutLogEnabled, _In_ BOOL fWebSocketSupported, _In_ STRU *pstruStdoutLogFile, @@ -290,5 +291,5 @@ private: HANDLE m_hChildProcessWaitHandles[MAX_ACTIVE_CHILD_PROCESSES]; PROCESS_MANAGER *m_pProcessManager; - ENVIRONMENT_VAR_HASH *m_pEnvironmentVarTable ; + std::map m_pEnvironmentVarTable; }; diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h b/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h index be1276fd48..dfb72556c4 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h @@ -4,12 +4,11 @@ #pragma once #define HOSTING_STARTUP_ASSEMBLIES_ENV_STR L"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES" -#define HOSTING_STARTUP_ASSEMBLIES_NAME L"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=" #define HOSTING_STARTUP_ASSEMBLIES_VALUE L"Microsoft.AspNetCore.Server.IISIntegration" -#define ASPNETCORE_IIS_AUTH_ENV_STR L"ASPNETCORE_IIS_HTTPAUTH=" -#define ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR L"ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED=" -#define ASPNETCORE_IIS_PHYSICAL_PATH_ENV_STR L"ASPNETCORE_IIS_PHYSICAL_PATH=" -#define ASPNETCORE_HTTPS_PORT_ENV_STR L"ASPNETCORE_HTTPS_PORT=" +#define ASPNETCORE_IIS_AUTH_ENV_STR L"ASPNETCORE_IIS_HTTPAUTH" +#define ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR L"ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED" +#define ASPNETCORE_IIS_PHYSICAL_PATH_ENV_STR L"ASPNETCORE_IIS_PHYSICAL_PATH" +#define ASPNETCORE_HTTPS_PORT_ENV_STR L"ASPNETCORE_HTTPS_PORT" #define ASPNETCORE_IIS_AUTH_WINDOWS L"windows;" #define ASPNETCORE_IIS_AUTH_BASIC L"basic;" #define ASPNETCORE_IIS_AUTH_ANONYMOUS L"anonymous;" diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehelpers.h b/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehelpers.h index f95feca6b9..c595fb3d80 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehelpers.h +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehelpers.h @@ -2,6 +2,8 @@ // Licensed under the MIT License. See License.txt in the project root for license information. #pragma once +#include +#include "Environment.h" class ENVIRONMENT_VAR_HELPERS { @@ -24,422 +26,77 @@ public: } static - VOID - CopyToTable( - ENVIRONMENT_VAR_ENTRY * pEntry, - PVOID pvData - ) - { - // best effort copy, ignore the failure - ENVIRONMENT_VAR_ENTRY * pNewEntry = new ENVIRONMENT_VAR_ENTRY(); - if (pNewEntry != NULL) - { - pNewEntry->Initialize(pEntry->QueryName(), pEntry->QueryValue()); - ENVIRONMENT_VAR_HASH *pHash = static_cast(pvData); - DBG_ASSERT(pHash); - pHash->InsertRecord(pNewEntry); - // Need to dereference as InsertRecord references it now - pNewEntry->Dereference(); - } - } - - static - VOID - AppendEnvironmentVariables - ( - ENVIRONMENT_VAR_ENTRY * pEntry, - PVOID pvData - ) - { - HRESULT hr = S_OK; - DWORD dwResult = 0; - DWORD dwError = 0; - STRU struNameBuffer; - STACK_STRU(struValueBuffer, 300); - BOOL fFound = FALSE; - - HRESULT* pHr = static_cast(pvData); - - // pEntry->QueryName includes the trailing =, remove it before calling stru - if (FAILED(hr = struNameBuffer.Copy(pEntry->QueryName()))) - { - goto Finished; - } - dwResult = struNameBuffer.LastIndexOf(L'='); - if (dwResult != -1) - { - struNameBuffer.QueryStr()[dwResult] = L'\0'; - if (FAILED(hr = struNameBuffer.SyncWithBuffer())) - { - goto Finished; - } - } - - dwResult = GetEnvironmentVariable(struNameBuffer.QueryStr(), struValueBuffer.QueryStr(), struValueBuffer.QuerySizeCCH()); - if (dwResult == 0) - { - dwError = GetLastError(); - // Windows API (e.g., CreateProcess) allows variable with empty string value - // in such case dwResult will be 0 and dwError will also be 0 - // As UI and CMD does not allow empty value, ignore this environment var - if (dwError != ERROR_ENVVAR_NOT_FOUND && dwError != ERROR_SUCCESS) - { - hr = HRESULT_FROM_WIN32(dwError); - goto Finished; - } - } - else if (dwResult > struValueBuffer.QuerySizeCCH()) - { - // have to increase the buffer and try get environment var again - struValueBuffer.Reset(); - struValueBuffer.Resize(dwResult + (DWORD)wcslen(pEntry->QueryValue()) + 2); // for null char and semicolon - dwResult = GetEnvironmentVariable(struNameBuffer.QueryStr(), - struValueBuffer.QueryStr(), - struValueBuffer.QuerySizeCCH()); - - if (dwResult <= 0) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } - fFound = TRUE; - } - else - { - fFound = TRUE; - } - - if (FAILED(hr = struValueBuffer.SyncWithBuffer())) - { - goto Finished; - } - - if (fFound) - { - if (FAILED(hr = struValueBuffer.Append(L";"))) - { - goto Finished; - } - } - if (FAILED(hr = struValueBuffer.Append(pEntry->QueryValue()))) - { - goto Finished; - } - - if (FAILED(hr = pEntry->Initialize(pEntry->QueryName(), struValueBuffer.QueryStr()))) - { - goto Finished; - } - - Finished: - if (FAILED(hr)) - { - *pHr = hr; - } - return; - } - - static - VOID - SetEnvironmentVariables - ( - ENVIRONMENT_VAR_ENTRY * pEntry, - PVOID pvData - ) - { - UNREFERENCED_PARAMETER(pvData); - HRESULT hr = S_OK; - DWORD dwResult = 0; - STRU struNameBuffer; - - HRESULT* pHr = static_cast(pvData); - - // pEntry->QueryName includes the trailing =, remove it before calling SetEnvironmentVariable. - if (FAILED(hr = struNameBuffer.Copy(pEntry->QueryName()))) - { - goto Finished; - } - dwResult = struNameBuffer.LastIndexOf(L'='); - if (dwResult != -1) - { - struNameBuffer.QueryStr()[dwResult] = L'\0'; - if (FAILED(hr = struNameBuffer.SyncWithBuffer())) - { - goto Finished; - } - } - - dwResult = SetEnvironmentVariable(struNameBuffer.QueryStr(), pEntry->QueryValue()); - if (dwResult == 0) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - } - - Finished: - if (FAILED(hr)) - { - *pHr = hr; - } - return; - } - - static - HRESULT + std::map InitEnvironmentVariablesTable ( - _In_ ENVIRONMENT_VAR_HASH* pInEnvironmentVarTable, + _In_ const std::map& pInEnvironmentVarTable, _In_ BOOL fWindowsAuthEnabled, _In_ BOOL fBasicAuthEnabled, _In_ BOOL fAnonymousAuthEnabled, + _In_ BOOL fAddHostingStartup, _In_ PCWSTR pApplicationPhysicalPath, - _In_ PCWSTR pHttpsPort, - _Out_ ENVIRONMENT_VAR_HASH** ppEnvironmentVarTable + _In_ PCWSTR pHttpsPort ) { - HRESULT hr = S_OK; - BOOL fFound = FALSE; - DWORD dwResult, dwError; - STRU strIisAuthEnvValue; - STACK_STRU(strStartupAssemblyEnv, 1024); - ENVIRONMENT_VAR_ENTRY* pHostingEntry = NULL; - ENVIRONMENT_VAR_ENTRY* pIISAuthEntry = NULL; - ENVIRONMENT_VAR_ENTRY* pIISPathEntry = NULL; - ENVIRONMENT_VAR_ENTRY* pIISHttpsPort = NULL; - ENVIRONMENT_VAR_HASH* pEnvironmentVarTable = NULL; + std::map environmentVariables = pInEnvironmentVarTable; - pEnvironmentVarTable = new ENVIRONMENT_VAR_HASH(); - - // - // few environment variables expected, small bucket size for hash table - // - if (FAILED(hr = pEnvironmentVarTable->Initialize(37 /*prime*/))) + environmentVariables.insert_or_assign(ASPNETCORE_IIS_PHYSICAL_PATH_ENV_STR, pApplicationPhysicalPath); + if (pHttpsPort) { - goto Finished; - } - - // copy the envirable hash table (from configuration) to a temp one as we may need to remove elements - pInEnvironmentVarTable->Apply(ENVIRONMENT_VAR_HELPERS::CopyToTable, pEnvironmentVarTable); - if (pEnvironmentVarTable->Count() != pInEnvironmentVarTable->Count()) - { - // hash table copy failed - hr = E_UNEXPECTED; - goto Finished; - } - - pEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_IIS_PHYSICAL_PATH_ENV_STR, &pIISPathEntry); - if (pIISPathEntry != NULL) - { - // user defined ASPNETCORE_IIS_PHYSICAL_PATH in configuration, wipe it off - pIISPathEntry->Dereference(); - pEnvironmentVarTable->DeleteKey((PWSTR)ASPNETCORE_IIS_PHYSICAL_PATH_ENV_STR); - } - - pIISPathEntry = new ENVIRONMENT_VAR_ENTRY(); - - if (FAILED(hr = pIISPathEntry->Initialize(ASPNETCORE_IIS_PHYSICAL_PATH_ENV_STR, pApplicationPhysicalPath)) || - FAILED(hr = pEnvironmentVarTable->InsertRecord(pIISPathEntry))) - { - goto Finished; - } - - if (pHttpsPort != nullptr) - { - pEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_HTTPS_PORT_ENV_STR, &pIISHttpsPort); - if (pIISHttpsPort != NULL) - { - // user defined ASPNETCORE_HTTPS_PORT in configuration, don't override it - pIISHttpsPort->Dereference(); - } - else - { - pIISHttpsPort = new ENVIRONMENT_VAR_ENTRY(); - - if (FAILED(hr = pIISHttpsPort->Initialize(ASPNETCORE_HTTPS_PORT_ENV_STR, pHttpsPort)) || - FAILED(hr = pEnvironmentVarTable->InsertRecord(pIISHttpsPort))) - { - goto Finished; - } - } - } - - pEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_IIS_AUTH_ENV_STR, &pIISAuthEntry); - if (pIISAuthEntry != NULL) - { - // user defined ASPNETCORE_IIS_HTTPAUTH in configuration, wipe it off - pIISAuthEntry->Dereference(); - pEnvironmentVarTable->DeleteKey((PWSTR)ASPNETCORE_IIS_AUTH_ENV_STR); + environmentVariables.try_emplace(ASPNETCORE_HTTPS_PORT_ENV_STR, pHttpsPort); } + std::wstring strIisAuthEnvValue; if (fWindowsAuthEnabled) { - strIisAuthEnvValue.Copy(ASPNETCORE_IIS_AUTH_WINDOWS); + strIisAuthEnvValue.append(ASPNETCORE_IIS_AUTH_WINDOWS); } - if (fBasicAuthEnabled) { - strIisAuthEnvValue.Append(ASPNETCORE_IIS_AUTH_BASIC); + strIisAuthEnvValue.append(ASPNETCORE_IIS_AUTH_BASIC); } - if (fAnonymousAuthEnabled) { - strIisAuthEnvValue.Append(ASPNETCORE_IIS_AUTH_ANONYMOUS); + strIisAuthEnvValue.append(ASPNETCORE_IIS_AUTH_ANONYMOUS); + } + if (strIisAuthEnvValue.empty()) + { + strIisAuthEnvValue.append(ASPNETCORE_IIS_AUTH_NONE); } - if (strIisAuthEnvValue.IsEmpty()) - { - strIisAuthEnvValue.Copy(ASPNETCORE_IIS_AUTH_NONE); - } + environmentVariables.insert_or_assign(ASPNETCORE_IIS_AUTH_ENV_STR, strIisAuthEnvValue); - pIISAuthEntry = new ENVIRONMENT_VAR_ENTRY(); - - if (FAILED(hr = pIISAuthEntry->Initialize(ASPNETCORE_IIS_AUTH_ENV_STR, strIisAuthEnvValue.QueryStr())) || - FAILED(hr = pEnvironmentVarTable->InsertRecord(pIISAuthEntry))) + if (fAddHostingStartup && environmentVariables.count(HOSTING_STARTUP_ASSEMBLIES_ENV_STR) == 0) { - goto Finished; - } + auto hostingStartupValues = Environment::GetEnvironmentVariableValue(HOSTING_STARTUP_ASSEMBLIES_ENV_STR).value_or(L""); - // Compiler is complaining about conversion between PCWSTR and PWSTR here. - // Explictly casting. - pEnvironmentVarTable->FindKey((PWSTR)HOSTING_STARTUP_ASSEMBLIES_NAME, &pHostingEntry); - if (pHostingEntry != NULL) - { - // user defined ASPNETCORE_HOSTINGSTARTUPASSEMBLIES in configuration - // the value will be used in OutputEnvironmentVariables. Do nothing here - pHostingEntry->Dereference(); - pHostingEntry = NULL; - goto Skipped; - } - - //check whether ASPNETCORE_HOSTINGSTARTUPASSEMBLIES is defined in system - dwResult = GetEnvironmentVariable(HOSTING_STARTUP_ASSEMBLIES_ENV_STR, - strStartupAssemblyEnv.QueryStr(), - strStartupAssemblyEnv.QuerySizeCCH()); - if (dwResult == 0) - { - dwError = GetLastError(); - // Windows API (e.g., CreateProcess) allows variable with empty string value - // in such case dwResult will be 0 and dwError will also be 0 - // As UI and CMD does not allow empty value, ignore this environment var - if (dwError != ERROR_ENVVAR_NOT_FOUND && dwError != ERROR_SUCCESS) + if (hostingStartupValues.find(HOSTING_STARTUP_ASSEMBLIES_ENV_STR) == std::wstring::npos) { - hr = HRESULT_FROM_WIN32(dwError); - goto Finished; + hostingStartupValues += std::wstring(L";") + HOSTING_STARTUP_ASSEMBLIES_VALUE; } - } - else if (dwResult > strStartupAssemblyEnv.QuerySizeCCH()) - { - // have to increase the buffer and try get environment var again - strStartupAssemblyEnv.Reset(); - strStartupAssemblyEnv.Resize(dwResult + (DWORD)wcslen(HOSTING_STARTUP_ASSEMBLIES_VALUE) + 1); - dwResult = GetEnvironmentVariable(HOSTING_STARTUP_ASSEMBLIES_ENV_STR, - strStartupAssemblyEnv.QueryStr(), - strStartupAssemblyEnv.QuerySizeCCH()); - if (dwResult <= 0) - { - hr = E_UNEXPECTED; - goto Finished; - } - fFound = TRUE; - } - else - { - fFound = TRUE; + + environmentVariables.insert_or_assign(HOSTING_STARTUP_ASSEMBLIES_ENV_STR, hostingStartupValues); } - strStartupAssemblyEnv.SyncWithBuffer(); - if (strStartupAssemblyEnv.IndexOf(HOSTING_STARTUP_ASSEMBLIES_VALUE) == -1) + for (auto& environmentVariable : environmentVariables) { - if (fFound) - { - strStartupAssemblyEnv.Append(L";"); - } - strStartupAssemblyEnv.Append(HOSTING_STARTUP_ASSEMBLIES_VALUE); + environmentVariable.second = Environment::ExpandEnvironmentVariables(environmentVariable.second); } - // the environment variable was not defined, create it and add to hashtable - pHostingEntry = new ENVIRONMENT_VAR_ENTRY(); - - if (FAILED(hr = pHostingEntry->Initialize(HOSTING_STARTUP_ASSEMBLIES_NAME, strStartupAssemblyEnv.QueryStr())) || - FAILED(hr = pEnvironmentVarTable->InsertRecord(pHostingEntry))) - { - goto Finished; - } - - Skipped: - *ppEnvironmentVarTable = pEnvironmentVarTable; - pEnvironmentVarTable = NULL; - - Finished: - if (pHostingEntry != NULL) - { - pHostingEntry->Dereference(); - pHostingEntry = NULL; - } - - if (pIISAuthEntry != NULL) - { - pIISAuthEntry->Dereference(); - pIISAuthEntry = NULL; - } - - if (pEnvironmentVarTable != NULL) - { - pEnvironmentVarTable->Clear(); - delete pEnvironmentVarTable; - pEnvironmentVarTable = NULL; - } - return hr; + return environmentVariables; } static - HRESULT + std::map AddWebsocketEnabledToEnvironmentVariables ( - _Inout_ ENVIRONMENT_VAR_HASH* pInEnvironmentVarTable, + _Inout_ const std::map& pInEnvironmentVarTable, _In_ BOOL fWebsocketsEnabled ) { - HRESULT hr = S_OK; - ENVIRONMENT_VAR_ENTRY* pIISWebsocketEntry = NULL; - STACK_STRU(strIISWebsocketEnvValue, 40); - - // We only need to set the WEBSOCKET_SUPPORTED environment variable for out of process - pInEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR, &pIISWebsocketEntry); - if (pIISWebsocketEntry != NULL) - { - // user defined ASPNETCORE_IIS_WEBSOCKETS in configuration, wipe it off - pIISWebsocketEntry->Dereference(); - pInEnvironmentVarTable->DeleteKey((PWSTR)ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR); - } - // Set either true or false for the WebsocketEnvValue. - if (fWebsocketsEnabled) - { - if (FAILED(hr = strIISWebsocketEnvValue.Copy(L"true"))) - { - goto Finished; - } - } - else - { - if (FAILED(hr = strIISWebsocketEnvValue.Copy(L"false"))) - { - goto Finished; - } - } - - pIISWebsocketEntry = new ENVIRONMENT_VAR_ENTRY(); - - if (FAILED(hr = pIISWebsocketEntry->Initialize(ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR, strIISWebsocketEnvValue.QueryStr())) || - FAILED(hr = pInEnvironmentVarTable->InsertRecord(pIISWebsocketEntry))) - { - goto Finished; - } - - Finished: - return hr; + std::map environmentVariables = pInEnvironmentVarTable; + environmentVariables.insert_or_assign(ASPNETCORE_IIS_WEBSOCKETS_SUPPORTED_ENV_STR, fWebsocketsEnabled ? L"true" : L"false"); + return environmentVariables; } -public: - ENVIRONMENT_VAR_HELPERS(); - ~ENVIRONMENT_VAR_HELPERS(); }; diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp b/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp index 68abb1b8f1..f4507164df 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.cpp @@ -15,13 +15,6 @@ REQUESTHANDLER_CONFIG::~REQUESTHANDLER_CONFIG() delete[] m_ppStrArguments; m_ppStrArguments = NULL; } - - if (m_pEnvironmentVariables != NULL) - { - m_pEnvironmentVariables->Clear(); - delete m_pEnvironmentVariables; - m_pEnvironmentVariables = NULL; - } } HRESULT @@ -100,12 +93,7 @@ REQUESTHANDLER_CONFIG::Populate( IAppHostElement *pWindowsAuthenticationElement = NULL; IAppHostElement *pBasicAuthenticationElement = NULL; IAppHostElement *pAnonymousAuthenticationElement = NULL; - IAppHostElement *pEnvVarList = NULL; - IAppHostElement *pEnvVar = NULL; - IAppHostElementCollection *pEnvVarCollection = NULL; ULONGLONG ullRawTimeSpan = 0; - ENUM_INDEX index; - ENVIRONMENT_VAR_ENTRY* pEntry = NULL; DWORD dwCounter = 0; DWORD dwPosition = 0; WCHAR* pszPath = NULL; @@ -114,26 +102,20 @@ REQUESTHANDLER_CONFIG::Populate( BSTR bstrAnonymousAuthSection = NULL; BSTR bstrAspNetCoreSection = NULL; - m_pEnvironmentVariables = new ENVIRONMENT_VAR_HASH(); - if (FAILED(hr = m_pEnvironmentVariables->Initialize(37 /*prime*/))) - { - delete m_pEnvironmentVariables; - m_pEnvironmentVariables = NULL; - goto Finished; - } - pAdminManager = pHttpServer->GetAdminManager(); - if (pSite != nullptr) + try { - try + WebConfigConfigurationSource source(pAdminManager, *pHttpApplication); + if (pSite != nullptr) { - WebConfigConfigurationSource source(pAdminManager, *pHttpApplication); m_struHttpsPort.Copy(BindingInformation::GetHttpsPort(BindingInformation::Load(source, *pSite)).c_str()); } - catch (...) - { - FINISHED_IF_FAILED(OBSERVE_CAUGHT_EXCEPTION()); - } + + m_pEnvironmentVariables = source.GetSection(CS_ASPNETCORE_SECTION)->GetMap(CS_ASPNETCORE_ENVIRONMENT_VARIABLES); + } + catch (...) + { + FINISHED_IF_FAILED(OBSERVE_CAUGHT_EXCEPTION()); } hr = m_struConfigPath.Copy(pHttpApplication->GetAppConfigPath()); @@ -397,58 +379,6 @@ REQUESTHANDLER_CONFIG::Populate( goto Finished; } - hr = GetElementChildByName(pAspNetCoreElement, - CS_ASPNETCORE_ENVIRONMENT_VARIABLES, - &pEnvVarList); - if (FAILED(hr)) - { - goto Finished; - } - - hr = pEnvVarList->get_Collection(&pEnvVarCollection); - if (FAILED(hr)) - { - goto Finished; - } - - for (hr = FindFirstElement(pEnvVarCollection, &index, &pEnvVar); - SUCCEEDED(hr); - hr = FindNextElement(pEnvVarCollection, &index, &pEnvVar)) - { - if (hr == S_FALSE) - { - hr = S_OK; - break; - } - - if (FAILED(hr = GetElementStringProperty(pEnvVar, - CS_ASPNETCORE_ENVIRONMENT_VARIABLE_NAME, - &strEnvName)) || - FAILED(hr = GetElementStringProperty(pEnvVar, - CS_ASPNETCORE_ENVIRONMENT_VARIABLE_VALUE, - &strEnvValue)) || - FAILED(hr = strEnvName.Append(L"=")) || - FAILED(hr = STRU::ExpandEnvironmentVariables(strEnvValue.QueryStr(), &strExpandedEnvValue))) - { - goto Finished; - } - - pEntry = new ENVIRONMENT_VAR_ENTRY(); - - if (FAILED(hr = pEntry->Initialize(strEnvName.QueryStr(), strExpandedEnvValue.QueryStr())) || - FAILED(hr = m_pEnvironmentVariables->InsertRecord(pEntry))) - { - goto Finished; - } - strEnvName.Reset(); - strEnvValue.Reset(); - strExpandedEnvValue.Reset(); - pEnvVar->Release(); - pEnvVar = NULL; - pEntry->Dereference(); - pEntry = NULL; - } - Finished: if (pAspNetCoreElement != NULL) @@ -475,29 +405,5 @@ Finished: pBasicAuthenticationElement = NULL; } - if (pEnvVarList != NULL) - { - pEnvVarList->Release(); - pEnvVarList = NULL; - } - - if (pEnvVar != NULL) - { - pEnvVar->Release(); - pEnvVar = NULL; - } - - if (pEnvVarCollection != NULL) - { - pEnvVarCollection->Release(); - pEnvVarCollection = NULL; - } - - if (pEntry != NULL) - { - pEntry->Dereference(); - pEntry = NULL; - } - return hr; } diff --git a/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.h b/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.h index c757ab16c2..90a2b5a365 100644 --- a/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.h +++ b/src/Servers/IIS/src/AspNetCoreModuleV2/RequestHandlerLib/requesthandler_config.h @@ -68,7 +68,7 @@ public: _Out_ REQUESTHANDLER_CONFIG **ppAspNetCoreConfig ); - ENVIRONMENT_VAR_HASH* + std::map& QueryEnvironmentVariables( VOID ) @@ -225,7 +225,6 @@ protected: // REQUESTHANDLER_CONFIG() : m_fStdoutLogEnabled(FALSE), - m_pEnvironmentVariables(NULL), m_hostingModel(HOSTING_UNKNOWN), m_ppStrArguments(NULL) { @@ -257,7 +256,7 @@ protected: BOOL m_fBasicAuthEnabled; BOOL m_fAnonymousAuthEnabled; APP_HOSTING_MODEL m_hostingModel; - ENVIRONMENT_VAR_HASH* m_pEnvironmentVariables; + std::map m_pEnvironmentVariables; STRU m_struHostFxrLocation; PWSTR* m_ppStrArguments; DWORD m_dwArgc; diff --git a/src/Servers/IIS/test/Common.FunctionalTests/Inprocess/EnvironmentVariableTests.cs b/src/Servers/IIS/test/Common.FunctionalTests/Inprocess/EnvironmentVariableTests.cs index ae8fde39ed..d044e49c91 100644 --- a/src/Servers/IIS/test/Common.FunctionalTests/Inprocess/EnvironmentVariableTests.cs +++ b/src/Servers/IIS/test/Common.FunctionalTests/Inprocess/EnvironmentVariableTests.cs @@ -2,50 +2,132 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; +using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; +using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; using Xunit; namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests { - [Collection(IISTestSiteCollection.Name)] - public class EnvironmentVariableTests: FixtureLoggedTest - { - private readonly IISTestSiteFixture _fixture; + [Collection(PublishedSitesCollection.Name)] - public EnvironmentVariableTests(IISTestSiteFixture fixture): base(fixture) + public class EnvironmentVariableTests: IISFunctionalTestBase + { + private readonly PublishedSitesFixture _fixture; + + public EnvironmentVariableTests(PublishedSitesFixture fixture) { _fixture = fixture; } - [ConditionalFact] - public async Task GetUniqueEnvironmentVariable() + [ConditionalTheory] + [InlineData(HostingModel.InProcess)] + [InlineData(HostingModel.OutOfProcess)] + public async Task GetLongEnvironmentVariable(HostingModel hostingModel) { - Assert.Equal("foobar", await _fixture.Client.GetStringAsync("/CheckEnvironmentVariable")); - } + var expectedValue = "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" + + "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" + + "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" + + "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" + + "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" + + "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative"; + + + var deploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel, publish: true); + deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_INPROCESS_TESTING_LONG_VALUE"] = expectedValue; - [ConditionalFact] - public async Task GetLongEnvironmentVariable() - { Assert.Equal( - "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" + - "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" + - "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" + - "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" + - "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative" + - "AReallyLongValueThatIsGreaterThan300CharactersToForceResizeInNative", - await _fixture.Client.GetStringAsync("/CheckEnvironmentLongValueVariable")); + expectedValue, + await GetStringAsync(deploymentParameters, "/GetEnvironmentVariable?name=ASPNETCORE_INPROCESS_TESTING_LONG_VALUE")); } [ConditionalFact] - public async Task GetExistingEnvironmentVariable() + [RequiresNewHandler] + public Task AuthHeaderEnvironmentVariableRemoved_InProcess() => AuthHeaderEnvironmentVariableRemoved(HostingModel.InProcess); + + [ConditionalFact] + public Task AuthHeaderEnvironmentVariableRemoved_OutOfProcess() => AuthHeaderEnvironmentVariableRemoved(HostingModel.OutOfProcess); + + private async Task AuthHeaderEnvironmentVariableRemoved(HostingModel hostingModel) { - Assert.Contains(";foobarbaz", await _fixture.Client.GetStringAsync("/CheckAppendedEnvironmentVariable")); + var deploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel, publish: true); + deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_IIS_HTTPAUTH"] = "shouldberemoved"; + + Assert.DoesNotContain("shouldberemoved", await GetStringAsync(deploymentParameters,"/GetEnvironmentVariable?name=ASPNETCORE_IIS_HTTPAUTH")); } [ConditionalFact] - public async Task AuthHeaderEnvironmentVariableRemoved() + [RequiresNewHandler] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + public Task WebConfigOverridesGlobalEnvironmentVariables_InProcess() => WebConfigOverridesGlobalEnvironmentVariables(HostingModel.InProcess); + + [ConditionalFact] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + public Task WebConfigOverridesGlobalEnvironmentVariables_OutOfProcess() => WebConfigOverridesGlobalEnvironmentVariables(HostingModel.OutOfProcess); + + private async Task WebConfigOverridesGlobalEnvironmentVariables(HostingModel hostingModel) { - Assert.DoesNotContain("shouldberemoved", await _fixture.Client.GetStringAsync("/CheckRemoveAuthEnvironmentVariable")); + var deploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel, publish: true); + deploymentParameters.EnvironmentVariables["ASPNETCORE_ENVIRONMENT"] = "Development"; + deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_ENVIRONMENT"] = "Production"; + Assert.Equal("Production", await GetStringAsync(deploymentParameters, "/GetEnvironmentVariable?name=ASPNETCORE_ENVIRONMENT")); + } + + [ConditionalFact] + [RequiresNewHandler] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + public Task WebConfigAppendsHostingStartup_InProcess() => WebConfigAppendsHostingStartup(HostingModel.InProcess); + + [ConditionalFact] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + public Task WebConfigAppendsHostingStartup_OutOfProcess() => WebConfigAppendsHostingStartup(HostingModel.OutOfProcess); + + private async Task WebConfigAppendsHostingStartup(HostingModel hostingModel) + { + var deploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel, publish: true); + deploymentParameters.EnvironmentVariables["ASPNETCORE_HOSTINGSTARTUPASSEMBLIES"] = "Asm1"; + if (hostingModel == HostingModel.InProcess) + { + Assert.Equal("Asm1", await GetStringAsync(deploymentParameters, "/GetEnvironmentVariable?name=ASPNETCORE_HOSTINGSTARTUPASSEMBLIES")); + } + else + { + Assert.Equal("Asm1;Microsoft.AspNetCore.Server.IISIntegration", await GetStringAsync(deploymentParameters, "/GetEnvironmentVariable?name=ASPNETCORE_HOSTINGSTARTUPASSEMBLIES")); + } + } + + [ConditionalFact] + [RequiresNewHandler] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + public Task WebConfigOverridesHostingStartup_InProcess() => WebConfigOverridesHostingStartup(HostingModel.InProcess); + + [ConditionalFact] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + public Task WebConfigOverridesHostingStartup_OutOfProcess() => WebConfigOverridesHostingStartup(HostingModel.OutOfProcess); + + private async Task WebConfigOverridesHostingStartup(HostingModel hostingModel) + { + var deploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel, publish: true); + deploymentParameters.EnvironmentVariables["ASPNETCORE_HOSTINGSTARTUPASSEMBLIES"] = "Asm1"; + deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_HOSTINGSTARTUPASSEMBLIES"] = "Asm2"; + Assert.Equal("Asm2", await GetStringAsync(deploymentParameters, "/GetEnvironmentVariable?name=ASPNETCORE_HOSTINGSTARTUPASSEMBLIES")); + } + + [ConditionalFact] + [RequiresNewHandler] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + public Task WebConfigExpandsVariables_InProcess() => WebConfigExpandsVariables(HostingModel.InProcess); + + [ConditionalFact] + [RequiresIIS(IISCapability.PoolEnvironmentVariables)] + public Task WebConfigExpandsVariables_OutOfProcess() => WebConfigExpandsVariables(HostingModel.OutOfProcess); + + private async Task WebConfigExpandsVariables(HostingModel hostingModel) + { + var deploymentParameters = _fixture.GetBaseDeploymentParameters(hostingModel, publish: true); + deploymentParameters.EnvironmentVariables["TestVariable"] = "World"; + deploymentParameters.WebConfigBasedEnvironmentVariables["OtherVariable"] = "%TestVariable%;Hello"; + Assert.Equal("World;Hello", await GetStringAsync(deploymentParameters, "/GetEnvironmentVariable?name=OtherVariable")); } } } diff --git a/src/Servers/IIS/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs b/src/Servers/IIS/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs index 1a1a4ce490..f57aa0ebec 100644 --- a/src/Servers/IIS/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs +++ b/src/Servers/IIS/test/Common.FunctionalTests/Utilities/FunctionalTestsBase.cs @@ -45,6 +45,12 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting return result; } + protected virtual async Task GetStringAsync(IISDeploymentParameters parameters, string path) + { + var result = await DeployAsync(parameters); + return await result.HttpClient.GetStringAsync(path); + } + public override void Dispose() { StopServer(false); diff --git a/src/Servers/IIS/test/testassets/InProcessWebSite/Startup.cs b/src/Servers/IIS/test/testassets/InProcessWebSite/Startup.cs index eb84272b76..b156efa966 100644 --- a/src/Servers/IIS/test/testassets/InProcessWebSite/Startup.cs +++ b/src/Servers/IIS/test/testassets/InProcessWebSite/Startup.cs @@ -266,29 +266,6 @@ namespace TestSite } } - private async Task CheckEnvironmentVariable(HttpContext ctx) - { - var variable = Environment.GetEnvironmentVariable("ASPNETCORE_INPROCESS_TESTING_VALUE"); - await ctx.Response.WriteAsync(variable); - } - - private async Task CheckEnvironmentLongValueVariable(HttpContext ctx) - { - var variable = Environment.GetEnvironmentVariable("ASPNETCORE_INPROCESS_TESTING_LONG_VALUE"); - await ctx.Response.WriteAsync(variable); - } - - private async Task CheckAppendedEnvironmentVariable(HttpContext ctx) - { - var variable = Environment.GetEnvironmentVariable("ProgramFiles"); - await ctx.Response.WriteAsync(variable); - } - - private async Task CheckRemoveAuthEnvironmentVariable(HttpContext ctx) - { - var variable = Environment.GetEnvironmentVariable("ASPNETCORE_IIS_HTTPAUTH"); - await ctx.Response.WriteAsync(variable); - } private async Task ReadAndWriteSynchronously(HttpContext ctx) { var t2 = Task.Run(() => WriteManyTimesToResponseBody(ctx)); diff --git a/src/Servers/IIS/test/testassets/InProcessWebSite/web.config b/src/Servers/IIS/test/testassets/InProcessWebSite/web.config index 2a9bd223c3..352ab4021b 100644 --- a/src/Servers/IIS/test/testassets/InProcessWebSite/web.config +++ b/src/Servers/IIS/test/testassets/InProcessWebSite/web.config @@ -5,12 +5,6 @@ - - - - - - diff --git a/src/Servers/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs b/src/Servers/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs index f85f9e1fdd..513eb89cef 100644 --- a/src/Servers/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs +++ b/src/Servers/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs @@ -134,5 +134,10 @@ namespace TestSite GetDllDirectory(1024, builder); await context.Response.WriteAsync(builder.ToString()); } + + private async Task GetEnvironmentVariable(HttpContext ctx) + { + await ctx.Response.WriteAsync(Environment.GetEnvironmentVariable(ctx.Request.Query["name"].ToString())); + } } }