From 5fd9435392ca23c70c9bc047bba28f5c70e1377a Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 15 Jan 2019 09:00:50 -0800 Subject: [PATCH 1/4] Port ANCM current directory changes to 2.2 (#6150) --- .../CommonLib/ConfigurationSection.h | 1 + .../CommonLib/Environment.cpp | 60 +++++++++++++++++++ .../CommonLib/Environment.h | 4 ++ .../InProcessOptions.cpp | 4 ++ .../InProcessOptions.h | 9 ++- .../inprocessapplication.cpp | 22 +++++++ .../serverprocess.cpp | 1 + .../environmentvariablehash.h | 1 + .../environmentvariablehelpers.h | 22 ++++++- .../Inprocess/ErrorPagesTests.cs | 2 +- .../Inprocess/HostingEnvironmentTests.cs | 12 ++-- .../Inprocess/StartupTests.cs | 16 +++++ .../OutOfProcess/HelloWorldTest.cs | 15 +++-- .../RequiresNewHandler.cs | 2 +- .../Common.FunctionalTests/RequiresNewShim.cs | 2 +- .../DeployerSelector.cs | 3 +- .../DeployerSelector.cs | 3 +- .../IIS.FunctionalTests/DeployerSelector.cs | 3 +- .../DeployerSelector.cs | 3 +- .../shared/SharedStartup/Startup.shared.cs | 27 ++++++--- 20 files changed, 183 insertions(+), 29 deletions(-) diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h index f998478db2..9b6fd2cb49 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h @@ -23,6 +23,7 @@ #define CS_ASPNETCORE_HOSTING_MODEL_INPROCESS L"inprocess" #define CS_ASPNETCORE_HOSTING_MODEL L"hostingModel" #define CS_ASPNETCORE_HANDLER_SETTINGS L"handlerSettings" +#define CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY L"setCurrentDirectory" #define CS_ASPNETCORE_DISABLE_START_UP_ERROR_PAGE L"disableStartUpErrorPage" #define CS_ENABLED L"enabled" diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp index 5e71216ce5..7e5ff9ca47 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.cpp @@ -60,3 +60,63 @@ Environment::GetEnvironmentVariableValue(const std::wstring & str) return expandedStr; } + +std::wstring Environment::GetCurrentDirectoryValue() +{ + DWORD requestedSize = GetCurrentDirectory(0, nullptr); + if (requestedSize == 0) + { + throw std::system_error(GetLastError(), std::system_category(), "GetCurrentDirectory"); + } + + std::wstring expandedStr; + do + { + expandedStr.resize(requestedSize); + requestedSize = GetCurrentDirectory(requestedSize, expandedStr.data()); + if (requestedSize == 0) + { + throw std::system_error(GetLastError(), std::system_category(), "GetCurrentDirectory"); + } + } while (expandedStr.size() != requestedSize + 1); + + expandedStr.resize(requestedSize); + + return expandedStr; +} + +std::wstring Environment::GetDllDirectoryValue() +{ + // GetDllDirectory can return 0 in both the success case and the failure case, and it only sets last error when it fails. + // This requires you to set the last error to ERROR_SUCCESS before calling it in order to detect failure. + SetLastError(ERROR_SUCCESS); + + DWORD requestedSize = GetDllDirectory(0, nullptr); + if (requestedSize == 0) + { + if (GetLastError() != ERROR_SUCCESS) + { + throw std::system_error(GetLastError(), std::system_category(), "GetDllDirectory"); + } + else + { + return L""; + } + } + + std::wstring expandedStr; + do + { + expandedStr.resize(requestedSize); + requestedSize = GetDllDirectory(requestedSize, expandedStr.data()); + // 0 might be returned if GetDllDirectory is empty + if (requestedSize == 0 && GetLastError() != ERROR_SUCCESS) + { + throw std::system_error(GetLastError(), std::system_category(), "GetDllDirectory"); + } + } while (expandedStr.size() != requestedSize + 1); + + expandedStr.resize(requestedSize); + + return expandedStr; +} diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.h index 16022e18cf..8886ad428e 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/Environment.h @@ -16,5 +16,9 @@ public: std::wstring ExpandEnvironmentVariables(const std::wstring & str); static std::optional GetEnvironmentVariableValue(const std::wstring & str); + static + std::wstring GetCurrentDirectoryValue(); + static + std::wstring GetDllDirectoryValue(); }; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp index 1b3e22e1dc..a179884242 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp @@ -53,6 +53,10 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc 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); + + 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"); + m_dwStartupTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_STARTUP_TIME_LIMIT) * 1000; m_dwShutdownTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_SHUTDOWN_TIME_LIMIT) * 1000; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h index e299c2033e..dbba6408f4 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h @@ -40,6 +40,12 @@ public: return m_fDisableStartUpErrorPage; } + bool + QuerySetCurrentDirectory() const + { + return m_fSetCurrentDirectory; + } + bool QueryWindowsAuthEnabled() const { @@ -87,7 +93,7 @@ public: } InProcessOptions(const ConfigurationSource &configurationSource); - + static HRESULT InProcessOptions::Create( IHttpServer& pServer, @@ -100,6 +106,7 @@ private: std::wstring m_struStdoutLogFile; bool m_fStdoutLogEnabled; bool m_fDisableStartUpErrorPage; + bool m_fSetCurrentDirectory; bool m_fWindowsAuthEnabled; bool m_fBasicAuthEnabled; bool m_fAnonymousAuthEnabled; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp index c351dbc4c1..09f4dc3e5e 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/inprocessapplication.cpp @@ -11,6 +11,7 @@ #include "resources.h" #include "EventLog.h" #include "ModuleHelpers.h" +#include "Environment.h" IN_PROCESS_APPLICATION* IN_PROCESS_APPLICATION::s_Application = NULL; @@ -218,6 +219,25 @@ IN_PROCESS_APPLICATION::ExecuteApplication() // set the callbacks s_Application = this; + if (m_pConfig->QuerySetCurrentDirectory()) + { + auto dllDirectory = Environment::GetDllDirectoryValue(); + auto currentDirectory = Environment::GetCurrentDirectoryValue(); + + LOG_INFOF(L"Initial Dll directory: '%s', current directory: '%s'", dllDirectory.c_str(), currentDirectory.c_str()); + + // If DllDirectory wasn't set change it to previous current directory value + if (dllDirectory.empty()) + { + LOG_LAST_ERROR_IF(!SetDllDirectory(currentDirectory.c_str())); + LOG_INFOF(L"Setting dll directory to %s", currentDirectory.c_str()); + } + + LOG_LAST_ERROR_IF(!SetCurrentDirectory(this->QueryApplicationPhysicalPath().c_str())); + + LOG_INFOF(L"Setting current directory to %s", this->QueryApplicationPhysicalPath().c_str()); + } + //Start CLR thread m_clrThread = std::thread(ClrThreadEntryPoint, context); @@ -405,6 +425,7 @@ HRESULT IN_PROCESS_APPLICATION::SetEnvironmentVariablesOnWorkerProcess() { auto variables = m_pConfig->QueryEnvironmentVariables(); + auto inputTable = std::unique_ptr(new ENVIRONMENT_VAR_HASH()); RETURN_IF_FAILED(inputTable->Initialize(37 /*prime*/)); // Copy environment variables to old style hash table @@ -422,6 +443,7 @@ IN_PROCESS_APPLICATION::SetEnvironmentVariablesOnWorkerProcess() m_pConfig->QueryWindowsAuthEnabled(), m_pConfig->QueryBasicAuthEnabled(), m_pConfig->QueryAnonymousAuthEnabled(), + QueryApplicationPhysicalPath().c_str(), &pHashTable)); table.reset(pHashTable); diff --git a/src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp b/src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp index 494911472a..9b549e7bf6 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/OutOfProcessRequestHandler/serverprocess.cpp @@ -785,6 +785,7 @@ SERVER_PROCESS::StartProcess( m_fWindowsAuthEnabled, m_fBasicAuthEnabled, m_fAnonymousAuthEnabled, + m_struAppFullPath.QueryStr(), &pHashTable))) { pStrStage = L"InitEnvironmentVariablesTable"; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h b/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h index c5f63f6fde..eb7c9ad544 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehash.h @@ -8,6 +8,7 @@ #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_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/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehelpers.h b/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehelpers.h index b65c32ee29..17ab7603b4 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehelpers.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/RequestHandlerLib/environmentvariablehelpers.h @@ -191,6 +191,7 @@ public: _In_ BOOL fWindowsAuthEnabled, _In_ BOOL fBasicAuthEnabled, _In_ BOOL fAnonymousAuthEnabled, + _In_ PCWSTR pApplicationPhysicalPath, _Out_ ENVIRONMENT_VAR_HASH** ppEnvironmentVarTable ) { @@ -201,6 +202,7 @@ public: STACK_STRU(strStartupAssemblyEnv, 1024); ENVIRONMENT_VAR_ENTRY* pHostingEntry = NULL; ENVIRONMENT_VAR_ENTRY* pIISAuthEntry = NULL; + ENVIRONMENT_VAR_ENTRY* pIISPathEntry = NULL; ENVIRONMENT_VAR_HASH* pEnvironmentVarTable = NULL; pEnvironmentVarTable = new ENVIRONMENT_VAR_HASH(); @@ -213,7 +215,7 @@ public: goto Finished; } - // copy the envirable hash table (from configuration) to a temp one as we may need to remove elements + // 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()) { @@ -222,6 +224,22 @@ public: 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; + } + pEnvironmentVarTable->FindKey((PWSTR)ASPNETCORE_IIS_AUTH_ENV_STR, &pIISAuthEntry); if (pIISAuthEntry != NULL) { @@ -259,7 +277,7 @@ public: } // Compiler is complaining about conversion between PCWSTR and PWSTR here. - // Explictly casting. + // Explictly casting. pEnvironmentVarTable->FindKey((PWSTR)HOSTING_STARTUP_ASSEMBLIES_NAME, &pHostingEntry); if (pHostingEntry != NULL) { diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ErrorPagesTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ErrorPagesTests.cs index bf45949491..f53571001e 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ErrorPagesTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/ErrorPagesTests.cs @@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests private static void VerifyNoExtraTrailingBytes(string responseString) { - if (!DeployerSelector.IsBackwardsCompatiblityTest) + if (DeployerSelector.HasNewShim) { Assert.EndsWith("\r\n", responseString); } diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HostingEnvironmentTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HostingEnvironmentTests.cs index 061b828a6c..3c2df6cb84 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HostingEnvironmentTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/HostingEnvironmentTests.cs @@ -20,14 +20,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests } [ConditionalFact] - [RequiresIIS(IISCapability.ShutdownToken)] + [RequiresNewHandler] public async Task HostingEnvironmentIsCorrect() { - Assert.Equal( - $"ContentRootPath {_fixture.DeploymentResult.ContentRoot}" + Environment.NewLine + - $"WebRootPath {_fixture.DeploymentResult.ContentRoot}\\wwwroot" + Environment.NewLine + - $"CurrentDirectory {Path.GetDirectoryName(_fixture.DeploymentResult.HostProcess.MainModule.FileName)}", - await _fixture.Client.GetStringAsync("/HostingEnvironment")); + Assert.Equal(_fixture.DeploymentResult.ContentRoot, await _fixture.Client.GetStringAsync("/ContentRootPath")); + Assert.Equal(_fixture.DeploymentResult.ContentRoot + "\\wwwroot", await _fixture.Client.GetStringAsync("/WebRootPath")); + Assert.Equal(_fixture.DeploymentResult.ContentRoot, await _fixture.DeploymentResult.HttpClient.GetStringAsync("/CurrentDirectory")); + Assert.Equal(_fixture.DeploymentResult.ContentRoot + "\\", await _fixture.Client.GetStringAsync("/BaseDirectory")); + Assert.Equal(_fixture.DeploymentResult.ContentRoot + "\\", await _fixture.Client.GetStringAsync("/ASPNETCORE_IIS_PHYSICAL_PATH")); } } } diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs index 3bcc134cb2..b6e497cc45 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/StartupTests.cs @@ -449,6 +449,22 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests return dictionary; } + [ConditionalFact] + [RequiresNewHandler] + public async Task SetCurrentDirectoryHandlerSettingWorks() + { + var deploymentParameters = _fixture.GetBaseDeploymentParameters(publish: true); + deploymentParameters.HandlerSettings["SetCurrentDirectory"] = "false"; + + var deploymentResult = await DeployAsync(deploymentParameters); + + Assert.Equal(deploymentResult.ContentRoot, await deploymentResult.HttpClient.GetStringAsync("/ContentRootPath")); + Assert.Equal(deploymentResult.ContentRoot + "\\wwwroot", await deploymentResult.HttpClient.GetStringAsync("/WebRootPath")); + Assert.Equal(Path.GetDirectoryName(deploymentResult.HostProcess.MainModule.FileName), await deploymentResult.HttpClient.GetStringAsync("/CurrentDirectory")); + Assert.Equal(deploymentResult.ContentRoot + "\\", await deploymentResult.HttpClient.GetStringAsync("/BaseDirectory")); + Assert.Equal(deploymentResult.ContentRoot + "\\", await deploymentResult.HttpClient.GetStringAsync("/ASPNETCORE_IIS_PHYSICAL_PATH")); + } + private static void MoveApplication( IISDeploymentParameters parameters, string subdirectory) diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs index f6e36613d7..15f0a4e0d4 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/OutOfProcess/HelloWorldTest.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities; @@ -68,14 +69,16 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests Assert.Equal("null", responseText); - Assert.Equal( - $"ContentRootPath {deploymentResult.ContentRoot}" + Environment.NewLine + - $"WebRootPath {deploymentResult.ContentRoot}\\wwwroot" + Environment.NewLine + - $"CurrentDirectory {deploymentResult.ContentRoot}", - await deploymentResult.HttpClient.GetStringAsync("/HostingEnvironment")); - + Assert.Equal(deploymentResult.ContentRoot, await deploymentResult.HttpClient.GetStringAsync("/ContentRootPath")); + Assert.Equal(deploymentResult.ContentRoot + "\\wwwroot", await deploymentResult.HttpClient.GetStringAsync("/WebRootPath")); var expectedDll = variant.AncmVersion == AncmVersion.AspNetCoreModule ? "aspnetcore.dll" : "aspnetcorev2.dll"; Assert.Contains(deploymentResult.HostProcess.Modules.OfType(), m=> m.FileName.Contains(expectedDll)); + + if (DeployerSelector.HasNewHandler && variant.HostingModel == HostingModel.InProcess) + { + Assert.Equal(deploymentResult.ContentRoot, await deploymentResult.HttpClient.GetStringAsync("/CurrentDirectory")); + Assert.Equal(Path.GetDirectoryName(deploymentResult.HostProcess.MainModule.FileName), await deploymentResult.HttpClient.GetStringAsync("/DllDirectory")); + } } } } diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewHandler.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewHandler.cs index cbe43ec0c7..e768df3bb6 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewHandler.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewHandler.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class RequiresNewHandlerAttribute : Attribute, ITestCondition { - public bool IsMet => !DeployerSelector.IsForwardsCompatibilityTest; + public bool IsMet => DeployerSelector.HasNewHandler; public string SkipReason => "Test verifies new behavior in the aspnetcorev2_inprocess.dll that isn't supported in earlier versions."; } diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewShim.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewShim.cs index b0bc50a83b..3f9b5cae8b 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewShim.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/RequiresNewShim.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] public sealed class RequiresNewShimAttribute : Attribute, ITestCondition { - public bool IsMet => !DeployerSelector.IsBackwardsCompatiblityTest; + public bool IsMet => DeployerSelector.HasNewShim; public string SkipReason => "Test verifies new behavior in the aspnetcorev2.dll that isn't supported in earlier versions."; } diff --git a/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/DeployerSelector.cs b/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/DeployerSelector.cs index 5c6f3739a4..18ee47d427 100644 --- a/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/DeployerSelector.cs +++ b/src/Servers/IIS/IIS/test/IIS.BackwardsCompatibility.FunctionalTests/DeployerSelector.cs @@ -8,7 +8,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public static class DeployerSelector { public static ServerType ServerType => ServerType.IIS; - public static bool IsBackwardsCompatiblityTest => true; public static bool IsForwardsCompatibilityTest => false; + public static bool HasNewShim => false; + public static bool HasNewHandler => true; } } diff --git a/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/DeployerSelector.cs b/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/DeployerSelector.cs index bd7aabbe0f..2addfc912c 100644 --- a/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/DeployerSelector.cs +++ b/src/Servers/IIS/IIS/test/IIS.ForwardsCompatibility.FunctionalTests/DeployerSelector.cs @@ -8,7 +8,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public static class DeployerSelector { public static ServerType ServerType => ServerType.IIS; - public static bool IsBackwardsCompatiblityTest => false; public static bool IsForwardsCompatibilityTest => true; + public static bool HasNewShim => true; + public static bool HasNewHandler => false; } } diff --git a/src/Servers/IIS/IIS/test/IIS.FunctionalTests/DeployerSelector.cs b/src/Servers/IIS/IIS/test/IIS.FunctionalTests/DeployerSelector.cs index f2e1be321e..1451a0c34c 100644 --- a/src/Servers/IIS/IIS/test/IIS.FunctionalTests/DeployerSelector.cs +++ b/src/Servers/IIS/IIS/test/IIS.FunctionalTests/DeployerSelector.cs @@ -8,7 +8,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public static class DeployerSelector { public static ServerType ServerType => ServerType.IIS; - public static bool IsBackwardsCompatiblityTest => false; public static bool IsForwardsCompatibilityTest => false; + public static bool HasNewShim => true; + public static bool HasNewHandler => true; } } diff --git a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/DeployerSelector.cs b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/DeployerSelector.cs index ba6a1ec6e2..f91b27009f 100644 --- a/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/DeployerSelector.cs +++ b/src/Servers/IIS/IIS/test/IISExpress.FunctionalTests/DeployerSelector.cs @@ -8,7 +8,8 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests public static class DeployerSelector { public static ServerType ServerType => ServerType.IISExpress; - public static bool IsBackwardsCompatiblityTest => false; public static bool IsForwardsCompatibilityTest => false; + public static bool HasNewShim => true; + public static bool HasNewHandler => true; } } diff --git a/src/Servers/IIS/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs b/src/Servers/IIS/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs index b5d7f0305f..851654966b 100644 --- a/src/Servers/IIS/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs +++ b/src/Servers/IIS/IIS/test/testassets/shared/SharedStartup/Startup.shared.cs @@ -4,6 +4,8 @@ using System; using System.IO; using System.Linq; +using System.Runtime.InteropServices; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; @@ -21,14 +23,15 @@ namespace TestSite serviceCollection.AddResponseCompression(); } - private async Task HostingEnvironment(HttpContext ctx) - { - var hostingEnv = ctx.RequestServices.GetService(); + private async Task ContentRootPath(HttpContext ctx) => await ctx.Response.WriteAsync(ctx.RequestServices.GetService().ContentRootPath); - await ctx.Response.WriteAsync("ContentRootPath " + hostingEnv.ContentRootPath + Environment.NewLine); - await ctx.Response.WriteAsync("WebRootPath " + hostingEnv.WebRootPath + Environment.NewLine); - await ctx.Response.WriteAsync("CurrentDirectory " + Environment.CurrentDirectory); - } + private async Task WebRootPath(HttpContext ctx) => await ctx.Response.WriteAsync(ctx.RequestServices.GetService().WebRootPath); + + private async Task CurrentDirectory(HttpContext ctx) => await ctx.Response.WriteAsync(Environment.CurrentDirectory); + + private async Task BaseDirectory(HttpContext ctx) => await ctx.Response.WriteAsync(AppContext.BaseDirectory); + + private async Task ASPNETCORE_IIS_PHYSICAL_PATH(HttpContext ctx) => await ctx.Response.WriteAsync(Environment.GetEnvironmentVariable("ASPNETCORE_IIS_PHYSICAL_PATH")); private async Task ConsoleWrite(HttpContext ctx) { @@ -113,5 +116,15 @@ namespace TestSite await context.Response.Body.WriteAsync(new byte[100], 0, 100); }); } + + [DllImport("kernel32.dll")] + static extern uint GetDllDirectory(uint nBufferLength, [Out] StringBuilder lpBuffer); + + private async Task DllDirectory(HttpContext context) + { + var builder = new StringBuilder(1024); + GetDllDirectory(1024, builder); + await context.Response.WriteAsync(builder.ToString()); + } } } From 437baf6f5e02410de25d1a417cd3bf9b50ca281d Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 15 Jan 2019 10:33:26 -0800 Subject: [PATCH 2/4] Port Handle EOF HResult from async callback to 2.2 (#6483) --- eng/PatchConfig.props | 3 +- .../managedexports.cpp | 12 ------ .../IIS/IIS/src/Core/IO/AsyncIOEngine.Read.cs | 2 + .../IIS/IIS/src/Core/IO/AsyncIOOperation.cs | 4 +- .../Core/IO/WebSocketsAsyncIOEngine.Read.cs | 2 + src/Servers/IIS/IIS/src/NativeMethods.cs | 2 +- .../Inprocess/SynchronousReadAndWriteTests.cs | 39 +++++++++++++++++++ .../testassets/InProcessWebSite/Startup.cs | 7 ++++ 8 files changed, 56 insertions(+), 15 deletions(-) diff --git a/eng/PatchConfig.props b/eng/PatchConfig.props index 44b7a873f5..0447c82a69 100644 --- a/eng/PatchConfig.props +++ b/eng/PatchConfig.props @@ -27,9 +27,10 @@ Later on, this will be checked using this condition: - @aspnet/signalr; + Microsoft.AspNetCore.AspNetCoreModuleV2; Microsoft.AspNetCore.Authentication.Google; Microsoft.AspNetCore.Http; + Microsoft.AspNetCore.Server.IIS; diff --git a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp index 6085f65877..e32e342016 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/managedexports.cpp @@ -243,12 +243,6 @@ http_read_request_bytes( fAsync, pdwBytesReceived, pfCompletionPending); - - if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)) - { - // We reached the end of the data - hr = S_OK; - } } else { @@ -330,12 +324,6 @@ http_websockets_read_bytes( pDwBytesReceived, pfCompletionPending); - if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)) - { - // We reached the end of the data - hr = S_OK; - } - return hr; } diff --git a/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Read.cs b/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Read.cs index 00c6709453..abe60ccea6 100644 --- a/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Read.cs +++ b/src/Servers/IIS/IIS/src/Core/IO/AsyncIOEngine.Read.cs @@ -58,6 +58,8 @@ namespace Microsoft.AspNetCore.Server.IIS.Core.IO { _inputHandle.Dispose(); } + + protected override bool IsSuccessfulResult(int hr) => hr == NativeMethods.ERROR_HANDLE_EOF; } } } diff --git a/src/Servers/IIS/IIS/src/Core/IO/AsyncIOOperation.cs b/src/Servers/IIS/IIS/src/Core/IO/AsyncIOOperation.cs index 80e9234ea2..395457dc11 100644 --- a/src/Servers/IIS/IIS/src/Core/IO/AsyncIOOperation.cs +++ b/src/Servers/IIS/IIS/src/Core/IO/AsyncIOOperation.cs @@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Server.IIS.Core.IO if (hr != NativeMethods.ERROR_OPERATION_ABORTED) { _result = bytes; - if (hr != NativeMethods.HR_OK) + if (hr != NativeMethods.HR_OK && !IsSuccessfulResult(hr)) { // Treat all errors as the client disconnect _exception = new ConnectionResetException("The client has disconnected", Marshal.GetExceptionForHR(hr)); @@ -126,6 +126,8 @@ namespace Microsoft.AspNetCore.Server.IIS.Core.IO return asyncContinuation; } + protected virtual bool IsSuccessfulResult(int hr) => false; + public virtual void FreeOperationResources(int hr, int bytes) { } protected virtual void ResetOperation() diff --git a/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Read.cs b/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Read.cs index 413fa77703..2dac1a234e 100644 --- a/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Read.cs +++ b/src/Servers/IIS/IIS/src/Core/IO/WebSocketsAsyncIOEngine.Read.cs @@ -74,6 +74,8 @@ namespace Microsoft.AspNetCore.Server.IIS.Core.IO _engine.ReturnOperation(this); } + + protected override bool IsSuccessfulResult(int hr) => hr == NativeMethods.ERROR_HANDLE_EOF; } } } diff --git a/src/Servers/IIS/IIS/src/NativeMethods.cs b/src/Servers/IIS/IIS/src/NativeMethods.cs index 8e5e484edd..4702d29a57 100644 --- a/src/Servers/IIS/IIS/src/NativeMethods.cs +++ b/src/Servers/IIS/IIS/src/NativeMethods.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Server.IIS internal const int ERROR_NOT_FOUND = unchecked((int)0x80070490); internal const int ERROR_OPERATION_ABORTED = unchecked((int)0x800703E3); internal const int ERROR_INVALID_PARAMETER = unchecked((int)0x80070057); - internal const int COR_E_IO = unchecked((int)0x80131620); + internal const int ERROR_HANDLE_EOF = unchecked((int)0x80070026); private const string KERNEL32 = "kernel32.dll"; diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs index 03aa0e16e6..ae8bf44217 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs @@ -193,5 +193,44 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests await connection.WaitForConnectionClose(); } } + + [ConditionalFact] + [RequiresNewHandler] + public async Task AsyncChunkedPostIsAccepted() + { + // This test sends a lot of request because we are trying to force + // different async completion modes from IIS + for (int i = 0; i < 100; i++) + { + using (var connection = _fixture.CreateTestConnection()) + { + await connection.Send( + "POST /ReadFullBody HTTP/1.1", + $"Transfer-Encoding: chunked", + "Host: localhost", + "Connection: close", + "", + ""); + + await connection.Send("5", + "Hello", + ""); + + await connection.Send( + "0", + "", + ""); + + await connection.Receive( + "HTTP/1.1 200 OK", + ""); + + await connection.ReceiveHeaders(); + await connection.Receive("Completed"); + + await connection.WaitForConnectionClose(); + } + } + } } } diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs index eb84272b76..c19b4e80db 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs @@ -306,6 +306,13 @@ namespace TestSite } } + private async Task ReadFullBody(HttpContext ctx) + { + await ReadRequestBody(ctx); + ctx.Response.ContentLength = 9; + await ctx.Response.WriteAsync("Completed"); + } + private async Task WriteManyTimesToResponseBody(HttpContext ctx) { for (var i = 0; i < 10000; i++) From 852d890cd6d1f3729e48182d21ae59f42c9450fb Mon Sep 17 00:00:00 2001 From: Mikael Mengistu Date: Tue, 15 Jan 2019 11:15:21 -0800 Subject: [PATCH 3/4] Check HubConnection state before running invoke logic (#4400) --- eng/PatchConfig.props | 1 + .../java/com/microsoft/signalr/HubConnection.java | 6 +++++- .../com/microsoft/signalr/HubConnectionTest.java | 13 +++++++++++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/eng/PatchConfig.props b/eng/PatchConfig.props index 0447c82a69..198f32e4d7 100644 --- a/eng/PatchConfig.props +++ b/eng/PatchConfig.props @@ -31,6 +31,7 @@ Later on, this will be checked using this condition: Microsoft.AspNetCore.Authentication.Google; Microsoft.AspNetCore.Http; Microsoft.AspNetCore.Server.IIS; + java:signalr; diff --git a/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java b/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java index 485137d0a6..9b76b389f5 100644 --- a/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java +++ b/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java @@ -454,7 +454,7 @@ public class HubConnection { */ public void send(String method, Object... args) { if (hubConnectionState != HubConnectionState.CONNECTED) { - throw new RuntimeException("The 'send' method cannot be called if the connection is not active"); + throw new RuntimeException("The 'send' method cannot be called if the connection is not active."); } InvocationMessage invocationMessage = new InvocationMessage(null, method, args); @@ -472,6 +472,10 @@ public class HubConnection { */ @SuppressWarnings("unchecked") public Single invoke(Class returnType, String method, Object... args) { + if (hubConnectionState != HubConnectionState.CONNECTED) { + throw new RuntimeException("The 'invoke' method cannot be called if the connection is not active."); + } + String id = connectionState.getNextInvocationId(); InvocationMessage invocationMessage = new InvocationMessage(id, method, args); diff --git a/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/HubConnectionTest.java b/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/HubConnectionTest.java index 6c29be1c82..c46de57b3e 100644 --- a/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/HubConnectionTest.java +++ b/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/HubConnectionTest.java @@ -900,7 +900,16 @@ class HubConnectionTest { assertEquals(HubConnectionState.DISCONNECTED, hubConnection.getConnectionState()); Throwable exception = assertThrows(RuntimeException.class, () -> hubConnection.send("inc")); - assertEquals("The 'send' method cannot be called if the connection is not active", exception.getMessage()); + assertEquals("The 'send' method cannot be called if the connection is not active.", exception.getMessage()); + } + + @Test + public void cannotInvokeBeforeStart() { + HubConnection hubConnection = TestUtils.createHubConnection("http://example.com"); + assertEquals(HubConnectionState.DISCONNECTED, hubConnection.getConnectionState()); + + Throwable exception = assertThrows(RuntimeException.class, () -> hubConnection.invoke(String.class, "inc", "arg1")); + assertEquals("The 'invoke' method cannot be called if the connection is not active.", exception.getMessage()); } @Test @@ -1204,4 +1213,4 @@ class HubConnectionTest { () -> hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait()); assertEquals("Unexpected status code returned from negotiate: 500 Internal server error.", exception.getMessage()); } -} \ No newline at end of file +} From 874a67a242e3718e8cf7c33a594fc946d03f2eb3 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Tue, 15 Jan 2019 11:17:02 -0800 Subject: [PATCH 4/4] Add @aspnet/signalr to PackagesInPatch (#6716) --- eng/PatchConfig.props | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/PatchConfig.props b/eng/PatchConfig.props index 198f32e4d7..42789ba1b1 100644 --- a/eng/PatchConfig.props +++ b/eng/PatchConfig.props @@ -27,6 +27,7 @@ Later on, this will be checked using this condition: + @aspnet/signalr; Microsoft.AspNetCore.AspNetCoreModuleV2; Microsoft.AspNetCore.Authentication.Google; Microsoft.AspNetCore.Http;