From 6ca61a8fc760993894d1725d0f3854bf25efd0d0 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 30 Mar 2018 15:15:18 -0700 Subject: [PATCH] Merge release/2.1 to dev --- IISIntegration.sln | 119 +-- build/dependencies.props | 67 +- .../Properties => build}/launchSettings.json | 1 + build/testsite.props | 59 +- .../NativeIISSample/NativeIISSample.csproj | 1 - src/CommonLib/hostfxr_utility.cpp | 697 +++++++++++------- src/CommonLib/hostfxr_utility.h | 30 +- src/CommonLib/resources.h | 1 + src/CommonLib/utility.h | 2 +- src/IISLib/util.cxx | 6 +- .../NativeMethods.cs | 241 ++++-- .../Server/IISAwaitable.cs | 4 +- .../IISHttpContext.FeatureCollection.cs | 5 +- .../Server/IISHttpContext.ReadWrite.cs | 10 +- .../Server/IISHttpContext.Websockets.cs | 6 +- .../Server/IISHttpContext.cs | 34 +- .../Server/IISHttpServer.cs | 14 +- .../Server/IISServerSetupFilter.cs | 8 + .../WebHostBuilderIISExtensions.cs | 46 +- .../outofprocess/serverprocess.cxx | 47 +- .../outofprocess/serverprocess.h | 8 +- .../Properties/launchSettings.json | 27 - .../AspNetCoreModuleTests.vcxproj | 1 + .../hostfxr_utility_tests.cpp | 60 +- test/AspNetCoreModuleTests/utility_tests.cpp | 52 ++ .../IISIntegration.FunctionalTests.csproj | 4 +- .../Inprocess/ServerVariablesTest.cs | 7 +- .../Inprocess/StartupTests.cs | 233 ++++++ .../Inprocess/SynchronousReadAndWriteTests.cs | 2 +- .../Utilities/Helpers.cs | 38 +- test/TestTasks/InjectRequestHandler.cs | 62 ++ test/TestTasks/TestTasks.csproj | 12 + .../InProcessWebSite/InProcessWebSite.csproj} | 5 +- .../InProcessWebSite}/Program.cs | 2 +- .../Properties/launchSettings.json | 1 + .../InProcessWebSite}/Startup.cs | 5 + .../InProcessWebSite}/web.config | 0 .../OutOfProcessWebSite.csproj} | 4 +- .../OutOfProcessWebSite}/Program.cs | 2 +- .../Properties/launchSettings.json | 37 + .../OutOfProcessWebSite}/StartupHelloWorld.cs | 0 .../StartupHttpsHelloWorld.cs | 0 .../StartupNtlmAuthentication.cs | 0 .../StartupUpgradeFeatureDetection.cs | 0 .../OutOfProcessWebSite}/web.config | 0 .../OverriddenServerWebSite.csproj | 17 + .../OverriddenServerWebSite/Program.cs | 48 ++ .../Properties/launchSettings.json | 37 + .../OverriddenServerWebSite/web.config | 9 + .../StressTestWebSite}/Program.cs | 9 - .../Properties/launchSettings.json | 37 + .../StressTestWebSite}/Startup.cs | 0 .../StressTestWebSite.csproj} | 7 +- .../WebSockets/Constants.cs | 0 .../WebSockets/HandshakeHelpers.cs | 0 55 files changed, 1559 insertions(+), 565 deletions(-) rename {test/TestSites/Properties => build}/launchSettings.json (97%) delete mode 100644 test/ANCMStressTestApp/Properties/launchSettings.json create mode 100644 test/AspNetCoreModuleTests/utility_tests.cpp create mode 100644 test/IISIntegration.FunctionalTests/Inprocess/StartupTests.cs create mode 100644 test/TestTasks/InjectRequestHandler.cs create mode 100644 test/TestTasks/TestTasks.csproj rename test/{IISTestSite/IISTestSite.csproj => WebSites/InProcessWebSite/InProcessWebSite.csproj} (83%) rename test/{IISTestSite => WebSites/InProcessWebSite}/Program.cs (92%) rename test/{IISTestSite => WebSites/InProcessWebSite}/Properties/launchSettings.json (97%) rename test/{IISTestSite => WebSites/InProcessWebSite}/Startup.cs (99%) rename test/{IISTestSite => WebSites/InProcessWebSite}/web.config (100%) rename test/{TestSites/TestSites.csproj => WebSites/OutOfProcessWebSite/OutOfProcessWebSite.csproj} (83%) rename test/{TestSites => WebSites/OutOfProcessWebSite}/Program.cs (92%) create mode 100644 test/WebSites/OutOfProcessWebSite/Properties/launchSettings.json rename test/{TestSites => WebSites/OutOfProcessWebSite}/StartupHelloWorld.cs (100%) rename test/{TestSites => WebSites/OutOfProcessWebSite}/StartupHttpsHelloWorld.cs (100%) rename test/{TestSites => WebSites/OutOfProcessWebSite}/StartupNtlmAuthentication.cs (100%) rename test/{TestSites => WebSites/OutOfProcessWebSite}/StartupUpgradeFeatureDetection.cs (100%) rename test/{TestSites => WebSites/OutOfProcessWebSite}/web.config (100%) create mode 100644 test/WebSites/OverriddenServerWebSite/OverriddenServerWebSite.csproj create mode 100644 test/WebSites/OverriddenServerWebSite/Program.cs create mode 100644 test/WebSites/OverriddenServerWebSite/Properties/launchSettings.json create mode 100644 test/WebSites/OverriddenServerWebSite/web.config rename test/{ANCMStressTestApp => WebSites/StressTestWebSite}/Program.cs (69%) create mode 100644 test/WebSites/StressTestWebSite/Properties/launchSettings.json rename test/{ANCMStressTestApp => WebSites/StressTestWebSite}/Startup.cs (100%) rename test/{ANCMStressTestApp/ANCMStressTestApp.csproj => WebSites/StressTestWebSite/StressTestWebSite.csproj} (68%) rename test/{ANCMStressTestApp => WebSites/StressTestWebSite}/WebSockets/Constants.cs (100%) rename test/{ANCMStressTestApp => WebSites/StressTestWebSite}/WebSockets/HandshakeHelpers.cs (100%) diff --git a/IISIntegration.sln b/IISIntegration.sln index 4086f09be5..e14a378b23 100644 --- a/IISIntegration.sln +++ b/IISIntegration.sln @@ -29,7 +29,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.IISIntegration.Tests", "test\Microsoft.AspNetCore.Server.IISIntegration.Tests\Microsoft.AspNetCore.Server.IISIntegration.Tests.csproj", "{4106DB10-E09F-480E-9CE6-B39235512EE6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestSites", "test\TestSites\TestSites.csproj", "{F54715C3-88D8-49E3-A291-C13570FE81FC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OutOfProcessWebSite", "test\WebSites\OutOfProcessWebSite\OutOfProcessWebSite.csproj", "{F54715C3-88D8-49E3-A291-C13570FE81FC}" ProjectSection(ProjectDependencies) = postProject {D57EA297-6DC2-4BC0-8C91-334863327863} = {D57EA297-6DC2-4BC0-8C91-334863327863} {439824F9-1455-4CC4-BD79-B44FA0A16552} = {439824F9-1455-4CC4-BD79-B44FA0A16552} @@ -54,7 +54,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeIISSample", "samples\ {439824F9-1455-4CC4-BD79-B44FA0A16552} = {439824F9-1455-4CC4-BD79-B44FA0A16552} EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IISTestSite", "test\IISTestSite\IISTestSite.csproj", "{679FA2A2-898B-4320-884E-C2D294A97CE1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcessWebSite", "test\WebSites\InProcessWebSite\InProcessWebSite.csproj", "{679FA2A2-898B-4320-884E-C2D294A97CE1}" ProjectSection(ProjectDependencies) = postProject {D57EA297-6DC2-4BC0-8C91-334863327863} = {D57EA297-6DC2-4BC0-8C91-334863327863} {439824F9-1455-4CC4-BD79-B44FA0A16552} = {439824F9-1455-4CC4-BD79-B44FA0A16552} @@ -72,7 +72,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AspNetCoreModuleTests", "test\AspNetCoreModuleTests\AspNetCoreModuleTests.vcxproj", "{0692D963-DB10-4387-B3EA-460FBB9BD9A3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ANCMStressTestApp", "test\ANCMStressTestApp\ANCMStressTestApp.csproj", "{13FD8F12-FFBE-4D01-B4AC-444F2994B04F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StressTestWebSite", "test\WebSites\StressTestWebSite\StressTestWebSite.csproj", "{13FD8F12-FFBE-4D01-B4AC-444F2994B04F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestTasks", "test\TestTasks\TestTasks.csproj", "{064D860B-4D7C-4B1D-918F-E020F1B99E2A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSites", "WebSites", "{744ACDC6-F6A0-4FF9-9421-F25C5F2DC520}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OverriddenServerWebSite", "test\WebSites\OverriddenServerWebSite\OverriddenServerWebSite.csproj", "{FC2A97F8-A749-4C04-97D1-97500066A820}" + ProjectSection(ProjectDependencies) = postProject + {D57EA297-6DC2-4BC0-8C91-334863327863} = {D57EA297-6DC2-4BC0-8C91-334863327863} + {439824F9-1455-4CC4-BD79-B44FA0A16552} = {439824F9-1455-4CC4-BD79-B44FA0A16552} + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -120,18 +130,18 @@ Global {4106DB10-E09F-480E-9CE6-B39235512EE6}.Release|x64.Build.0 = Release|Any CPU {4106DB10-E09F-480E-9CE6-B39235512EE6}.Release|x86.ActiveCfg = Release|Any CPU {4106DB10-E09F-480E-9CE6-B39235512EE6}.Release|x86.Build.0 = Release|Any CPU - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|Any CPU.ActiveCfg = Debug|x64 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|Any CPU.Build.0 = Debug|x64 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|x64.ActiveCfg = Debug|x64 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|x64.Build.0 = Debug|x64 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|x86.ActiveCfg = Debug|x86 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|x86.Build.0 = Debug|x86 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|Any CPU.ActiveCfg = Release|x64 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|Any CPU.Build.0 = Release|x64 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|x64.ActiveCfg = Release|x64 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|x64.Build.0 = Release|x64 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|x86.ActiveCfg = Release|x86 - {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|x86.Build.0 = Release|x86 + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|x64.ActiveCfg = Debug|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|x64.Build.0 = Debug|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|x86.ActiveCfg = Debug|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Debug|x86.Build.0 = Debug|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|Any CPU.Build.0 = Release|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|x64.ActiveCfg = Release|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|x64.Build.0 = Release|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|x86.ActiveCfg = Release|Any CPU + {F54715C3-88D8-49E3-A291-C13570FE81FC}.Release|x86.Build.0 = Release|Any CPU {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -156,18 +166,18 @@ Global {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Release|x64.Build.0 = Release|x64 {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Release|x86.ActiveCfg = Release|x86 {9BC4AFCB-325D-4C81-8228-8CF301CE2F97}.Release|x86.Build.0 = Release|x86 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|Any CPU.ActiveCfg = Debug|x64 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|Any CPU.Build.0 = Debug|x64 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|x64.ActiveCfg = Debug|x64 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|x64.Build.0 = Debug|x64 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|x86.ActiveCfg = Debug|x86 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|x86.Build.0 = Debug|x86 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|Any CPU.ActiveCfg = Release|x64 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|Any CPU.Build.0 = Release|x64 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x64.ActiveCfg = Release|x64 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x64.Build.0 = Release|x64 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x86.ActiveCfg = Release|x86 - {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x86.Build.0 = Release|x86 + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|x64.ActiveCfg = Debug|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|x64.Build.0 = Debug|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Debug|x86.Build.0 = Debug|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|Any CPU.Build.0 = Release|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x64.ActiveCfg = Release|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x64.Build.0 = Release|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x86.ActiveCfg = Release|Any CPU + {679FA2A2-898B-4320-884E-C2D294A97CE1}.Release|x86.Build.0 = Release|Any CPU {439824F9-1455-4CC4-BD79-B44FA0A16552}.Debug|Any CPU.ActiveCfg = Debug|x64 {439824F9-1455-4CC4-BD79-B44FA0A16552}.Debug|x64.ActiveCfg = Debug|x64 {439824F9-1455-4CC4-BD79-B44FA0A16552}.Debug|x64.Build.0 = Debug|x64 @@ -230,18 +240,40 @@ Global {0692D963-DB10-4387-B3EA-460FBB9BD9A3}.Release|x64.Build.0 = Release|x64 {0692D963-DB10-4387-B3EA-460FBB9BD9A3}.Release|x86.ActiveCfg = Release|Win32 {0692D963-DB10-4387-B3EA-460FBB9BD9A3}.Release|x86.Build.0 = Release|Win32 - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|x64.ActiveCfg = Debug|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|x64.Build.0 = Debug|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|x86.ActiveCfg = Debug|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|x86.Build.0 = Debug|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|Any CPU.Build.0 = Release|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|x64.ActiveCfg = Release|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|x64.Build.0 = Release|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|x86.ActiveCfg = Release|Any CPU - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|x86.Build.0 = Release|Any CPU + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|Any CPU.ActiveCfg = Debug|x86 + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|x64.ActiveCfg = Debug|x64 + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|x64.Build.0 = Debug|x64 + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|x86.ActiveCfg = Debug|x86 + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Debug|x86.Build.0 = Debug|x86 + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|Any CPU.ActiveCfg = Release|x86 + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|x64.ActiveCfg = Release|x64 + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|x64.Build.0 = Release|x64 + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|x86.ActiveCfg = Release|x86 + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F}.Release|x86.Build.0 = Release|x86 + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Debug|x64.ActiveCfg = Debug|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Debug|x64.Build.0 = Debug|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Debug|x86.ActiveCfg = Debug|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Debug|x86.Build.0 = Debug|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Release|Any CPU.Build.0 = Release|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Release|x64.ActiveCfg = Release|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Release|x64.Build.0 = Release|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Release|x86.ActiveCfg = Release|Any CPU + {064D860B-4D7C-4B1D-918F-E020F1B99E2A}.Release|x86.Build.0 = Release|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Debug|x64.ActiveCfg = Debug|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Debug|x64.Build.0 = Debug|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Debug|x86.ActiveCfg = Debug|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Debug|x86.Build.0 = Debug|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Release|Any CPU.Build.0 = Release|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Release|x64.ActiveCfg = Release|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Release|x64.Build.0 = Release|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Release|x86.ActiveCfg = Release|Any CPU + {FC2A97F8-A749-4C04-97D1-97500066A820}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -250,17 +282,20 @@ Global {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64} = {C74B8F36-FD2F-45C9-9B8A-00E7CF0126A9} {8B3446E8-E6A8-4591-AA63-A95837C6E97C} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} {4106DB10-E09F-480E-9CE6-B39235512EE6} = {EF30B533-D715-421A-92B7-92FEF460AC9C} - {F54715C3-88D8-49E3-A291-C13570FE81FC} = {EF30B533-D715-421A-92B7-92FEF460AC9C} + {F54715C3-88D8-49E3-A291-C13570FE81FC} = {744ACDC6-F6A0-4FF9-9421-F25C5F2DC520} {4E3E1F5C-CD52-4CC0-A35F-D1FA1685D2FA} = {EF30B533-D715-421A-92B7-92FEF460AC9C} {9BC4AFCB-325D-4C81-8228-8CF301CE2F97} = {C74B8F36-FD2F-45C9-9B8A-00E7CF0126A9} - {679FA2A2-898B-4320-884E-C2D294A97CE1} = {EF30B533-D715-421A-92B7-92FEF460AC9C} + {679FA2A2-898B-4320-884E-C2D294A97CE1} = {744ACDC6-F6A0-4FF9-9421-F25C5F2DC520} {439824F9-1455-4CC4-BD79-B44FA0A16552} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} {4787A64F-9A3E-4867-A55A-70CB4B2B2FFE} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} {55494E58-E061-4C4C-A0A8-837008E72F85} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} {D57EA297-6DC2-4BC0-8C91-334863327863} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} {46A8612B-418B-4D70-B3A7-A21DD0627473} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD} {0692D963-DB10-4387-B3EA-460FBB9BD9A3} = {EF30B533-D715-421A-92B7-92FEF460AC9C} - {13FD8F12-FFBE-4D01-B4AC-444F2994B04F} = {EF30B533-D715-421A-92B7-92FEF460AC9C} + {13FD8F12-FFBE-4D01-B4AC-444F2994B04F} = {744ACDC6-F6A0-4FF9-9421-F25C5F2DC520} + {064D860B-4D7C-4B1D-918F-E020F1B99E2A} = {EF30B533-D715-421A-92B7-92FEF460AC9C} + {744ACDC6-F6A0-4FF9-9421-F25C5F2DC520} = {EF30B533-D715-421A-92B7-92FEF460AC9C} + {FC2A97F8-A749-4C04-97D1-97500066A820} = {744ACDC6-F6A0-4FF9-9421-F25C5F2DC520} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DB4F868D-E1AE-4FD7-9333-69FA15B268C5} diff --git a/build/dependencies.props b/build/dependencies.props index 057ebd9114..5cd40704ec 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -4,43 +4,46 @@ 2.1.0-preview3-17001 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 0.5.0-preview2-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 - 2.1.0-preview3-32037 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 0.5.0-preview2-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 15.6.82 + 15.6.82 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 + 2.1.0-preview3-32094 1.1.0 - 2.1.0-preview3-32037 + 2.1.0-preview3-32094 2.0.0 - 2.1.0-preview2-26314-02 - 2.1.0-preview3-32037 + 2.1.0-preview2-26326-03 + 2.1.0-preview3-32094 15.6.1 7.0.0 - 4.5.0-preview2-26313-01 - 4.5.0-preview2-26313-01 + 4.5.0-preview2-26326-04 + 4.5.0-preview2-26326-04 6.1.7601.17515 - 4.5.0-preview2-26313-01 - 4.5.0-preview2-26313-01 - 4.5.0-preview2-26313-01 - 4.5.0-preview2-26313-01 - 4.5.0-preview2-26313-01 + 4.5.0-preview2-26326-04 + 4.5.0-preview2-26326-04 + 4.5.0-preview2-26326-04 + 4.5.0-preview2-26326-04 + 4.5.0-preview2-26326-04 + 9.0.1 2.3.1 2.4.0-beta.1.build3945 diff --git a/test/TestSites/Properties/launchSettings.json b/build/launchSettings.json similarity index 97% rename from test/TestSites/Properties/launchSettings.json rename to build/launchSettings.json index 7d09a120ab..6d5ce43f73 100644 --- a/test/TestSites/Properties/launchSettings.json +++ b/build/launchSettings.json @@ -12,6 +12,7 @@ "commandName": "Executable", "executablePath": "$(IISExpressPath)", "commandLineArgs": "$(IISExpressArguments)", + "nativeDebugging": true, "environmentVariables": { "IIS_SITE_PATH": "$(MSBuildThisFileDirectory)", "ANCM_PATH": "$(TargetDir)$(AncmPath)", diff --git a/build/testsite.props b/build/testsite.props index 549556790f..0a3f00ac79 100644 --- a/build/testsite.props +++ b/build/testsite.props @@ -8,15 +8,7 @@ x64 $(Platform) - - - - - - - - - + $(MSBuildProgramFiles32)\IIS Express\iisexpress.exe $(SystemRoot)\SysWOW64\inetsrv\w3wp.exe @@ -29,12 +21,61 @@ x64 + + + + + + + /config:"$(IISExpressAppHostConfig)" -h "$(IISAppHostConfig)" $(NativePlatform)\aspnetcore.dll + $(NativePlatform)\aspnetcorerh.dll $(userprofile)\.dotnet\$(NativePlatform)\dotnet.exe + + + + + + + + + False + + + + + + + $(MSBuildThisFileDirectory)..\test\TestTasks\bin\$(Configuration)\$(TargetFramework)\TestTasks + + $(InjectDepsAssembly) + "win7-$(NativePlatform)" "$(AncmRHPath)" + + + + $(InjectDepsAssembly).exe + $(InjectDepsAssembly) + + + + $(InjectDepsAssembly).dll + dotnet + $(InjectDepsAssembly) $(InjectDepsArguments) + + + + + + + + + + + diff --git a/samples/NativeIISSample/NativeIISSample.csproj b/samples/NativeIISSample/NativeIISSample.csproj index d35016b658..d9bf22ba16 100644 --- a/samples/NativeIISSample/NativeIISSample.csproj +++ b/samples/NativeIISSample/NativeIISSample.csproj @@ -17,5 +17,4 @@ inprocess - diff --git a/src/CommonLib/hostfxr_utility.cpp b/src/CommonLib/hostfxr_utility.cpp index b7fe5a1eea..6d3d6d74a0 100644 --- a/src/CommonLib/hostfxr_utility.cpp +++ b/src/CommonLib/hostfxr_utility.cpp @@ -164,19 +164,25 @@ HOSTFXR_UTILITY::GetHostFxrParameters( { HRESULT hr = S_OK; STRU struSystemPathVariable; - STRU struHostFxrPath; - STRU struExeLocation; - STRU struHostFxrSearchExpression; - STRU struHighestDotnetVersion; + STRU struAbsolutePathToHostFxr; + STRU struAbsolutePathToDotnet; STRU struEventMsg; - std::vector vVersionFolders; - DWORD dwPosition; + STACK_STRU(struExpandedProcessPath, MAX_PATH); + STACK_STRU(struExpandedArguments, MAX_PATH); - // Convert the process path an absolute path. + // Copy and Expand the processPath and Arguments. + if (FAILED(hr = struExpandedProcessPath.CopyAndExpandEnvironmentStrings(pcwzProcessPath)) + || FAILED(hr = struExpandedArguments.CopyAndExpandEnvironmentStrings(pcwzArguments))) + { + goto Finished; + } + + // Convert the process path an absolute path to our current application directory. + // If the path is already an absolute path, it will be unchanged. hr = UTILITY::ConvertPathToFullPath( - pcwzProcessPath, + struExpandedProcessPath.QueryStr(), pcwzApplicationPhysicalPath, - &struExeLocation + &struAbsolutePathToDotnet ); if (FAILED(hr)) @@ -184,152 +190,76 @@ HOSTFXR_UTILITY::GetHostFxrParameters( goto Finished; } - if (UTILITY::CheckIfFileExists(struExeLocation.QueryStr())) + // Check if the absolute path is to dotnet or not. + if (struAbsolutePathToDotnet.EndsWith(L"dotnet.exe") || struAbsolutePathToDotnet.EndsWith(L"dotnet")) { - // Check if hostfxr is in this folder, if it is, we are a standalone application, - // else we assume we received an absolute path to dotnet.exe - if (!struExeLocation.EndsWith(L"dotnet.exe") && - !struExeLocation.EndsWith(L"dotnet")) + // + // The processPath ends with dotnet.exe or dotnet + // like: C:\Program Files\dotnet\dotnet.exe, C:\Program Files\dotnet\dotnet, dotnet.exe, or dotnet. + // Get the absolute path to dotnet. If the path is already an absolute path, it will return that path + // + if (FAILED(hr = HOSTFXR_UTILITY::GetAbsolutePathToDotnet(&struAbsolutePathToDotnet))) // Make sure to append the dotnet.exe path correctly here (pass in regular path)? + { + goto Finished; + } + + if (FAILED(hr = GetAbsolutePathToHostFxr(&struAbsolutePathToDotnet, hEventLog, &struAbsolutePathToHostFxr))) + { + goto Finished; + } + + if (FAILED(hr = ParseHostfxrArguments( + struExpandedArguments.QueryStr(), + struAbsolutePathToDotnet.QueryStr(), + pcwzApplicationPhysicalPath, + hEventLog, + pdwArgCount, + pbstrArgv))) + { + goto Finished; + } + + if (FAILED(hr = struHostFxrDllLocation->Copy(struAbsolutePathToHostFxr))) { - hr = GetStandaloneHostfxrParameters( - struExeLocation.QueryStr(), - pcwzApplicationPhysicalPath, - pcwzArguments, - hEventLog, - struHostFxrDllLocation, - pdwArgCount, - pbstrArgv); goto Finished; } } else { - if (FAILED(hr = HOSTFXR_UTILITY::FindDotnetExePath(&struExeLocation))) + // + // The processPath is a path to the application executable + // like: C:\test\MyApp.Exe or MyApp.Exe + // Check if the file exists, and if it does, get the parameters for a standalone application + // + if (UTILITY::CheckIfFileExists(struAbsolutePathToDotnet.QueryStr())) { - goto Finished; + hr = GetStandaloneHostfxrParameters( + struAbsolutePathToDotnet.QueryStr(), + pcwzApplicationPhysicalPath, + struExpandedArguments.QueryStr(), + hEventLog, + struHostFxrDllLocation, + pdwArgCount, + pbstrArgv); } - } - - if (FAILED(hr = struExeLocation.SyncWithBuffer()) || - FAILED(hr = struHostFxrPath.Copy(struExeLocation))) - { - goto Finished; - } - - dwPosition = struHostFxrPath.LastIndexOf(L'\\', 0); - if (dwPosition == -1) - { - hr = E_FAIL; - goto Finished; - } - - struHostFxrPath.QueryStr()[dwPosition] = L'\0'; - - if (FAILED(hr = struHostFxrPath.SyncWithBuffer()) || - FAILED(hr = struHostFxrPath.Append(L"\\"))) - { - goto Finished; - } - - hr = struHostFxrPath.Append(L"host\\fxr"); - if (FAILED(hr)) - { - goto Finished; - } - - if (!UTILITY::DirectoryExists(&struHostFxrPath)) - { - hr = ERROR_BAD_ENVIRONMENT; - if (SUCCEEDED(struEventMsg.SafeSnwprintf( - ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG, - struHostFxrPath.QueryStr(), - hr))) + else { - UTILITY::LogEvent(hEventLog, - EVENTLOG_ERROR_TYPE, - ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND, - struEventMsg.QueryStr()); + // + // If the processPath file does not exist and it doesn't include dotnet.exe or dotnet + // then it is an invalid argument. + // + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);; + if (SUCCEEDED(struEventMsg.SafeSnwprintf( + ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG, + struExpandedProcessPath.QueryStr(), + hr))) + { + UTILITY::LogEvent(hEventLog, + EVENTLOG_ERROR_TYPE, + ASPNETCORE_EVENT_GENERAL_ERROR_MSG, + struEventMsg.QueryStr()); + } } - goto Finished; - } - - // Find all folders under host\\fxr\\ for version numbers. - hr = struHostFxrSearchExpression.Copy(struHostFxrPath); - if (FAILED(hr)) - { - goto Finished; - } - - hr = struHostFxrSearchExpression.Append(L"\\*"); - if (FAILED(hr)) - { - goto Finished; - } - - // As we use the logic from core-setup, we are opting to use std here. - // TODO remove all uses of std? - UTILITY::FindDotNetFolders(struHostFxrSearchExpression.QueryStr(), &vVersionFolders); - - if (vVersionFolders.size() == 0) - { - hr = HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT); - if (SUCCEEDED(struEventMsg.SafeSnwprintf( - ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG, - struHostFxrPath.QueryStr(), - hr))) - { - UTILITY::LogEvent(hEventLog, - EVENTLOG_ERROR_TYPE, - ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND, - struEventMsg.QueryStr()); - } - goto Finished; - } - - hr = UTILITY::FindHighestDotNetVersion(vVersionFolders, &struHighestDotnetVersion); - if (FAILED(hr)) - { - goto Finished; - } - - if (FAILED(hr = struHostFxrPath.Append(L"\\")) - || FAILED(hr = struHostFxrPath.Append(struHighestDotnetVersion.QueryStr())) - || FAILED(hr = struHostFxrPath.Append(L"\\hostfxr.dll"))) - { - goto Finished; - } - - if (!UTILITY::CheckIfFileExists(struHostFxrPath.QueryStr())) - { - // ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG - hr = HRESULT_FROM_WIN32(ERROR_FILE_INVALID); - if (SUCCEEDED(struEventMsg.SafeSnwprintf( - ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG, - struHostFxrPath.QueryStr(), - hr))) - { - UTILITY::LogEvent(hEventLog, - EVENTLOG_ERROR_TYPE, - ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND, - struEventMsg.QueryStr()); - } - goto Finished; - } - - if (FAILED(hr = ParseHostfxrArguments( - pcwzArguments, - struExeLocation.QueryStr(), - pcwzApplicationPhysicalPath, - hEventLog, - pdwArgCount, - pbstrArgv))) - { - goto Finished; - } - - if (FAILED(hr = struHostFxrDllLocation->Copy(struHostFxrPath))) - { - goto Finished; } Finished: @@ -383,7 +313,7 @@ HOSTFXR_UTILITY::ParseHostfxrArguments( goto Failure; } - argv = new PWSTR[argc + 1]; + argv = new BSTR[argc + 1]; if (argv == NULL) { hr = E_OUTOFMEMORY; @@ -457,41 +387,220 @@ Finished: return hr; } -// -// Invoke where.exe to find the location of dotnet.exe -// Copies contents of dotnet.exe to a temp file -// Respects path ordering. HRESULT -HOSTFXR_UTILITY::FindDotnetExePath( - _Out_ STRU* struDotnetPath +HOSTFXR_UTILITY::GetAbsolutePathToDotnet( + _Inout_ STRU* pStruAbsolutePathToDotnet ) { HRESULT hr = S_OK; + + // + // If we are given an absolute path to dotnet.exe, we are done + // + if (UTILITY::CheckIfFileExists(pStruAbsolutePathToDotnet->QueryStr())) + { + goto Finished; + } + + // + // If the path was C:\Program Files\dotnet\dotnet + // We need to try appending .exe and check if the file exists too. + // + if (FAILED(hr = pStruAbsolutePathToDotnet->Append(L".exe"))) + { + goto Finished; + } + + if (UTILITY::CheckIfFileExists(pStruAbsolutePathToDotnet->QueryStr())) + { + goto Finished; + } + + // At this point, we are calling where.exe to find dotnet. + // If we encounter any failures, try getting dotnet.exe from the + // backup location. + if (!InvokeWhereToFindDotnet(pStruAbsolutePathToDotnet)) + { + hr = GetAbsolutePathToDotnetFromProgramFiles(pStruAbsolutePathToDotnet); + } + +Finished: + + return hr; +} + +HRESULT +HOSTFXR_UTILITY::GetAbsolutePathToHostFxr( + STRU* pStruAbsolutePathToDotnet, + HANDLE hEventLog, + STRU* pStruAbsolutePathToHostfxr +) +{ + HRESULT hr = S_OK; + STRU struHostFxrPath; + STRU struHostFxrSearchExpression; + STRU struHighestDotnetVersion; + STRU struEventMsg; + std::vector vVersionFolders; + DWORD dwPosition = 0; + + if (FAILED(hr = struHostFxrPath.Copy(pStruAbsolutePathToDotnet))) + { + goto Finished; + } + + dwPosition = struHostFxrPath.LastIndexOf(L'\\', 0); + if (dwPosition == -1) + { + hr = E_FAIL; + goto Finished; + } + + struHostFxrPath.QueryStr()[dwPosition] = L'\0'; + + if (FAILED(hr = struHostFxrPath.SyncWithBuffer()) || + FAILED(hr = struHostFxrPath.Append(L"\\"))) + { + goto Finished; + } + + hr = struHostFxrPath.Append(L"host\\fxr"); + if (FAILED(hr)) + { + goto Finished; + } + + if (!UTILITY::DirectoryExists(&struHostFxrPath)) + { + hr = ERROR_BAD_ENVIRONMENT; + if (SUCCEEDED(struEventMsg.SafeSnwprintf( + ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG, + struHostFxrPath.QueryStr(), + hr))) + { + UTILITY::LogEvent(hEventLog, + EVENTLOG_ERROR_TYPE, + ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND, + struEventMsg.QueryStr()); + } + goto Finished; + } + + // Find all folders under host\\fxr\\ for version numbers. + hr = struHostFxrSearchExpression.Copy(struHostFxrPath); + if (FAILED(hr)) + { + goto Finished; + } + + hr = struHostFxrSearchExpression.Append(L"\\*"); + if (FAILED(hr)) + { + goto Finished; + } + + // As we use the logic from core-setup, we are opting to use std here. + UTILITY::FindDotNetFolders(struHostFxrSearchExpression.QueryStr(), &vVersionFolders); + + if (vVersionFolders.size() == 0) + { + hr = HRESULT_FROM_WIN32(ERROR_BAD_ENVIRONMENT); + if (SUCCEEDED(struEventMsg.SafeSnwprintf( + ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND_MSG, + struHostFxrPath.QueryStr(), + hr))) + { + UTILITY::LogEvent(hEventLog, + EVENTLOG_ERROR_TYPE, + ASPNETCORE_EVENT_HOSTFXR_DIRECTORY_NOT_FOUND, + struEventMsg.QueryStr()); + } + goto Finished; + } + + hr = UTILITY::FindHighestDotNetVersion(vVersionFolders, &struHighestDotnetVersion); + if (FAILED(hr)) + { + goto Finished; + } + + if (FAILED(hr = struHostFxrPath.Append(L"\\")) + || FAILED(hr = struHostFxrPath.Append(struHighestDotnetVersion.QueryStr())) + || FAILED(hr = struHostFxrPath.Append(L"\\hostfxr.dll"))) + { + goto Finished; + } + + if (!UTILITY::CheckIfFileExists(struHostFxrPath.QueryStr())) + { + // ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG + hr = HRESULT_FROM_WIN32(ERROR_FILE_INVALID); + if (SUCCEEDED(struEventMsg.SafeSnwprintf( + ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG, + struHostFxrPath.QueryStr(), + hr))) + { + UTILITY::LogEvent(hEventLog, + EVENTLOG_ERROR_TYPE, + ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND, + struEventMsg.QueryStr()); + } + goto Finished; + } + + if (FAILED(hr = pStruAbsolutePathToHostfxr->Copy(struHostFxrPath))) + { + goto Finished; + } + +Finished: + return hr; +} + +// +// Tries to call where.exe to find the location of dotnet.exe. +// Will check that the bitness of dotnet matches the current +// worker process bitness. +// Returns true if a valid dotnet was found, else false. +// +BOOL +HOSTFXR_UTILITY::InvokeWhereToFindDotnet( + _Inout_ STRU* pStruAbsolutePathToDotnet +) +{ + HRESULT hr = S_OK; + // Arguments to call where.exe STARTUPINFOW startupInfo = { 0 }; PROCESS_INFORMATION processInformation = { 0 }; SECURITY_ATTRIBUTES securityAttributes; - STRU struDotnetSubstring; - STRU struDotnetLocationsString; - LPWSTR pwzDotnetName = NULL; - DWORD dwExitCode; - DWORD dwNumBytesRead; - DWORD dwFilePointer; - DWORD dwBinaryType; - DWORD dwPathSize = MAX_PATH; - INT index = 0; - INT prevIndex = 0; - BOOL fResult = FALSE; - BOOL fIsWow64Process; - BOOL fIsCurrentProcess64Bit; - BOOL fFound = FALSE; + CHAR pzFileContents[READ_BUFFER_SIZE]; HANDLE hStdOutReadPipe = INVALID_HANDLE_VALUE; HANDLE hStdOutWritePipe = INVALID_HANDLE_VALUE; + LPWSTR pwzDotnetName = NULL; + DWORD dwFilePointer; + BOOL fIsWow64Process; + BOOL fIsCurrentProcess64Bit; + DWORD dwExitCode; + STRU struDotnetSubstring; + STRU struDotnetLocationsString; + DWORD dwNumBytesRead; + DWORD dwBinaryType; + INT index = 0; + INT prevIndex = 0; + BOOL fProcessCreationResult = FALSE; + BOOL fResult = FALSE; + // Set the security attributes for the read/write pipe securityAttributes.nLength = sizeof(securityAttributes); securityAttributes.lpSecurityDescriptor = NULL; securityAttributes.bInheritHandle = TRUE; + // Reset the path to dotnet as we will be using whether the string is + // empty or not as state + pStruAbsolutePathToDotnet->Reset(); + + // Create a read/write pipe that will be used for reading the result of where.exe if (!CreatePipe(&hStdOutReadPipe, &hStdOutWritePipe, &securityAttributes, 0)) { hr = HRESULT_FROM_WIN32(GetLastError()); @@ -503,7 +612,7 @@ HOSTFXR_UTILITY::FindDotnetExePath( goto Finished; } - // Set stdout and error to redirect to the temp file. + // Set the stdout and err pipe to the write pipes. startupInfo.cb = sizeof(startupInfo); startupInfo.dwFlags |= STARTF_USESTDHANDLES; startupInfo.hStdOutput = hStdOutWritePipe; @@ -511,14 +620,14 @@ HOSTFXR_UTILITY::FindDotnetExePath( // CreateProcess requires a mutable string to be passed to commandline // See https://blogs.msdn.microsoft.com/oldnewthing/20090601-00/?p=18083/ - pwzDotnetName = SysAllocString(L"\"where.exe\" dotnet.exe"); if (pwzDotnetName == NULL) { - hr = E_OUTOFMEMORY; goto Finished; } - fResult = CreateProcessW(NULL, + + // Create a process to invoke where.exe + fProcessCreationResult = CreateProcessW(NULL, pwzDotnetName, NULL, NULL, @@ -530,14 +639,15 @@ HOSTFXR_UTILITY::FindDotnetExePath( &processInformation ); - if (!fResult) + if (!fProcessCreationResult) { - hr = HRESULT_FROM_WIN32(GetLastError()); goto Finished; } - if (WaitForSingleObject(processInformation.hProcess, 2000) != WAIT_OBJECT_0) // 2 seconds + // Wait for where.exe to return, waiting 2 seconds. + if (WaitForSingleObject(processInformation.hProcess, 2000) != WAIT_OBJECT_0) { + // Timeout occured, terminate the where.exe process and return. TerminateProcess(processInformation.hProcess, 2); hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); goto Finished; @@ -550,131 +660,95 @@ HOSTFXR_UTILITY::FindDotnetExePath( // if (!GetExitCodeProcess(processInformation.hProcess, &dwExitCode)) { - goto Fallback; + goto Finished; } // // In this block, if anything fails, we will goto our fallback of // looking in C:/Program Files/ // - if (dwExitCode == 0) + if (dwExitCode != 0) { - // Where succeeded. - // Reset file pointer to the beginning of the file. - dwFilePointer = SetFilePointer(hStdOutReadPipe, 0, NULL, FILE_BEGIN); - if (dwFilePointer == INVALID_SET_FILE_POINTER) - { - goto Fallback; - } - - // - // As the call to where.exe succeeded (dotnet.exe was found), ReadFile should not hang. - // TODO consider putting ReadFile in a separate thread with a timeout to guarantee it doesn't block. - // - if (!ReadFile(hStdOutReadPipe, pzFileContents, READ_BUFFER_SIZE, &dwNumBytesRead, NULL)) - { - goto Fallback; - } - if (dwNumBytesRead >= READ_BUFFER_SIZE) - { - // This shouldn't ever be this large. We could continue to call ReadFile in a loop, - // however I'd rather error out here and report an issue. - goto Fallback; - } - - if (FAILED(hr = struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead))) - { - goto Finished; - } - - // Check the bitness of the currently running process - // matches the dotnet.exe found. - if (!IsWow64Process(GetCurrentProcess(), &fIsWow64Process)) - { - // Calling IsWow64Process failed - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } - if (fIsWow64Process) - { - // 32 bit mode - fIsCurrentProcess64Bit = FALSE; - } - else - { - SYSTEM_INFO systemInfo; - GetNativeSystemInfo(&systemInfo); - fIsCurrentProcess64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; - } - - while (!fFound) - { - index = struDotnetLocationsString.IndexOf(L"\r\n", prevIndex); - if (index == -1) - { - break; - } - if (FAILED(hr = struDotnetSubstring.Copy(&struDotnetLocationsString.QueryStr()[prevIndex], index - prevIndex))) - { - goto Finished; - } - // \r\n is two wchars, so add 2 here. - prevIndex = index + 2; - - if (GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType) && - fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY)) { - // Found a valid dotnet. - if (FAILED(hr = struDotnetPath->Copy(struDotnetSubstring))) - { - goto Finished; - } - fFound = TRUE; - } - } + goto Finished; } -Fallback: - - // Look in ProgramFiles - while (!fFound) + // Where succeeded. + // Reset file pointer to the beginning of the file. + dwFilePointer = SetFilePointer(hStdOutReadPipe, 0, NULL, FILE_BEGIN); + if (dwFilePointer == INVALID_SET_FILE_POINTER) { - if (FAILED(hr = struDotnetSubstring.Resize(dwPathSize))) - { - goto Finished; - } - - // Program files will changes based on the - dwNumBytesRead = GetEnvironmentVariable(L"ProgramFiles", struDotnetSubstring.QueryStr(), dwPathSize); - if (dwNumBytesRead == 0) - { - hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; - } - else if (dwNumBytesRead == dwPathSize) - { - dwPathSize *= 2 + 30; // for dotnet substring - } - else - { - if (FAILED(hr = struDotnetSubstring.SyncWithBuffer()) || - FAILED(hr = struDotnetSubstring.Append(L"\\dotnet\\dotnet.exe"))) - { - goto Finished; - } - if (!UTILITY::CheckIfFileExists(struDotnetSubstring.QueryStr())) - { - hr = HRESULT_FROM_WIN32( GetLastError() ); - goto Finished; - } - if (FAILED(hr = struDotnetPath->Copy(struDotnetSubstring))) - { - goto Finished; - } - fFound = TRUE; - } + goto Finished; } + // + // As the call to where.exe succeeded (dotnet.exe was found), ReadFile should not hang. + // TODO consider putting ReadFile in a separate thread with a timeout to guarantee it doesn't block. + // + if (!ReadFile(hStdOutReadPipe, pzFileContents, READ_BUFFER_SIZE, &dwNumBytesRead, NULL)) + { + goto Finished; + } + if (dwNumBytesRead >= READ_BUFFER_SIZE) + { + // This shouldn't ever be this large. We could continue to call ReadFile in a loop, + // however if someone had this many dotnet.exes on their machine. + goto Finished; + } + + hr = HRESULT_FROM_WIN32(GetLastError()); + if (FAILED(hr = struDotnetLocationsString.CopyA(pzFileContents, dwNumBytesRead))) + { + goto Finished; + } + + // Check the bitness of the currently running process + // matches the dotnet.exe found. + if (!IsWow64Process(GetCurrentProcess(), &fIsWow64Process)) + { + // Calling IsWow64Process failed + goto Finished; + } + if (fIsWow64Process) + { + // 32 bit mode + fIsCurrentProcess64Bit = FALSE; + } + else + { + // Check the SystemInfo to see if we are currently 32 or 64 bit. + SYSTEM_INFO systemInfo; + GetNativeSystemInfo(&systemInfo); + fIsCurrentProcess64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; + } + + while (TRUE) + { + index = struDotnetLocationsString.IndexOf(L"\r\n", prevIndex); + if (index == -1) + { + break; + } + if (FAILED(hr = struDotnetSubstring.Copy(&struDotnetLocationsString.QueryStr()[prevIndex], index - prevIndex))) + { + goto Finished; + } + // \r\n is two wchars, so add 2 here. + prevIndex = index + 2; + + if (GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType) && + fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY)) + { + // The bitness of dotnet matched with the current worker process bitness. + if (FAILED(hr = pStruAbsolutePathToDotnet->Copy(struDotnetSubstring))) + { + goto Finished; + } + fResult = TRUE; + break; + } + } + Finished: if (hStdOutReadPipe != INVALID_HANDLE_VALUE) @@ -698,5 +772,60 @@ Finished: SysFreeString(pwzDotnetName); } + return fResult; +} + + +HRESULT +HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles( + _Inout_ STRU* pStruAbsolutePathToDotnet +) +{ + HRESULT hr = S_OK; + BOOL fFound = FALSE; + DWORD dwNumBytesRead = 0; + DWORD dwPathSize = MAX_PATH; + STRU struDotnetSubstring; + + while (!fFound) + { + if (FAILED(hr = struDotnetSubstring.Resize(dwPathSize))) + { + goto Finished; + } + + dwNumBytesRead = GetEnvironmentVariable(L"ProgramFiles", struDotnetSubstring.QueryStr(), dwPathSize); + if (dwNumBytesRead == 0) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Finished; + } + else if (dwNumBytesRead >= dwPathSize) + { + // + // The path to ProgramFiles should never be this long, but resize and try again. + dwPathSize *= 2 + 30; // for dotnet substring + } + else + { + if (FAILED(hr = struDotnetSubstring.SyncWithBuffer()) || + FAILED(hr = struDotnetSubstring.Append(L"\\dotnet\\dotnet.exe"))) + { + goto Finished; + } + if (!UTILITY::CheckIfFileExists(struDotnetSubstring.QueryStr())) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Finished; + } + if (FAILED(hr = pStruAbsolutePathToDotnet->Copy(struDotnetSubstring))) + { + goto Finished; + } + fFound = TRUE; + } + } + +Finished: return hr; } diff --git a/src/CommonLib/hostfxr_utility.h b/src/CommonLib/hostfxr_utility.h index 2effba31f1..e7703c5bfa 100644 --- a/src/CommonLib/hostfxr_utility.h +++ b/src/CommonLib/hostfxr_utility.h @@ -21,7 +21,7 @@ public: PCWSTR pcwzProcessPath, PCWSTR pcwzApplicationPhysicalPath, PCWSTR pcwzArguments, - _Inout_ STRU* struHostFxrDllLocation, + _Inout_ STRU* pStruHostFxrDllLocation, _Out_ DWORD* pdwArgCount, _Out_ BSTR** ppwzArgv ); @@ -33,7 +33,7 @@ public: PCWSTR pcwzApplicationPhysicalPath, PCWSTR pcwzArguments, HANDLE hEventLog, - _Inout_ STRU* struHostFxrDllLocation, + _Inout_ STRU* pStruHostFxrDllLocation, _Out_ DWORD* pdwArgCount, _Out_ BSTR** ppwzArgv ); @@ -46,13 +46,33 @@ public: PCWSTR pcwzApplicationPhysicalPath, HANDLE hEventLog, _Out_ DWORD* pdwArgCount, - _Out_ BSTR** ppwzArgv + _Out_ BSTR** ppwzArgv ); static HRESULT - FindDotnetExePath( - STRU* struDotnetLocation + GetAbsolutePathToDotnet( + STRU* pStruAbsolutePathToDotnet + ); + + static + HRESULT + GetAbsolutePathToHostFxr( + _In_ STRU* pStruAbsolutePathToDotnet, + _In_ HANDLE hEventLog, + _Out_ STRU* pStruAbsolutePathToHostfxr + ); + + static + BOOL + InvokeWhereToFindDotnet( + _Inout_ STRU* pStruAbsolutePathToDotnet + ); + + static + HRESULT + GetAbsolutePathToDotnetFromProgramFiles( + _Inout_ STRU* pStruAbsolutePathToDotnet ); }; diff --git a/src/CommonLib/resources.h b/src/CommonLib/resources.h index adba68b794..63a9cf301e 100644 --- a/src/CommonLib/resources.h +++ b/src/CommonLib/resources.h @@ -41,3 +41,4 @@ #define ASPNETCORE_EVENT_HOSTFXR_DLL_NOT_FOUND_MSG L"Could not find hostfxr.dll in '%s'. ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_APPLICATION_EXE_NOT_FOUND_MSG L"Could not find application executable in '%s'. ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_INPROCESS_THREAD_EXCEPTION_MSG L"Application '%s' with physical root '%s' hit unexpected managed exception, ErrorCode = '0x%x. Please check the stderr logs for more information." +#define ASPNETCORE_EVENT_INVALID_PROCESS_PATH_MSG L"Invalid or unknown processPath provided in web.config: processPath = %s, ErrorCode = '0x%x'." diff --git a/src/CommonLib/utility.h b/src/CommonLib/utility.h index 49549c0f33..891f0f0092 100644 --- a/src/CommonLib/utility.h +++ b/src/CommonLib/utility.h @@ -118,7 +118,7 @@ public: _In_ WORD dwEventInfoType, _In_ DWORD dwEventId, _In_ LPCWSTR pstrMsg - ); + ); private: diff --git a/src/IISLib/util.cxx b/src/IISLib/util.cxx index bde325025e..214ee65abf 100644 --- a/src/IISLib/util.cxx +++ b/src/IISLib/util.cxx @@ -61,13 +61,17 @@ Return Values: return hr; } } - else + else if (wcslen(pszName) > MAX_PATH) { if (FAILED(hr = pstrPath->Copy(L"\\\\?\\"))) { return hr; } } + else + { + pstrPath->Reset(); + } return pstrPath->Append(pszName); } diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/NativeMethods.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/NativeMethods.cs index 00c9d5beb1..4f8c2ce10e 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/NativeMethods.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/NativeMethods.cs @@ -7,24 +7,28 @@ using Microsoft.AspNetCore.HttpSys.Internal; namespace Microsoft.AspNetCore.Server.IISIntegration { - internal class NativeMethods + internal static class NativeMethods { -#if DOTNET5_4 - private const string api_ms_win_core_handle_LIB = "api-ms-win-core-handle-l1-1-0.dll"; -#else + private const int HR_NOT_FOUND = unchecked((int)0x80070490); + private const int HR_OK = 0; + private const string KERNEL32 = "kernel32.dll"; -#endif -#if DOTNET5_4 - [DllImport(api_ms_win_core_handle_LIB, ExactSpelling = true, SetLastError = true)] -#else - [DllImport(KERNEL32, ExactSpelling = true, SetLastError = true)] -#endif - internal static extern bool CloseHandle(IntPtr handle); - - public const int S_OK = 0; private const string AspNetCoreModuleDll = "aspnetcorerh.dll"; + [DllImport(KERNEL32, ExactSpelling = true, SetLastError = true)] + + public static extern bool CloseHandle(IntPtr handle); + + + [DllImport("kernel32.dll")] + private static extern IntPtr GetModuleHandle(string lpModuleName); + + public static bool IsAspNetCoreModuleLoaded() + { + return GetModuleHandle(AspNetCoreModuleDll) != IntPtr.Zero; + } + public enum REQUEST_NOTIFICATION_STATUS { RQ_NOTIFICATION_CONTINUE, @@ -37,85 +41,232 @@ namespace Microsoft.AspNetCore.Server.IISIntegration public delegate REQUEST_NOTIFICATION_STATUS PFN_ASYNC_COMPLETION(IntPtr pvManagedHttpContext, int hr, int bytes); public delegate REQUEST_NOTIFICATION_STATUS PFN_WEBSOCKET_ASYNC_COMPLETION(IntPtr pInProcessHandler, IntPtr completionInfo, IntPtr pvCompletionContext); - // TODO make this all internal [DllImport(AspNetCoreModuleDll)] - public static extern int http_post_completion(IntPtr pInProcessHandler, int cbBytes); + private static extern int http_post_completion(IntPtr pInProcessHandler, int cbBytes); [DllImport(AspNetCoreModuleDll)] - public static extern int http_set_completion_status(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS rquestNotificationStatus); + private static extern int http_set_completion_status(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS rquestNotificationStatus); [DllImport(AspNetCoreModuleDll)] - public static extern void http_indicate_completion(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS notificationStatus); + private static extern void http_indicate_completion(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS notificationStatus); [DllImport(AspNetCoreModuleDll)] - public static extern void register_callbacks(PFN_REQUEST_HANDLER request_callback, PFN_SHUTDOWN_HANDLER shutdown_callback, PFN_ASYNC_COMPLETION managed_context_handler, IntPtr pvRequestContext, IntPtr pvShutdownContext); + private static extern void register_callbacks(PFN_REQUEST_HANDLER request_callback, PFN_SHUTDOWN_HANDLER shutdown_callback, PFN_ASYNC_COMPLETION managed_context_handler, IntPtr pvRequestContext, IntPtr pvShutdownContext); [DllImport(AspNetCoreModuleDll)] - internal unsafe static extern int http_write_response_bytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected); + private static extern unsafe int http_write_response_bytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected); [DllImport(AspNetCoreModuleDll)] - public unsafe static extern int http_flush_response_bytes(IntPtr pInProcessHandler, out bool fCompletionExpected); + private static extern int http_flush_response_bytes(IntPtr pInProcessHandler, out bool fCompletionExpected); [DllImport(AspNetCoreModuleDll)] - internal unsafe static extern HttpApiTypes.HTTP_REQUEST_V2* http_get_raw_request(IntPtr pInProcessHandler); + private static extern unsafe HttpApiTypes.HTTP_REQUEST_V2* http_get_raw_request(IntPtr pInProcessHandler); [DllImport(AspNetCoreModuleDll)] - internal unsafe static extern void http_stop_calls_into_managed(); + private static extern void http_stop_calls_into_managed(); [DllImport(AspNetCoreModuleDll)] - internal unsafe static extern void http_stop_incoming_requests(); + private static extern void http_stop_incoming_requests(); [DllImport(AspNetCoreModuleDll)] - internal unsafe static extern HttpApiTypes.HTTP_RESPONSE_V2* http_get_raw_response(IntPtr pInProcessHandler); + private static extern unsafe HttpApiTypes.HTTP_RESPONSE_V2* http_get_raw_response(IntPtr pInProcessHandler); [DllImport(AspNetCoreModuleDll, CharSet = CharSet.Ansi)] - public unsafe static extern int http_set_response_status_code(IntPtr pInProcessHandler, ushort statusCode, string pszReason); + private static extern int http_set_response_status_code(IntPtr pInProcessHandler, ushort statusCode, string pszReason); [DllImport(AspNetCoreModuleDll)] - public unsafe static extern int http_read_request_bytes(IntPtr pInProcessHandler, byte* pvBuffer, int cbBuffer, out int dwBytesReceived, out bool fCompletionExpected); + private static extern unsafe int http_read_request_bytes(IntPtr pInProcessHandler, byte* pvBuffer, int cbBuffer, out int dwBytesReceived, out bool fCompletionExpected); [DllImport(AspNetCoreModuleDll)] - public unsafe static extern bool http_get_completion_info(IntPtr pCompletionInfo, out int cbBytes, out int hr); + private static extern void http_get_completion_info(IntPtr pCompletionInfo, out int cbBytes, out int hr); [DllImport(AspNetCoreModuleDll)] - public unsafe static extern bool http_set_managed_context(IntPtr pInProcessHandler, IntPtr pvManagedContext); + private static extern int http_set_managed_context(IntPtr pInProcessHandler, IntPtr pvManagedContext); [DllImport(AspNetCoreModuleDll)] - public unsafe static extern int http_get_application_properties(ref IISConfigurationData iiConfigData); + private static extern int http_get_application_properties(ref IISConfigurationData iiConfigData); [DllImport(AspNetCoreModuleDll)] - public static extern int http_get_server_variable(IntPtr pInProcessHandler, [MarshalAs(UnmanagedType.AnsiBStr)] string variableName, [MarshalAs(UnmanagedType.BStr)] out string value); + private static extern int http_get_server_variable( + IntPtr pInProcessHandler, + [MarshalAs(UnmanagedType.AnsiBStr)] string variableName, + [MarshalAs(UnmanagedType.BStr)] out string value); [DllImport(AspNetCoreModuleDll)] - public unsafe static extern bool http_shutdown(); + private static extern unsafe int http_websockets_read_bytes( + IntPtr pInProcessHandler, + byte* pvBuffer, + int cbBuffer, + PFN_WEBSOCKET_ASYNC_COMPLETION pfnCompletionCallback, + IntPtr pvCompletionContext, + out int dwBytesReceived, + out bool fCompletionExpected); [DllImport(AspNetCoreModuleDll)] - public unsafe static extern int http_websockets_read_bytes(IntPtr pInProcessHandler, byte* pvBuffer, int cbBuffer, PFN_WEBSOCKET_ASYNC_COMPLETION pfnCompletionCallback, IntPtr pvCompletionContext, out int dwBytesReceived, out bool fCompletionExpected); + private static extern unsafe int http_websockets_write_bytes( + IntPtr pInProcessHandler, + HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, + int nChunks, + PFN_WEBSOCKET_ASYNC_COMPLETION pfnCompletionCallback, + IntPtr pvCompletionContext, + out bool fCompletionExpected); [DllImport(AspNetCoreModuleDll)] - internal unsafe static extern int http_websockets_write_bytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, PFN_WEBSOCKET_ASYNC_COMPLETION pfnCompletionCallback, IntPtr pvCompletionContext, out bool fCompletionExpected); + private static extern int http_enable_websockets(IntPtr pInProcessHandler); [DllImport(AspNetCoreModuleDll)] - public unsafe static extern int http_enable_websockets(IntPtr pInProcessHandler); + private static extern int http_cancel_io(IntPtr pInProcessHandler); [DllImport(AspNetCoreModuleDll)] - public unsafe static extern int http_cancel_io(IntPtr pInProcessHandler); + private static extern unsafe int http_response_set_unknown_header(IntPtr pInProcessHandler, byte* pszHeaderName, byte* pszHeaderValue, ushort usHeaderValueLength, bool fReplace); [DllImport(AspNetCoreModuleDll)] - public unsafe static extern int http_response_set_unknown_header(IntPtr pInProcessHandler, byte* pszHeaderName, byte* pszHeaderValue, ushort usHeaderValueLength, bool fReplace); + private static extern unsafe int http_response_set_known_header(IntPtr pInProcessHandler, int headerId, byte* pHeaderValue, ushort length, bool fReplace); [DllImport(AspNetCoreModuleDll)] - internal unsafe static extern int http_response_set_known_header(IntPtr pInProcessHandler, int headerId, byte* pHeaderValue, ushort length, bool fReplace); + private static extern int http_get_authentication_information(IntPtr pInProcessHandler, [MarshalAs(UnmanagedType.BStr)] out string authType, out IntPtr token); - [DllImport(AspNetCoreModuleDll)] - public unsafe static extern int http_get_authentication_information(IntPtr pInProcessHandler, [MarshalAs(UnmanagedType.BStr)] out string authType, out IntPtr token); - - [DllImport("kernel32.dll")] - public static extern IntPtr GetModuleHandle(string lpModuleName); - - public static bool is_ancm_loaded() + public static void HttpPostCompletion(IntPtr pInProcessHandler, int cbBytes) { - return GetModuleHandle(AspNetCoreModuleDll) != IntPtr.Zero; + Validate(http_post_completion(pInProcessHandler, cbBytes)); + } + + public static void HttpSetCompletionStatus(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS rquestNotificationStatus) + { + Validate(http_set_completion_status(pInProcessHandler, rquestNotificationStatus)); + } + + public static void HttpIndicateCompletion(IntPtr pInProcessHandler, REQUEST_NOTIFICATION_STATUS notificationStatus) + { + http_indicate_completion(pInProcessHandler, notificationStatus); + } + public static void HttpRegisterCallbacks(PFN_REQUEST_HANDLER request_callback, PFN_SHUTDOWN_HANDLER shutdown_callback, PFN_ASYNC_COMPLETION managed_context_handler, IntPtr pvRequestContext, IntPtr pvShutdownContext) + { + register_callbacks(request_callback, shutdown_callback, managed_context_handler, pvRequestContext, pvShutdownContext); + } + + public static unsafe int HttpWriteResponseBytes(IntPtr pInProcessHandler, HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, int nChunks, out bool fCompletionExpected) + { + return http_write_response_bytes(pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected); + } + + public static int HttpFlushResponseBytes(IntPtr pInProcessHandler, out bool fCompletionExpected) + { + return http_flush_response_bytes(pInProcessHandler, out fCompletionExpected); + } + public static unsafe HttpApiTypes.HTTP_REQUEST_V2* HttpGetRawRequest(IntPtr pInProcessHandler) + { + return http_get_raw_request(pInProcessHandler); + } + + public static void HttpStopCallsIntoManaged() + { + http_stop_calls_into_managed(); + } + + public static void HttpStopIncomingRequests() + { + http_stop_incoming_requests(); + } + + public static unsafe HttpApiTypes.HTTP_RESPONSE_V2* HttpGetRawResponse(IntPtr pInProcessHandler) + { + return http_get_raw_response(pInProcessHandler); + } + + public static void HttpSetResponseStatusCode(IntPtr pInProcessHandler, ushort statusCode, string pszReason) + { + Validate(http_set_response_status_code(pInProcessHandler, statusCode, pszReason)); + } + + public static unsafe int HttpReadRequestBytes(IntPtr pInProcessHandler, byte* pvBuffer, int cbBuffer, out int dwBytesReceived, out bool fCompletionExpected) + { + return http_read_request_bytes(pInProcessHandler, pvBuffer, cbBuffer, out dwBytesReceived, out fCompletionExpected); + } + + public static void HttpGetCompletionInfo(IntPtr pCompletionInfo, out int cbBytes, out int hr) + { + http_get_completion_info(pCompletionInfo, out cbBytes, out hr); + } + + public static void HttpSetManagedContext(IntPtr pInProcessHandler, IntPtr pvManagedContext) + { + Validate(http_set_managed_context(pInProcessHandler, pvManagedContext)); + } + + public static IISConfigurationData HttpGetApplicationProperties() + { + var iisConfigurationData = new IISConfigurationData(); + Validate(http_get_application_properties(ref iisConfigurationData)); + return iisConfigurationData; + } + + public static bool HttpTryGetServerVariable(IntPtr pInProcessHandler, string variableName, out string value) + { + return http_get_server_variable(pInProcessHandler, variableName, out value) == 0; + } + + public static unsafe int HttpWebsocketsReadBytes( + IntPtr pInProcessHandler, + byte* pvBuffer, + int cbBuffer, + PFN_WEBSOCKET_ASYNC_COMPLETION pfnCompletionCallback, + IntPtr pvCompletionContext, out int dwBytesReceived, + out bool fCompletionExpected) + { + return http_websockets_read_bytes(pInProcessHandler, pvBuffer, cbBuffer, pfnCompletionCallback, pvCompletionContext, out dwBytesReceived, out fCompletionExpected); + } + + public static unsafe int HttpWebsocketsWriteBytes( + IntPtr pInProcessHandler, + HttpApiTypes.HTTP_DATA_CHUNK* pDataChunks, + int nChunks, + PFN_WEBSOCKET_ASYNC_COMPLETION pfnCompletionCallback, + IntPtr pvCompletionContext, + out bool fCompletionExpected) + { + return http_websockets_write_bytes(pInProcessHandler, pDataChunks, nChunks, pfnCompletionCallback, pvCompletionContext, out fCompletionExpected); + } + + public static void HttpEnableWebsockets(IntPtr pInProcessHandler) + { + Validate(http_enable_websockets(pInProcessHandler)); + } + + public static bool HttpTryCancelIO(IntPtr pInProcessHandler) + { + var hr = http_cancel_io(pInProcessHandler); + // Async operation finished + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363792(v=vs.85).aspx + if (hr == HR_NOT_FOUND) + { + return false; + } + Validate(hr); + return true; + } + + public static unsafe void HttpResponseSetUnknownHeader(IntPtr pInProcessHandler, byte* pszHeaderName, byte* pszHeaderValue, ushort usHeaderValueLength, bool fReplace) + { + Validate(http_response_set_unknown_header(pInProcessHandler, pszHeaderName, pszHeaderValue, usHeaderValueLength, fReplace)); + } + + public static unsafe void HttpResponseSetKnownHeader(IntPtr pInProcessHandler, int headerId, byte* pHeaderValue, ushort length, bool fReplace) + { + Validate(http_response_set_known_header(pInProcessHandler, headerId, pHeaderValue, length, fReplace)); + } + + public static void HttpGetAuthenticationInformation(IntPtr pInProcessHandler, out string authType, out IntPtr token) + { + Validate(http_get_authentication_information(pInProcessHandler, out authType, out token)); + } + + private static void Validate(int hr) + { + if (hr != HR_OK) + { + throw Marshal.GetExceptionForHR(hr); + } } } } diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISAwaitable.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISAwaitable.cs index 28cc3672d5..a24586b137 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISAwaitable.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISAwaitable.cs @@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { var context = (IISHttpContext)GCHandle.FromIntPtr(pvCompletionContext).Target; - NativeMethods.http_get_completion_info(pCompletionInfo, out int cbBytes, out int hr); + NativeMethods.HttpGetCompletionInfo(pCompletionInfo, out int cbBytes, out int hr); context.CompleteReadWebSockets(hr, cbBytes); @@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { var context = (IISHttpContext)GCHandle.FromIntPtr(pvCompletionContext).Target; - NativeMethods.http_get_completion_info(pCompletionInfo, out int cbBytes, out int hr); + NativeMethods.HttpGetCompletionInfo(pCompletionInfo, out int cbBytes, out int hr); context.CompleteWriteWebSockets(hr, cbBytes); diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.FeatureCollection.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.FeatureCollection.cs index 4a1ecd5f27..f43c88a319 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.FeatureCollection.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.FeatureCollection.cs @@ -243,8 +243,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration return null; } - int hr = NativeMethods.http_get_server_variable(_pInProcessHandler, variableName, out var value); - return hr == 0 ? value : null; + return NativeMethods.HttpTryGetServerVariable(_pInProcessHandler, variableName, out var value) ? value : null; } } @@ -296,7 +295,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration ReasonPhrase = ReasonPhrases.GetReasonPhrase(StatusCodes.Status101SwitchingProtocols); _readWebSocketsOperation = new IISAwaitable(); _writeWebSocketsOperation = new IISAwaitable(); - NativeMethods.http_enable_websockets(_pInProcessHandler); + NativeMethods.HttpEnableWebsockets(_pInProcessHandler); // Upgrade async will cause the stream processing to go into duplex mode await UpgradeAsync(); diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs index 699081e659..7b5f18efce 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.ReadWrite.cs @@ -169,7 +169,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration } else { - var hr = NativeMethods.http_read_request_bytes( + var hr = NativeMethods.HttpReadRequestBytes( _pInProcessHandler, (byte*)_inputHandle.Pointer, length, @@ -225,7 +225,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; chunk.fromMemory.pBuffer = (IntPtr)pBuffer; chunk.fromMemory.BufferLength = (uint)buffer.Length; - hr = NativeMethods.http_write_response_bytes(_pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected); + hr = NativeMethods.HttpWriteResponseBytes(_pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected); } } else if (nChunks < HttpDataChunkStackLimit) @@ -274,7 +274,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration currentChunk++; } - hr = NativeMethods.http_write_response_bytes(_pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected); + hr = NativeMethods.HttpWriteResponseBytes(_pInProcessHandler, pDataChunks, nChunks, out fCompletionExpected); // Free the handles foreach (var handle in handles) @@ -289,7 +289,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { // Calls flush var hr = 0; - hr = NativeMethods.http_flush_response_bytes(_pInProcessHandler, out var fCompletionExpected); + hr = NativeMethods.HttpFlushResponseBytes(_pInProcessHandler, out var fCompletionExpected); if (!fCompletionExpected) { _operation.Complete(hr, 0); @@ -468,7 +468,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { _reading = false; // Calls IHttpContext->CancelIo(), which will cause the OnAsyncCompletion handler to fire. - NativeMethods.http_cancel_io(_pInProcessHandler); + NativeMethods.HttpTryCancelIO(_pInProcessHandler); } } } diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.Websockets.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.Websockets.cs index fa84c3ce11..6ddbdef865 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.Websockets.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.Websockets.cs @@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration bool fCompletionExpected; // For websocket calls, we can directly provide a callback function to be called once the websocket operation completes. - hr = NativeMethods.http_websockets_read_bytes( + hr = NativeMethods.HttpWebsocketsReadBytes( _pInProcessHandler, (byte*)_inputHandle.Pointer, length, @@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration chunk.DataChunkType = HttpApiTypes.HTTP_DATA_CHUNK_TYPE.HttpDataChunkFromMemory; chunk.fromMemory.pBuffer = (IntPtr)pBuffer; chunk.fromMemory.BufferLength = (uint)buffer.Length; - hr = NativeMethods.http_websockets_write_bytes(_pInProcessHandler, pDataChunks, nChunks, IISAwaitable.WriteCallback, (IntPtr)_thisHandle, out fCompletionExpected); + hr = NativeMethods.HttpWebsocketsWriteBytes(_pInProcessHandler, pDataChunks, nChunks, IISAwaitable.WriteCallback, (IntPtr)_thisHandle, out fCompletionExpected); } } else @@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration currentChunk++; } - hr = NativeMethods.http_websockets_write_bytes(_pInProcessHandler, pDataChunks, nChunks, IISAwaitable.WriteCallback, (IntPtr)_thisHandle, out fCompletionExpected); + hr = NativeMethods.HttpWebsocketsWriteBytes(_pInProcessHandler, pDataChunks, nChunks, IISAwaitable.WriteCallback, (IntPtr)_thisHandle, out fCompletionExpected); foreach (var handle in handles) { diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs index 32afb07033..8ca003ed7f 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpContext.cs @@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration private const string WebSocketVersionString = "WEBSOCKET_VERSION"; internal unsafe IISHttpContext(MemoryPool memoryPool, IntPtr pInProcessHandler, IISOptions options, IISHttpServer server) - : base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.http_get_raw_request(pInProcessHandler)) + : base((HttpApiTypes.HTTP_REQUEST*)NativeMethods.HttpGetRawRequest(pInProcessHandler)) { _thisHandle = GCHandle.Alloc(this); @@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration _pInProcessHandler = pInProcessHandler; _server = server; - NativeMethods.http_set_managed_context(pInProcessHandler, (IntPtr)_thisHandle); + NativeMethods.HttpSetManagedContext(pInProcessHandler, (IntPtr)_thisHandle); unsafe { Method = GetVerb(); @@ -145,8 +145,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration // server variables a few extra times if a bunch of requests hit the server at the same time. if (_websocketAvailability == WebsocketAvailabilityStatus.Uninitialized) { - NativeMethods.http_get_server_variable(pInProcessHandler, WebSocketVersionString, out var webSocketsSupported); - var webSocketsAvailable = !string.IsNullOrEmpty(webSocketsSupported); + var webSocketsAvailable = NativeMethods.HttpTryGetServerVariable(pInProcessHandler, WebSocketVersionString, out var webSocketsSupported) + && !string.IsNullOrEmpty(webSocketsSupported); + _websocketAvailability = webSocketsAvailable ? WebsocketAvailabilityStatus.Available : WebsocketAvailabilityStatus.NotAvailable; @@ -335,7 +336,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration var reasonPhrase = string.IsNullOrEmpty(ReasonPhrase) ? ReasonPhrases.GetReasonPhrase(StatusCode) : ReasonPhrase; // This copies data into the underlying buffer - NativeMethods.http_set_response_status_code(_pInProcessHandler, (ushort)StatusCode, reasonPhrase); + NativeMethods.HttpSetResponseStatusCode(_pInProcessHandler, (ushort)StatusCode, reasonPhrase); HttpResponseHeaders.IsReadOnly = true; foreach (var headerPair in HttpResponseHeaders) @@ -352,7 +353,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { fixed (byte* pHeaderValue = headerValueBytes) { - NativeMethods.http_response_set_unknown_header(_pInProcessHandler, pHeaderName, pHeaderValue, (ushort)headerValueBytes.Length, fReplace: false); + NativeMethods.HttpResponseSetUnknownHeader(_pInProcessHandler, pHeaderName, pHeaderValue, (ushort)headerValueBytes.Length, fReplace: false); } } } @@ -364,7 +365,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration var headerValueBytes = Encoding.UTF8.GetBytes(headerValues[i]); fixed (byte* pHeaderValue = headerValueBytes) { - NativeMethods.http_response_set_known_header(_pInProcessHandler, knownHeaderIndex, pHeaderValue, (ushort)headerValueBytes.Length, fReplace: false); + NativeMethods.HttpResponseSetKnownHeader(_pInProcessHandler, knownHeaderIndex, pHeaderValue, (ushort)headerValueBytes.Length, fReplace: false); } } } @@ -475,22 +476,13 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { Debug.Assert(!_operation.HasContinuation, "Pending async operation!"); - var hr = NativeMethods.http_set_completion_status(_pInProcessHandler, requestNotificationStatus); - if (hr != NativeMethods.S_OK) - { - throw Marshal.GetExceptionForHR(hr); - } - - hr = NativeMethods.http_post_completion(_pInProcessHandler, 0); - if (hr != NativeMethods.S_OK) - { - throw Marshal.GetExceptionForHR(hr); - } + NativeMethods.HttpSetCompletionStatus(_pInProcessHandler, requestNotificationStatus); + NativeMethods.HttpPostCompletion(_pInProcessHandler, 0); } public void IndicateCompletion(NativeMethods.REQUEST_NOTIFICATION_STATUS notificationStatus) { - NativeMethods.http_indicate_completion(_pInProcessHandler, notificationStatus); + NativeMethods.HttpIndicateCompletion(_pInProcessHandler, notificationStatus); } internal void OnAsyncCompletion(int hr, int cbBytes) @@ -540,9 +532,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration private WindowsPrincipal GetWindowsPrincipal() { - var hr = NativeMethods.http_get_authentication_information(_pInProcessHandler, out var authenticationType, out var token); + NativeMethods.HttpGetAuthenticationInformation(_pInProcessHandler, out var authenticationType, out var token); - if (hr == 0 && token != IntPtr.Zero && authenticationType != null) + if (token != IntPtr.Zero && authenticationType != null) { if ((authenticationType.Equals(NtlmString, StringComparison.OrdinalIgnoreCase) || authenticationType.Equals(NegotiateString, StringComparison.OrdinalIgnoreCase) diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpServer.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpServer.cs index 110b38e729..046d8b9c4f 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpServer.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISHttpServer.cs @@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration _iisContextFactory = new IISContextFactory(_memoryPool, application, _options, this); // Start the server by registering the callback - NativeMethods.register_callbacks(_requestHandler, _shutdownHandler, _onAsyncCompletion, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle); + NativeMethods.HttpRegisterCallbacks(_requestHandler, _shutdownHandler, _onAsyncCompletion, (IntPtr)_httpServerHandle, (IntPtr)_httpServerHandle); return Task.CompletedTask; } @@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { cancellationToken.Register(() => { - NativeMethods.http_stop_calls_into_managed(); + NativeMethods.HttpStopCallsIntoManaged(); _shutdownSignal.TrySetResult(null); }); } @@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration } // First call back into native saying "DON'T SEND ME ANY MORE REQUESTS" - NativeMethods.http_stop_incoming_requests(); + NativeMethods.HttpStopIncomingRequests(); try { @@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration else { // We have drained all requests. Block any callbacks into managed at this point. - NativeMethods.http_stop_calls_into_managed(); + NativeMethods.HttpStopCallsIntoManaged(); _shutdownSignal.TrySetResult(null); } } @@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration _stopping = 1; // Block any more calls into managed from native as we are unloading. - NativeMethods.http_stop_calls_into_managed(); + NativeMethods.HttpStopCallsIntoManaged(); _shutdownSignal.TrySetResult(null); if (_httpServerHandle.IsAllocated) @@ -153,7 +153,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration if (Interlocked.Decrement(ref context.Server._outstandingRequests) == 0 && context.Server.Stopping) { // All requests have been drained. - NativeMethods.http_stop_calls_into_managed(); + NativeMethods.HttpStopCallsIntoManaged(); context.Server._shutdownSignal.TrySetResult(null); } @@ -191,7 +191,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration ~IISHttpServer() { // If this finalize is invoked, try our best to block all calls into managed. - NativeMethods.http_stop_calls_into_managed(); + NativeMethods.HttpStopCallsIntoManaged(); } } diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISServerSetupFilter.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISServerSetupFilter.cs index 94fa2ac136..787c979c96 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISServerSetupFilter.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/Server/IISServerSetupFilter.cs @@ -4,6 +4,8 @@ using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Server.IISIntegration { @@ -20,6 +22,12 @@ namespace Microsoft.AspNetCore.Server.IISIntegration { return app => { + var server = app.ApplicationServices.GetService(); + if (server?.GetType() != typeof(IISHttpServer)) + { + throw new InvalidOperationException("Application is running inside IIS process but is not configured to use IIS server."); + } + app.UsePathBase(_virtualPath); next(app); }; diff --git a/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs b/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs index a96305d4d8..c236c7eac4 100644 --- a/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs +++ b/src/Microsoft.AspNetCore.Server.IISIntegration/WebHostBuilderIISExtensions.cs @@ -41,34 +41,10 @@ namespace Microsoft.AspNetCore.Hosting } // Check if in process - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && NativeMethods.is_ancm_loaded()) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && NativeMethods.IsAspNetCoreModuleLoaded()) { - hostBuilder.UseSetting(nameof(UseIISIntegration), "true"); - hostBuilder.CaptureStartupErrors(true); - // TODO consider adding a configuration load where all variables needed are loaded from ANCM in one call. - var iisConfigData = new IISConfigurationData(); - var hResult = NativeMethods.http_get_application_properties(ref iisConfigData); - - var exception = Marshal.GetExceptionForHR(hResult); - if (exception != null) - { - throw exception; - } - - hostBuilder.UseContentRoot(iisConfigData.pwzFullApplicationPath); - return hostBuilder.ConfigureServices(services => - { - services.AddSingleton(); - services.AddSingleton(new IISServerSetupFilter(iisConfigData.pwzVirtualApplicationPath)); - services.AddAuthenticationCore(); - services.Configure( - options => - { - options.ForwardWindowsAuthentication = iisConfigData.fWindowsAuthEnabled || iisConfigData.fBasicAuthEnabled; - } - ); - }); + return SetupInProcessServer(hostBuilder); } var port = hostBuilder.GetSetting(ServerPort) ?? Environment.GetEnvironmentVariable($"ASPNETCORE_{ServerPort}"); @@ -131,5 +107,23 @@ namespace Microsoft.AspNetCore.Hosting return hostBuilder; } + + private static IWebHostBuilder SetupInProcessServer(IWebHostBuilder hostBuilder) + { + hostBuilder.UseSetting(nameof(UseIISIntegration), "true"); + hostBuilder.CaptureStartupErrors(true); + + var iisConfigData = NativeMethods.HttpGetApplicationProperties(); + hostBuilder.UseContentRoot(iisConfigData.pwzFullApplicationPath); + return hostBuilder.ConfigureServices( + services => { + services.AddSingleton(); + services.AddSingleton(new IISServerSetupFilter(iisConfigData.pwzVirtualApplicationPath)); + services.AddAuthenticationCore(); + services.Configure( + options => { options.ForwardWindowsAuthentication = iisConfigData.fWindowsAuthEnabled || iisConfigData.fBasicAuthEnabled; } + ); + }); + } } } diff --git a/src/RequestHandler/outofprocess/serverprocess.cxx b/src/RequestHandler/outofprocess/serverprocess.cxx index 03f957c17d..7aaea35a1f 100644 --- a/src/RequestHandler/outofprocess/serverprocess.cxx +++ b/src/RequestHandler/outofprocess/serverprocess.cxx @@ -30,7 +30,6 @@ SERVER_PROCESS::Initialize( ) { HRESULT hr = S_OK; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = { 0 }; m_pProcessManager = pProcessManager; m_dwStartupTimeLimitInMS = dwStartupTimeLimitInMS; @@ -47,11 +46,24 @@ SERVER_PROCESS::Initialize( FAILED(hr = m_struPhysicalPath.Copy(*pszAppPhysicalPath))|| FAILED(hr = m_struAppFullPath.Copy(*pszAppPath))|| FAILED(hr = m_struAppVirtualPath.Copy(*pszAppVirtualPath))|| - FAILED(hr = m_Arguments.Copy(*pszArguments))) + FAILED(hr = m_Arguments.Copy(*pszArguments)) || + FAILED(hr = SetupJobObject())) { goto Finished; } + m_pEnvironmentVarTable = pEnvironmentVariables; + +Finished: + return hr; +} + +HRESULT +SERVER_PROCESS::SetupJobObject(VOID) +{ + HRESULT hr = S_OK; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = { 0 }; + if (m_hJobObject == NULL) { m_hJobObject = CreateJobObject(NULL, // LPSECURITY_ATTRIBUTES @@ -75,14 +87,10 @@ SERVER_PROCESS::Initialize( sizeof jobInfo)) { hr = HRESULT_FROM_WIN32(GetLastError()); - goto Finished; } } - - m_pEnvironmentVarTable = pEnvironmentVariables; } -Finished: return hr; } @@ -130,23 +138,25 @@ SERVER_PROCESS::GetRandomPort HRESULT SERVER_PROCESS::SetupListenPort( - ENVIRONMENT_VAR_HASH *pEnvironmentVarTable + ENVIRONMENT_VAR_HASH *pEnvironmentVarTable, + BOOL* pfCriticalError ) { HRESULT hr = S_OK; ENVIRONMENT_VAR_ENTRY *pEntry = NULL; STACK_STRU(strEventMsg, 256); + *pfCriticalError = FALSE; pEnvironmentVarTable->FindKey(ASPNETCORE_PORT_ENV_STR, &pEntry); if (pEntry != NULL) { - pEntry->Dereference(); if (pEntry->QueryValue() != NULL || pEntry->QueryValue()[0] != L'\0') { m_dwPort = (DWORD)_wtoi(pEntry->QueryValue()); if (m_dwPort >MAX_PORT || m_dwPort < MIN_PORT) { hr = E_INVALIDARG; + *pfCriticalError = TRUE; goto Finished; // need add log for this one } @@ -160,6 +170,8 @@ SERVER_PROCESS::SetupListenPort( // pEnvironmentVarTable->DeleteKey(ASPNETCORE_PORT_ENV_STR); } + pEntry->Dereference(); + pEntry = NULL; } WCHAR buffer[15]; @@ -207,7 +219,7 @@ Finished: hr))) { UTILITY::LogEvent(g_hEventLog, - EVENTLOG_INFORMATION_TYPE, + EVENTLOG_ERROR_TYPE, ASPNETCORE_EVENT_PROCESS_START_SUCCESS, strEventMsg.QueryStr()); } @@ -736,7 +748,7 @@ SERVER_PROCESS::StartProcess( MULTISZ mszNewEnvironment; ENVIRONMENT_VAR_HASH *pHashTable = NULL; PWSTR pStrStage = NULL; - + BOOL fCriticalError = FALSE; GetStartupInfoW(&startupInfo); // @@ -782,7 +794,7 @@ SERVER_PROCESS::StartProcess( // // setup the the port that the backend process will listen on // - if (FAILED(hr = SetupListenPort(pHashTable))) + if (FAILED(hr = SetupListenPort(pHashTable, &fCriticalError))) { pStrStage = L"SetupListenPort"; goto Failure; @@ -840,6 +852,12 @@ SERVER_PROCESS::StartProcess( m_hProcessHandle = processInformation.hProcess; m_dwProcessId = processInformation.dwProcessId; + if (FAILED(hr = SetupJobObject())) + { + pStrStage = L"SetupJobObject"; + goto Failure; + } + if (m_hJobObject != NULL) { if (!AssignProcessToJobObject(m_hJobObject, m_hProcessHandle)) @@ -887,6 +905,12 @@ SERVER_PROCESS::StartProcess( goto Finished; Failure: + if (fCriticalError) + { + // Critical error, no retry need to avoid wasting resource and polluting log + dwRetryCount = 0; + } + if (SUCCEEDED(strEventMsg.SafeSnwprintf( ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG, m_struAppFullPath.QueryStr(), @@ -917,7 +941,6 @@ SERVER_PROCESS::StartProcess( } CleanUp(); - } Finished: diff --git a/src/RequestHandler/outofprocess/serverprocess.h b/src/RequestHandler/outofprocess/serverprocess.h index 3b6ff4b370..cef9446c15 100644 --- a/src/RequestHandler/outofprocess/serverprocess.h +++ b/src/RequestHandler/outofprocess/serverprocess.h @@ -127,6 +127,11 @@ private: VOID CleanUp(); + HRESULT + SetupJobObject( + VOID + ); + BOOL IsDebuggerIsAttached( VOID @@ -162,7 +167,8 @@ private: HRESULT SetupListenPort( - ENVIRONMENT_VAR_HASH *pEnvironmentVarTable + ENVIRONMENT_VAR_HASH *pEnvironmentVarTable, + BOOL *pfCriticalError ); HRESULT diff --git a/test/ANCMStressTestApp/Properties/launchSettings.json b/test/ANCMStressTestApp/Properties/launchSettings.json deleted file mode 100644 index 8e9b6cfcf7..0000000000 --- a/test/ANCMStressTestApp/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:16606/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "ANCMStressTestSample": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:16607/" - } - } -} \ No newline at end of file diff --git a/test/AspNetCoreModuleTests/AspNetCoreModuleTests.vcxproj b/test/AspNetCoreModuleTests/AspNetCoreModuleTests.vcxproj index 1dc88d2ac3..910b52ee74 100644 --- a/test/AspNetCoreModuleTests/AspNetCoreModuleTests.vcxproj +++ b/test/AspNetCoreModuleTests/AspNetCoreModuleTests.vcxproj @@ -167,6 +167,7 @@ Create + diff --git a/test/AspNetCoreModuleTests/hostfxr_utility_tests.cpp b/test/AspNetCoreModuleTests/hostfxr_utility_tests.cpp index dfa0e3c4b9..7e1d6f8d89 100644 --- a/test/AspNetCoreModuleTests/hostfxr_utility_tests.cpp +++ b/test/AspNetCoreModuleTests/hostfxr_utility_tests.cpp @@ -71,7 +71,7 @@ namespace AspNetCoreModuleTests Assert::AreEqual(DWORD(3), retVal); Assert::AreEqual(exeStr, bstrArray[0]); Assert::AreEqual(L"exec", bstrArray[1]); - Assert::AreEqual(L"\\\\?\\C:\\test\\test.dll", bstrArray[2]); + Assert::AreEqual(L"C:\\test\\test.dll", bstrArray[2]); } TEST_METHOD(ParseHostfxrArguments_ProvideNoArgs_InvalidArgs) @@ -90,5 +90,63 @@ namespace AspNetCoreModuleTests Assert::AreEqual(E_INVALIDARG, hr); } + + TEST_METHOD(GetAbsolutePathToDotnetFromProgramFiles_BackupWorks) + { + STRU struAbsolutePathToDotnet; + HRESULT hr = S_OK; + BOOL fDotnetInProgramFiles; + BOOL is64Bit; + BOOL fIsWow64 = FALSE; + SYSTEM_INFO systemInfo; + IsWow64Process(GetCurrentProcess(), &fIsWow64); + if (fIsWow64) + { + is64Bit = FALSE; + } + else + { + GetNativeSystemInfo(&systemInfo); + is64Bit = systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; + } + + if (is64Bit) + { + fDotnetInProgramFiles = UTILITY::CheckIfFileExists(L"C:/Program Files/dotnet/dotnet.exe"); + } + else + { + fDotnetInProgramFiles = UTILITY::CheckIfFileExists(L"C:/Program Files (x86)/dotnet/dotnet.exe"); + } + + hr = HOSTFXR_UTILITY::GetAbsolutePathToDotnetFromProgramFiles(&struAbsolutePathToDotnet); + if (fDotnetInProgramFiles) + { + Assert::AreEqual(hr, S_OK); + } + else + { + Assert::AreNotEqual(hr, S_OK); + Assert::IsTrue(struAbsolutePathToDotnet.IsEmpty()); + } + } + + TEST_METHOD(GetHostFxrArguments_InvalidParams) + { + DWORD retVal = 0; + BSTR* bstrArray; + STRU struHostFxrDllLocation; + + HRESULT hr = HOSTFXR_UTILITY::GetHostFxrParameters( + INVALID_HANDLE_VALUE, + L"bogus", // processPath + L"", // application physical path, ignored. + L"ignored", //arguments + NULL, // event log + &retVal, // arg count + &bstrArray); // args array. + + Assert::AreEqual(E_INVALIDARG, hr); + } }; } diff --git a/test/AspNetCoreModuleTests/utility_tests.cpp b/test/AspNetCoreModuleTests/utility_tests.cpp new file mode 100644 index 0000000000..a24711cf1a --- /dev/null +++ b/test/AspNetCoreModuleTests/utility_tests.cpp @@ -0,0 +1,52 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +#include "stdafx.h" +#include "CppUnitTest.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace AspNetCoreModuleTests +{ + TEST_CLASS(UTILITY_TESTS) + { + public: + + TEST_METHOD(PassUnexpandedString_ExpandsResult) + { + HRESULT hr = S_OK; + PCWSTR unexpandedString = L"ANCM_TEST_ENV_VAR"; + PCWSTR unexpandedStringValue = L"foobar"; + STRU struExpandedString; + SetEnvironmentVariable(L"ANCM_TEST_ENV_VAR", unexpandedStringValue); + + hr = struExpandedString.CopyAndExpandEnvironmentStrings(L"%ANCM_TEST_ENV_VAR%"); + Assert::AreEqual(hr, S_OK); + Assert::AreEqual(L"foobar", struExpandedString.QueryStr()); + } + + TEST_METHOD(PassUnexpandedString_Resize_ExpandsResult) + { + HRESULT hr = S_OK; + PCWSTR unexpandedString = L"ANCM_TEST_ENV_VAR_LONG"; + STRU struStringValue; + STACK_STRU(struExpandedString, MAX_PATH); + + struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize"); + struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize"); + struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize"); + struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize"); + struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize"); + struStringValue.Append(L"TestValueThatIsLongerThan256CharactersLongToTriggerResize"); + + SetEnvironmentVariable(unexpandedString, struStringValue.QueryStr()); + + hr = struExpandedString.CopyAndExpandEnvironmentStrings(L"%ANCM_TEST_ENV_VAR_LONG%"); + Assert::AreEqual(hr, S_OK); + Assert::AreEqual(struStringValue.QueryCCH(), struExpandedString.QueryCCH()); + // The values are exactly the same, however Assert::AreEqual is returning false. + //Assert::AreEqual(struStringValue.QueryStr(), struExpandedString.QueryStr()); + Assert::AreEqual(0, wcscmp(struStringValue.QueryStr(), struExpandedString.QueryStr())); + } + }; +} diff --git a/test/IISIntegration.FunctionalTests/IISIntegration.FunctionalTests.csproj b/test/IISIntegration.FunctionalTests/IISIntegration.FunctionalTests.csproj index c09eeee095..d79a5ec5d9 100644 --- a/test/IISIntegration.FunctionalTests/IISIntegration.FunctionalTests.csproj +++ b/test/IISIntegration.FunctionalTests/IISIntegration.FunctionalTests.csproj @@ -10,7 +10,9 @@ - + + False + diff --git a/test/IISIntegration.FunctionalTests/Inprocess/ServerVariablesTest.cs b/test/IISIntegration.FunctionalTests/Inprocess/ServerVariablesTest.cs index f86b3bcffd..d6fd676346 100644 --- a/test/IISIntegration.FunctionalTests/Inprocess/ServerVariablesTest.cs +++ b/test/IISIntegration.FunctionalTests/Inprocess/ServerVariablesTest.cs @@ -28,8 +28,13 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests [ConditionalFact] public async Task ReturnsNullForUndefinedServerVariable() { - var port = _fixture.Client.BaseAddress.Port; Assert.Equal("THIS_VAR_IS_UNDEFINED: (null)", await _fixture.Client.GetStringAsync("/ServerVariable?q=THIS_VAR_IS_UNDEFINED")); } + + [ConditionalFact] + public async Task BasePathIsNotPrefixedBySlashSlashQuestionMark() + { + Assert.DoesNotContain(@"\\?\", await _fixture.Client.GetStringAsync("/BasePath")); + } } } diff --git a/test/IISIntegration.FunctionalTests/Inprocess/StartupTests.cs b/test/IISIntegration.FunctionalTests/Inprocess/StartupTests.cs new file mode 100644 index 0000000000..e351649d86 --- /dev/null +++ b/test/IISIntegration.FunctionalTests/Inprocess/StartupTests.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using System.Xml.Linq; +using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Testing; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + + +namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests +{ + public class StartupTests : LoggedTest + { + public StartupTests(ITestOutputHelper output) : base(output) + { + + } + + [Fact] + public async Task ExpandEnvironmentVariableInWebConfig() + { + var architecture = RuntimeArchitecture.x64; + var dotnetLocation = $"%USERPROFILE%\\.dotnet\\{architecture.ToString()}\\dotnet.exe"; + using (StartLog(out var loggerFactory)) + { + var logger = loggerFactory.CreateLogger("HelloWorldTest"); + + var deploymentParameters = GetBaseDeploymentParameters(); + + // Point to dotnet installed in user profile. + deploymentParameters.EnvironmentVariables["DotnetPath"] = Environment.ExpandEnvironmentVariables(dotnetLocation); // Path to dotnet. + + using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory)) + { + var deploymentResult = await deployer.DeployAsync(); + + Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%"); + + // Request to base address and check if various parts of the body are rendered & measure the cold startup time. + var response = await RetryHelper.RetryRequest(() => + { + return deploymentResult.HttpClient.GetAsync("HelloWorld"); + }, logger, deploymentResult.HostShutdownToken, retryCount: 30); + + var responseText = await response.Content.ReadAsStringAsync(); + try + { + Assert.Equal("Hello World", responseText); + } + catch (XunitException) + { + logger.LogWarning(response.ToString()); + logger.LogWarning(responseText); + throw; + } + } + } + } + + [Fact] + public async Task InvalidProcessPath_ExpectServerError() + { + var dotnetLocation = "bogus"; + using (StartLog(out var loggerFactory)) + { + var logger = loggerFactory.CreateLogger("HelloWorldTest"); + var deploymentParameters = GetBaseDeploymentParameters(); + + // Point to dotnet installed in user profile. + deploymentParameters.EnvironmentVariables["DotnetPath"] = Environment.ExpandEnvironmentVariables(dotnetLocation); // Path to dotnet. + + using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory)) + { + var deploymentResult = await deployer.DeployAsync(); + + Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", "%DotnetPath%"); + + // Request to base address and check if various parts of the body are rendered & measure the cold startup time. + var response = await RetryHelper.RetryRequest(() => + { + return deploymentResult.HttpClient.GetAsync("HelloWorld"); + }, logger, deploymentResult.HostShutdownToken, retryCount: 30); + + Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); + } + } + } + +#if NETCOREAPP2_0 || NETCOREAPP2_1 + + [Fact] // Consistently fails on CI for net461 + public async Task StandaloneApplication_ExpectCorrectPublish() + { + using (StartLog(out var loggerFactory)) + { + var logger = loggerFactory.CreateLogger("HelloWorldTest"); + + var deploymentParameters = GetBaseDeploymentParameters(); + + using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory)) + { + var deploymentResult = await deployer.DeployAsync(); + + // Request to base address and check if various parts of the body are rendered & measure the cold startup time. + var response = await RetryHelper.RetryRequest(() => + { + return deploymentResult.HttpClient.GetAsync("HelloWorld"); + }, logger, deploymentResult.HostShutdownToken, retryCount: 30); + + var responseText = await response.Content.ReadAsStringAsync(); + try + { + Assert.Equal("Hello World", responseText); + } + catch (XunitException) + { + logger.LogWarning(response.ToString()); + logger.LogWarning(responseText); + throw; + } + } + } + } + + [Fact] // Consistently fails on CI for net461 + public async Task StandaloneApplication_AbsolutePathToExe_ExpectCorrectPublish() + { + using (StartLog(out var loggerFactory)) + { + var logger = loggerFactory.CreateLogger("HelloWorldTest"); + + var deploymentParameters = GetBaseDeploymentParameters(); + deploymentParameters.ApplicationType = ApplicationType.Standalone; + + using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, loggerFactory)) + { + var deploymentResult = await deployer.DeployAsync(); + + Helpers.ModifyAspNetCoreSectionInWebConfig(deploymentResult, "processPath", $"{deploymentResult.ContentRoot}\\InProcessWebSite.exe"); + + // Request to base address and check if various parts of the body are rendered & measure the cold startup time. + var response = await RetryHelper.RetryRequest(() => + { + return deploymentResult.HttpClient.GetAsync("HelloWorld"); + }, logger, deploymentResult.HostShutdownToken, retryCount: 30); + + var responseText = await response.Content.ReadAsStringAsync(); + try + { + Assert.Equal("Hello World", responseText); + } + catch (XunitException) + { + logger.LogWarning(response.ToString()); + logger.LogWarning(responseText); + throw; + } + } + } + } + +#elif NET461 +#else +#error Target frameworks need to be updated +#endif + + [Fact] + public async Task DetectsOveriddenServer() + { + var testSink = new TestSink(); + using (StartLog(out var loggerFactory)) + { + var testLoggerFactory = new TestLoggerFactory(testSink, true); + loggerFactory.AddProvider(new TestLoggerProvider(testLoggerFactory)); + + using (var deployer = ApplicationDeployerFactory.Create(GetBaseDeploymentParameters("OverriddenServerWebSite"), loggerFactory)) + { + var deploymentResult = await deployer.DeployAsync(); + var response = await deploymentResult.HttpClient.GetAsync("/"); + Assert.False(response.IsSuccessStatusCode); + } + } + Assert.Contains(testSink.Writes, context => context.State.ToString().Contains("Application is running inside IIS process but is not configured to use IIS server")); + } + + private DeploymentParameters GetBaseDeploymentParameters(string site = null) + { + return new DeploymentParameters(Helpers.GetTestWebSitePath(site ?? "InProcessWebSite"), ServerType.IISExpress, RuntimeFlavor.CoreClr, RuntimeArchitecture.x64) + { + ServerConfigTemplateContent = File.ReadAllText("AppHostConfig/Http.config"), + SiteName = "HttpTestSite", // This is configured in the Http.config + TargetFramework = "netcoreapp2.1", + ApplicationType = ApplicationType.Portable, + Configuration = GetCurrentConfiguration() + }; + } + + private static string GetCurrentConfiguration() + { +#if DEBUG + return "Debug"; +#else + return "Release"; +#endif + } + + private class TestLoggerProvider : ILoggerProvider + { + private readonly TestLoggerFactory _loggerFactory; + + public TestLoggerProvider(TestLoggerFactory loggerFactory) + { + _loggerFactory = loggerFactory; + } + + public void Dispose() + { + } + + public ILogger CreateLogger(string categoryName) + { + return _loggerFactory.CreateLogger(categoryName); + } + } + } +} diff --git a/test/IISIntegration.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs b/test/IISIntegration.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs index e69437a7b8..c79c8a462c 100644 --- a/test/IISIntegration.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs +++ b/test/IISIntegration.FunctionalTests/Inprocess/SynchronousReadAndWriteTests.cs @@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests Assert.Equal(body, responseText); } - [ConditionalFact(Skip = "See https://github.com/aspnet/IISIntegration/issues/687")] + [ConditionalFact] public async Task ReadAndWriteEchoTwice() { var requestBody = new string('a', 10000); diff --git a/test/IISIntegration.FunctionalTests/Utilities/Helpers.cs b/test/IISIntegration.FunctionalTests/Utilities/Helpers.cs index c09b1bf3a7..5cda360005 100644 --- a/test/IISIntegration.FunctionalTests/Utilities/Helpers.cs +++ b/test/IISIntegration.FunctionalTests/Utilities/Helpers.cs @@ -1,33 +1,41 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.AspNetCore.Server.IntegrationTesting; using System; using System.IO; +using System.Linq; +using System.Xml.Linq; namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests { public class Helpers { - public static string GetInProcessTestSitesPath() - { + public static string GetTestWebSitePath(string name) + { return Path.GetFullPath( Path.Combine(AppDomain.CurrentDomain.BaseDirectory, - "..", // tfm - "..", // debug - "..", // obj - "..", // projectfolder - "IISTestSite")); + "..", // tfm + "..", // debug + "..", // obj + "..", // projectfolder + "WebSites", + name)); } - public static string GetOutOfProcessTestSitesPath() + public static string GetInProcessTestSitesPath() => GetTestWebSitePath("InProcessWebSite"); + + public static string GetOutOfProcessTestSitesPath() => GetTestWebSitePath("OutOfProcessWebSite"); + + public static void ModifyAspNetCoreSectionInWebConfig(DeploymentResult deploymentResult, string key, string value) { - return Path.GetFullPath( - Path.Combine(AppDomain.CurrentDomain.BaseDirectory, - "..", // tfm - "..", // debug - "..", // obj - "..", // projectfolder - "TestSites")); + // modify the web.config after publish + var root = deploymentResult.ContentRoot; + var webConfigFile = $"{root}/web.config"; + var config = XDocument.Load(webConfigFile); + var element = config.Descendants("aspNetCore").FirstOrDefault(); + element.SetAttributeValue(key, value); + config.Save(webConfigFile); } } } diff --git a/test/TestTasks/InjectRequestHandler.cs b/test/TestTasks/InjectRequestHandler.cs new file mode 100644 index 0000000000..639428ebd1 --- /dev/null +++ b/test/TestTasks/InjectRequestHandler.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace TestTasks +{ + public class InjectRequestHandler + { + private static void Main(string[] args) + { + var depsFile = args[2]; + var rid = args[0]; + var libraryLocation = args[1]; + + JToken deps; + using (var file = File.OpenText(depsFile)) + using (JsonTextReader reader = new JsonTextReader(file)) + { + deps = JObject.ReadFrom(reader); + } + + var libraryName = "ANCMRH/1.0"; + var libraries = (JObject)deps["libraries"]; + var targetName = (JValue)deps["runtimeTarget"]["name"]; + + var target = (JObject)deps["targets"][targetName.Value]; + var targetLibrary = target.Properties().FirstOrDefault(p => p.Name == libraryName); + targetLibrary?.Remove(); + targetLibrary = + new JProperty(libraryName, new JObject( + new JProperty("runtimeTargets", new JObject( + new JProperty(libraryLocation.Replace('\\', '/'), new JObject( + new JProperty("rid", rid), + new JProperty("assetType", "native") + )))))); + target.AddFirst(targetLibrary); + + var library = libraries.Properties().FirstOrDefault(p => p.Name == libraryName); + library?.Remove(); + library = + new JProperty(libraryName, new JObject( + new JProperty("type", "package"), + new JProperty("serviceable", true), + new JProperty("sha512", ""), + new JProperty("path", libraryName), + new JProperty("hashPath", ""))); + libraries.AddFirst(library); + + using (var file = File.CreateText(depsFile)) + using (var writer = new JsonTextWriter(file) { Formatting = Formatting.Indented }) + { + deps.WriteTo(writer); + } + } + } +} diff --git a/test/TestTasks/TestTasks.csproj b/test/TestTasks/TestTasks.csproj new file mode 100644 index 0000000000..aa4c144936 --- /dev/null +++ b/test/TestTasks/TestTasks.csproj @@ -0,0 +1,12 @@ + + + + Exe + $(StandardTestTfms) + + + + + + + diff --git a/test/IISTestSite/IISTestSite.csproj b/test/WebSites/InProcessWebSite/InProcessWebSite.csproj similarity index 83% rename from test/IISTestSite/IISTestSite.csproj rename to test/WebSites/InProcessWebSite/InProcessWebSite.csproj index f628ba9cf9..c615d460ee 100644 --- a/test/IISTestSite/IISTestSite.csproj +++ b/test/WebSites/InProcessWebSite/InProcessWebSite.csproj @@ -1,13 +1,12 @@  - - + $(StandardTestTfms) - + diff --git a/test/IISTestSite/Program.cs b/test/WebSites/InProcessWebSite/Program.cs similarity index 92% rename from test/IISTestSite/Program.cs rename to test/WebSites/InProcessWebSite/Program.cs index 5e22d2f2c6..0550f0f1fd 100644 --- a/test/IISTestSite/Program.cs +++ b/test/WebSites/InProcessWebSite/Program.cs @@ -17,7 +17,7 @@ namespace IISTestSite factory.AddFilter("Console", level => level >= LogLevel.Information); }) .UseIISIntegration() - .UseStartup("IISTestSite") + .UseStartup(typeof(Program).Assembly.FullName) .Build(); host.Run(); diff --git a/test/IISTestSite/Properties/launchSettings.json b/test/WebSites/InProcessWebSite/Properties/launchSettings.json similarity index 97% rename from test/IISTestSite/Properties/launchSettings.json rename to test/WebSites/InProcessWebSite/Properties/launchSettings.json index 7d09a120ab..6d5ce43f73 100644 --- a/test/IISTestSite/Properties/launchSettings.json +++ b/test/WebSites/InProcessWebSite/Properties/launchSettings.json @@ -12,6 +12,7 @@ "commandName": "Executable", "executablePath": "$(IISExpressPath)", "commandLineArgs": "$(IISExpressArguments)", + "nativeDebugging": true, "environmentVariables": { "IIS_SITE_PATH": "$(MSBuildThisFileDirectory)", "ANCM_PATH": "$(TargetDir)$(AncmPath)", diff --git a/test/IISTestSite/Startup.cs b/test/WebSites/InProcessWebSite/Startup.cs similarity index 99% rename from test/IISTestSite/Startup.cs rename to test/WebSites/InProcessWebSite/Startup.cs index 00b83e769d..781601aea5 100644 --- a/test/IISTestSite/Startup.cs +++ b/test/WebSites/InProcessWebSite/Startup.cs @@ -667,5 +667,10 @@ namespace IISTestSite } }); } + + private void BasePath(IApplicationBuilder app) + { + app.Run(async ctx => { await ctx.Response.WriteAsync(AppDomain.CurrentDomain.BaseDirectory); }); + } } } diff --git a/test/IISTestSite/web.config b/test/WebSites/InProcessWebSite/web.config similarity index 100% rename from test/IISTestSite/web.config rename to test/WebSites/InProcessWebSite/web.config diff --git a/test/TestSites/TestSites.csproj b/test/WebSites/OutOfProcessWebSite/OutOfProcessWebSite.csproj similarity index 83% rename from test/TestSites/TestSites.csproj rename to test/WebSites/OutOfProcessWebSite/OutOfProcessWebSite.csproj index 19ed3dc502..0b96c98c36 100644 --- a/test/TestSites/TestSites.csproj +++ b/test/WebSites/OutOfProcessWebSite/OutOfProcessWebSite.csproj @@ -1,13 +1,13 @@  - + $(StandardTestTfms) - + diff --git a/test/TestSites/Program.cs b/test/WebSites/OutOfProcessWebSite/Program.cs similarity index 92% rename from test/TestSites/Program.cs rename to test/WebSites/OutOfProcessWebSite/Program.cs index af6e90bd15..18104fa30a 100644 --- a/test/TestSites/Program.cs +++ b/test/WebSites/OutOfProcessWebSite/Program.cs @@ -17,7 +17,7 @@ namespace TestSites factory.AddFilter("Console", level => level >= LogLevel.Information); }) .UseIISIntegration() - .UseStartup("TestSites") + .UseStartup(typeof(Program).Assembly.FullName) .UseKestrel() .Build(); diff --git a/test/WebSites/OutOfProcessWebSite/Properties/launchSettings.json b/test/WebSites/OutOfProcessWebSite/Properties/launchSettings.json new file mode 100644 index 0000000000..6d5ce43f73 --- /dev/null +++ b/test/WebSites/OutOfProcessWebSite/Properties/launchSettings.json @@ -0,0 +1,37 @@ +{ + "iisSettings": { + "windowsAuthentication": true, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:5762/", + "sslPort": 0 + } + }, + "profiles": { + "ANCM IIS Express": { + "commandName": "Executable", + "executablePath": "$(IISExpressPath)", + "commandLineArgs": "$(IISExpressArguments)", + "nativeDebugging": true, + "environmentVariables": { + "IIS_SITE_PATH": "$(MSBuildThisFileDirectory)", + "ANCM_PATH": "$(TargetDir)$(AncmPath)", + "LAUNCHER_ARGS": "$(TargetPath)", + "ASPNETCORE_ENVIRONMENT": "Development", + "LAUNCHER_PATH": "$(DotNetPath)" + } + }, + "ANCM IIS": { + "commandName": "Executable", + "executablePath": "$(IISPath)", + "commandLineArgs": "$(IISArguments)", + "environmentVariables": { + "IIS_SITE_PATH": "$(MSBuildThisFileDirectory)", + "ANCM_PATH": "$(TargetDir)$(AncmPath)", + "LAUNCHER_ARGS": "$(TargetPath)", + "ASPNETCORE_ENVIRONMENT": "Development", + "LAUNCHER_PATH": "$(DotNetPath)" + } + } + } +} diff --git a/test/TestSites/StartupHelloWorld.cs b/test/WebSites/OutOfProcessWebSite/StartupHelloWorld.cs similarity index 100% rename from test/TestSites/StartupHelloWorld.cs rename to test/WebSites/OutOfProcessWebSite/StartupHelloWorld.cs diff --git a/test/TestSites/StartupHttpsHelloWorld.cs b/test/WebSites/OutOfProcessWebSite/StartupHttpsHelloWorld.cs similarity index 100% rename from test/TestSites/StartupHttpsHelloWorld.cs rename to test/WebSites/OutOfProcessWebSite/StartupHttpsHelloWorld.cs diff --git a/test/TestSites/StartupNtlmAuthentication.cs b/test/WebSites/OutOfProcessWebSite/StartupNtlmAuthentication.cs similarity index 100% rename from test/TestSites/StartupNtlmAuthentication.cs rename to test/WebSites/OutOfProcessWebSite/StartupNtlmAuthentication.cs diff --git a/test/TestSites/StartupUpgradeFeatureDetection.cs b/test/WebSites/OutOfProcessWebSite/StartupUpgradeFeatureDetection.cs similarity index 100% rename from test/TestSites/StartupUpgradeFeatureDetection.cs rename to test/WebSites/OutOfProcessWebSite/StartupUpgradeFeatureDetection.cs diff --git a/test/TestSites/web.config b/test/WebSites/OutOfProcessWebSite/web.config similarity index 100% rename from test/TestSites/web.config rename to test/WebSites/OutOfProcessWebSite/web.config diff --git a/test/WebSites/OverriddenServerWebSite/OverriddenServerWebSite.csproj b/test/WebSites/OverriddenServerWebSite/OverriddenServerWebSite.csproj new file mode 100644 index 0000000000..4332ea3fd1 --- /dev/null +++ b/test/WebSites/OverriddenServerWebSite/OverriddenServerWebSite.csproj @@ -0,0 +1,17 @@ + + + + + + $(StandardTestTfms) + + + + + + + + + + + diff --git a/test/WebSites/OverriddenServerWebSite/Program.cs b/test/WebSites/OverriddenServerWebSite/Program.cs new file mode 100644 index 0000000000..bb65e03004 --- /dev/null +++ b/test/WebSites/OverriddenServerWebSite/Program.cs @@ -0,0 +1,48 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.DependencyInjection; + +namespace IISTestSite +{ + public static class Program + { + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .UseIISIntegration() + .ConfigureServices(services => services.AddSingleton()) + .Configure(builder => builder.Run(async context => { await context.Response.WriteAsync("I shouldn't work"); })) + .Build(); + + host.Run(); + } + } + + public class DummyServer: IServer + { + public void Dispose() + { + } + + public Task StartAsync(IHttpApplication application, CancellationToken cancellationToken) + { + return Task.Delay(TimeSpan.MaxValue); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.Delay(TimeSpan.MaxValue); + } + + public IFeatureCollection Features { get; } + } +} diff --git a/test/WebSites/OverriddenServerWebSite/Properties/launchSettings.json b/test/WebSites/OverriddenServerWebSite/Properties/launchSettings.json new file mode 100644 index 0000000000..6d5ce43f73 --- /dev/null +++ b/test/WebSites/OverriddenServerWebSite/Properties/launchSettings.json @@ -0,0 +1,37 @@ +{ + "iisSettings": { + "windowsAuthentication": true, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:5762/", + "sslPort": 0 + } + }, + "profiles": { + "ANCM IIS Express": { + "commandName": "Executable", + "executablePath": "$(IISExpressPath)", + "commandLineArgs": "$(IISExpressArguments)", + "nativeDebugging": true, + "environmentVariables": { + "IIS_SITE_PATH": "$(MSBuildThisFileDirectory)", + "ANCM_PATH": "$(TargetDir)$(AncmPath)", + "LAUNCHER_ARGS": "$(TargetPath)", + "ASPNETCORE_ENVIRONMENT": "Development", + "LAUNCHER_PATH": "$(DotNetPath)" + } + }, + "ANCM IIS": { + "commandName": "Executable", + "executablePath": "$(IISPath)", + "commandLineArgs": "$(IISArguments)", + "environmentVariables": { + "IIS_SITE_PATH": "$(MSBuildThisFileDirectory)", + "ANCM_PATH": "$(TargetDir)$(AncmPath)", + "LAUNCHER_ARGS": "$(TargetPath)", + "ASPNETCORE_ENVIRONMENT": "Development", + "LAUNCHER_PATH": "$(DotNetPath)" + } + } + } +} diff --git a/test/WebSites/OverriddenServerWebSite/web.config b/test/WebSites/OverriddenServerWebSite/web.config new file mode 100644 index 0000000000..f125d57107 --- /dev/null +++ b/test/WebSites/OverriddenServerWebSite/web.config @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/test/ANCMStressTestApp/Program.cs b/test/WebSites/StressTestWebSite/Program.cs similarity index 69% rename from test/ANCMStressTestApp/Program.cs rename to test/WebSites/StressTestWebSite/Program.cs index b0edb5b7f2..e8e5392c2c 100644 --- a/test/ANCMStressTestApp/Program.cs +++ b/test/WebSites/StressTestWebSite/Program.cs @@ -1,16 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Linq; -using System.Threading; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Server.IISIntegration; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace ANCMStressTestApp diff --git a/test/WebSites/StressTestWebSite/Properties/launchSettings.json b/test/WebSites/StressTestWebSite/Properties/launchSettings.json new file mode 100644 index 0000000000..6d5ce43f73 --- /dev/null +++ b/test/WebSites/StressTestWebSite/Properties/launchSettings.json @@ -0,0 +1,37 @@ +{ + "iisSettings": { + "windowsAuthentication": true, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:5762/", + "sslPort": 0 + } + }, + "profiles": { + "ANCM IIS Express": { + "commandName": "Executable", + "executablePath": "$(IISExpressPath)", + "commandLineArgs": "$(IISExpressArguments)", + "nativeDebugging": true, + "environmentVariables": { + "IIS_SITE_PATH": "$(MSBuildThisFileDirectory)", + "ANCM_PATH": "$(TargetDir)$(AncmPath)", + "LAUNCHER_ARGS": "$(TargetPath)", + "ASPNETCORE_ENVIRONMENT": "Development", + "LAUNCHER_PATH": "$(DotNetPath)" + } + }, + "ANCM IIS": { + "commandName": "Executable", + "executablePath": "$(IISPath)", + "commandLineArgs": "$(IISArguments)", + "environmentVariables": { + "IIS_SITE_PATH": "$(MSBuildThisFileDirectory)", + "ANCM_PATH": "$(TargetDir)$(AncmPath)", + "LAUNCHER_ARGS": "$(TargetPath)", + "ASPNETCORE_ENVIRONMENT": "Development", + "LAUNCHER_PATH": "$(DotNetPath)" + } + } + } +} diff --git a/test/ANCMStressTestApp/Startup.cs b/test/WebSites/StressTestWebSite/Startup.cs similarity index 100% rename from test/ANCMStressTestApp/Startup.cs rename to test/WebSites/StressTestWebSite/Startup.cs diff --git a/test/ANCMStressTestApp/ANCMStressTestApp.csproj b/test/WebSites/StressTestWebSite/StressTestWebSite.csproj similarity index 68% rename from test/ANCMStressTestApp/ANCMStressTestApp.csproj rename to test/WebSites/StressTestWebSite/StressTestWebSite.csproj index 54755b4824..3566143fcd 100644 --- a/test/ANCMStressTestApp/ANCMStressTestApp.csproj +++ b/test/WebSites/StressTestWebSite/StressTestWebSite.csproj @@ -1,12 +1,13 @@ + + - netcoreapp2.1 - win-x86;win-x64 + $(StandardTestTfms) - + diff --git a/test/ANCMStressTestApp/WebSockets/Constants.cs b/test/WebSites/StressTestWebSite/WebSockets/Constants.cs similarity index 100% rename from test/ANCMStressTestApp/WebSockets/Constants.cs rename to test/WebSites/StressTestWebSite/WebSockets/Constants.cs diff --git a/test/ANCMStressTestApp/WebSockets/HandshakeHelpers.cs b/test/WebSites/StressTestWebSite/WebSockets/HandshakeHelpers.cs similarity index 100% rename from test/ANCMStressTestApp/WebSockets/HandshakeHelpers.cs rename to test/WebSites/StressTestWebSite/WebSockets/HandshakeHelpers.cs