diff --git a/IISIntegration.sln b/IISIntegration.sln
new file mode 100644
index 0000000000..77a2abadb2
--- /dev/null
+++ b/IISIntegration.sln
@@ -0,0 +1,65 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.23107.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04B1EDB6-E967-4D25-89B9-E6F8304038CD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0EF45656-B25D-40D8-959C-726EAF185E60}"
+ ProjectSection(SolutionItems) = preProject
+ global.json = global.json
+ NuGet.Config = NuGet.Config
+ EndProjectSection
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.IISPlatformHandler", "src\Microsoft.AspNet.IISPlatformHandler\Microsoft.AspNet.IISPlatformHandler.xproj", "{ABE53415-83CE-4AF0-AF67-E52160C7862B}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{EF30B533-D715-421A-92B7-92FEF460AC9C}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.IISPlatformHandler.Tests", "test\Microsoft.AspNet.IISPlatformHandler.Tests\Microsoft.AspNet.IISPlatformHandler.Tests.xproj", "{FBBBE015-1CE3-454B-9647-23F8073FB7AB}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.IISPlatformHandler.FunctionalTests", "test\Microsoft.AspNet.IISPlatformHandler.FunctionalTests\Microsoft.AspNet.IISPlatformHandler.FunctionalTests.xproj", "{A83A33D1-3D29-403C-9750-5811AC7B4025}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "TestSites", "test\TestSites\TestSites.xproj", "{E27453AD-9CA0-49A2-94FA-96337D77677D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{C74B8F36-FD2F-45C9-9B8A-00E7CF0126A9}"
+EndProject
+Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "IISSample", "samples\IISSample\IISSample.xproj", "{E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {ABE53415-83CE-4AF0-AF67-E52160C7862B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ABE53415-83CE-4AF0-AF67-E52160C7862B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ABE53415-83CE-4AF0-AF67-E52160C7862B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ABE53415-83CE-4AF0-AF67-E52160C7862B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FBBBE015-1CE3-454B-9647-23F8073FB7AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FBBBE015-1CE3-454B-9647-23F8073FB7AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FBBBE015-1CE3-454B-9647-23F8073FB7AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FBBBE015-1CE3-454B-9647-23F8073FB7AB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A83A33D1-3D29-403C-9750-5811AC7B4025}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A83A33D1-3D29-403C-9750-5811AC7B4025}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A83A33D1-3D29-403C-9750-5811AC7B4025}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A83A33D1-3D29-403C-9750-5811AC7B4025}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E27453AD-9CA0-49A2-94FA-96337D77677D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E27453AD-9CA0-49A2-94FA-96337D77677D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E27453AD-9CA0-49A2-94FA-96337D77677D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E27453AD-9CA0-49A2-94FA-96337D77677D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {ABE53415-83CE-4AF0-AF67-E52160C7862B} = {04B1EDB6-E967-4D25-89B9-E6F8304038CD}
+ {FBBBE015-1CE3-454B-9647-23F8073FB7AB} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
+ {A83A33D1-3D29-403C-9750-5811AC7B4025} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
+ {E27453AD-9CA0-49A2-94FA-96337D77677D} = {EF30B533-D715-421A-92B7-92FEF460AC9C}
+ {E4E2BDC4-A9C6-4AE9-B429-032EC83EDE64} = {C74B8F36-FD2F-45C9-9B8A-00E7CF0126A9}
+ EndGlobalSection
+EndGlobal
diff --git a/build.cmd b/build.cmd
index b54d91cf74..197579bc35 100644
--- a/build.cmd
+++ b/build.cmd
@@ -31,7 +31,9 @@ IF %BUILDCMD_DNX_VERSION%=="" (
) ELSE (
CALL packages\KoreBuild\build\dnvm install %BUILDCMD_DNX_VERSION% -runtime CLR -arch x86 -a default
)
+CALL packages\KoreBuild\build\dnvm install default -arch x64
CALL packages\KoreBuild\build\dnvm install default -runtime CoreCLR -arch x86
+CALL packages\KoreBuild\build\dnvm install default -runtime CoreCLR -arch x64
:run
CALL packages\KoreBuild\build\dnvm use default -runtime CLR -arch x86
diff --git a/samples/IISSample/IISSample.xproj b/samples/IISSample/IISSample.xproj
new file mode 100644
index 0000000000..4b3ec10e26
--- /dev/null
+++ b/samples/IISSample/IISSample.xproj
@@ -0,0 +1,19 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ e4e2bdc4-a9c6-4ae9-b429-032ec83ede64
+ IISSample
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+ 2.0
+ 65029
+
+
+
\ No newline at end of file
diff --git a/samples/IISSample/Startup.cs b/samples/IISSample/Startup.cs
new file mode 100644
index 0000000000..ef4475292f
--- /dev/null
+++ b/samples/IISSample/Startup.cs
@@ -0,0 +1,30 @@
+using System;
+using Microsoft.AspNet.Builder;
+using Microsoft.AspNet.Http;
+using Microsoft.Framework.Logging;
+
+namespace IISSample
+{
+ public class Startup
+ {
+ public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
+ {
+ loggerfactory.AddConsole(LogLevel.Verbose);
+
+ var logger = loggerfactory.CreateLogger("Requests");
+
+ app.Run(async (context) =>
+ {
+ logger.LogVerbose("Received request: " + context.Request.Method + " " + context.Request.Path);
+
+ context.Response.ContentType = "text/plain";
+ await context.Response.WriteAsync("Hello World - " + DateTimeOffset.Now + Environment.NewLine);
+ await context.Response.WriteAsync("User - " + context.User.Identity.Name + Environment.NewLine);
+ foreach (var header in context.Request.Headers)
+ {
+ await context.Response.WriteAsync(header.Key + ": " + header.Value + Environment.NewLine);
+ }
+ });
+ }
+ }
+}
diff --git a/samples/IISSample/project.json b/samples/IISSample/project.json
new file mode 100644
index 0000000000..b7b968402a
--- /dev/null
+++ b/samples/IISSample/project.json
@@ -0,0 +1,34 @@
+{
+ "webroot": "wwwroot",
+ "version": "1.0.0-*",
+
+ "dependencies": {
+ "Microsoft.AspNet.IISPlatformHandler": "1.0.0-*",
+ "Microsoft.AspNet.Server.Kestrel": "1.0.0-*",
+ "Microsoft.AspNet.Server.WebListener": "1.0.0-*",
+ "Microsoft.Framework.Logging.Console": "1.0.0-*"
+ },
+
+ "commands": {
+ "weblistener": "Microsoft.AspNet.Server.WebListener",
+ "web": "Microsoft.AspNet.Server.Kestrel"
+ },
+
+ "frameworks": {
+ "dnx451": { },
+ "dnxcore50": { }
+ },
+
+ "publishExclude": [
+ "node_modules",
+ "bower_components",
+ "**.xproj",
+ "**.user",
+ "**.vspscc"
+ ],
+ "exclude": [
+ "wwwroot",
+ "node_modules",
+ "bower_components"
+ ]
+}
diff --git a/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs
new file mode 100644
index 0000000000..5984a26d4c
--- /dev/null
+++ b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddleware.cs
@@ -0,0 +1,85 @@
+// 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.Globalization;
+using System.Net;
+using System.Security.Principal;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Builder;
+using Microsoft.AspNet.Http;
+using Microsoft.Framework.Internal;
+using Microsoft.Framework.Primitives;
+
+namespace Microsoft.AspNet.IISPlatformHandler
+{
+ public class IISPlatformHandlerMiddleware
+ {
+ private const string XForwardedForHeaderName = "X-Forwarded-For";
+ private const string XForwardedProtoHeaderName = "X-Forwarded-Proto";
+ private const string XIISWindowsAuthToken = "X-IIS-WindowsAuthToken";
+ private const string XOriginalProtoName = "X-Original-Proto";
+ private const string XOriginalIPName = "X-Original-IP";
+
+ private readonly RequestDelegate _next;
+
+ public IISPlatformHandlerMiddleware(RequestDelegate next)
+ {
+ _next = next;
+ }
+
+ public Task Invoke(HttpContext httpContext)
+ {
+ var xForwardProtoHeaderValue = httpContext.Request.Headers[XForwardedProtoHeaderName];
+ if (!string.IsNullOrEmpty(xForwardProtoHeaderValue))
+ {
+ if (!string.IsNullOrEmpty(httpContext.Request.Scheme))
+ {
+ httpContext.Request.Headers[XOriginalProtoName] = httpContext.Request.Scheme;
+ }
+ httpContext.Request.Scheme = xForwardProtoHeaderValue;
+ }
+
+ var xForwardedForHeaderValue = httpContext.Request.Headers.GetCommaSeparatedValues(XForwardedForHeaderName);
+ if (xForwardedForHeaderValue != null && xForwardedForHeaderValue.Length > 0)
+ {
+ IPAddress ipFromHeader;
+ if (IPAddress.TryParse(xForwardedForHeaderValue[0], out ipFromHeader))
+ {
+ var remoteIPString = httpContext.Connection.RemoteIpAddress?.ToString();
+ if (!string.IsNullOrEmpty(remoteIPString))
+ {
+ httpContext.Request.Headers[XOriginalIPName] = remoteIPString;
+ }
+ httpContext.Connection.RemoteIpAddress = ipFromHeader;
+ }
+ }
+
+ var xIISWindowsAuthToken = httpContext.Request.Headers[XIISWindowsAuthToken];
+ int hexHandle;
+ if (!StringValues.IsNullOrEmpty(xIISWindowsAuthToken)
+ && int.TryParse(xIISWindowsAuthToken, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out hexHandle))
+ {
+ var handle = new IntPtr(hexHandle);
+ var winIdentity = new WindowsIdentity(handle);
+ // WindowsIdentity just duplicated the handle so we need to close the original.
+ NativeMethods.CloseHandle(handle);
+
+ httpContext.Response.RegisterForDispose(winIdentity);
+ var winPrincipal = new WindowsPrincipal(winIdentity);
+
+ var existingPrincipal = httpContext.User;
+ if (existingPrincipal != null)
+ {
+ httpContext.User = SecurityHelper.MergeUserPrincipal(existingPrincipal, winPrincipal);
+ }
+ else
+ {
+ httpContext.User = winPrincipal;
+ }
+ }
+
+ return _next(httpContext);
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddlewareExtensions.cs b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddlewareExtensions.cs
new file mode 100644
index 0000000000..2525cd280d
--- /dev/null
+++ b/src/Microsoft.AspNet.IISPlatformHandler/IISPlatformHandlerMiddlewareExtensions.cs
@@ -0,0 +1,21 @@
+// 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.AspNet.IISPlatformHandler;
+
+namespace Microsoft.AspNet.Builder
+{
+ public static class IISPlatformHandlerMiddlewareExtensions
+ {
+ ///
+ /// Adds middleware for interacting with the IIS HttpPlatformHandler reverse proxy module.
+ /// This will handle forwarded Windows Authentication, request scheme, remote IPs, etc..
+ ///
+ ///
+ ///
+ public static IApplicationBuilder UseIISPlatformHandler(this IApplicationBuilder builder)
+ {
+ return builder.UseMiddleware();
+ }
+ }
+}
diff --git a/src/Microsoft.AspNet.IISPlatformHandler/Microsoft.AspNet.IISPlatformHandler.xproj b/src/Microsoft.AspNet.IISPlatformHandler/Microsoft.AspNet.IISPlatformHandler.xproj
new file mode 100644
index 0000000000..d8a0feec36
--- /dev/null
+++ b/src/Microsoft.AspNet.IISPlatformHandler/Microsoft.AspNet.IISPlatformHandler.xproj
@@ -0,0 +1,20 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+
+ abe53415-83ce-4af0-af67-e52160c7862b
+ Microsoft.AspNet.PlatformHandler
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+
+ 2.0
+
+
+
diff --git a/src/Microsoft.AspNet.IISPlatformHandler/NativeMethods.cs b/src/Microsoft.AspNet.IISPlatformHandler/NativeMethods.cs
new file mode 100644
index 0000000000..c2ebda0251
--- /dev/null
+++ b/src/Microsoft.AspNet.IISPlatformHandler/NativeMethods.cs
@@ -0,0 +1,24 @@
+// 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.Runtime.InteropServices;
+
+namespace Microsoft.AspNet.IISPlatformHandler
+{
+ internal class NativeMethods
+ {
+#if DNXCORE50
+ private const string api_ms_win_core_handle_LIB = "api-ms-win-core-handle-l1-1-0.dll";
+#else
+ private const string KERNEL32 = "kernel32.dll";
+#endif
+
+#if DNXCORE50
+ [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);
+ }
+}
diff --git a/src/Microsoft.AspNet.IISPlatformHandler/project.json b/src/Microsoft.AspNet.IISPlatformHandler/project.json
new file mode 100644
index 0000000000..1a916ca57d
--- /dev/null
+++ b/src/Microsoft.AspNet.IISPlatformHandler/project.json
@@ -0,0 +1,23 @@
+{
+ "version": "1.0.0-*",
+ "description": "ASP.NET 5 components for working with the IIS HttpPlatformHandler module.",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/aspnet/IISIntegration"
+ },
+ "dependencies": {
+ "Microsoft.AspNet.Http.Extensions": "1.0.0-*",
+ "Microsoft.Framework.SecurityHelper.Sources": {
+ "type": "build",
+ "version": "1.0.0-*"
+ }
+ },
+ "frameworks": {
+ "dnx451": { },
+ "dnxcore50": {
+ "dependencies": {
+ "System.Security.Principal.Windows": "4.0.0-beta-*"
+ }
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HelloWorldTest.cs b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HelloWorldTest.cs
new file mode 100644
index 0000000000..9d264e523b
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HelloWorldTest.cs
@@ -0,0 +1,97 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Server.Testing;
+using Microsoft.AspNet.Testing.xunit;
+using Microsoft.Framework.Logging;
+using Xunit;
+using Xunit.Sdk;
+
+namespace Microsoft.AspNet.IISPlatformHandler.FunctionalTests
+{
+ // Uses ports ranging 5061 - 5069.
+ public class HelloWorldTests
+ {
+ [ConditionalTheory]
+ [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
+ [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x86, "http://localhost:5061/", ServerType.Kestrel)]
+ [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x64, "http://localhost:5062/", ServerType.Kestrel)]
+ [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "http://localhost:5063/", ServerType.Kestrel)]
+ [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, "http://localhost:5064/", ServerType.Kestrel)]
+ public Task HelloWorld_IISExpress(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl, ServerType delegateServer)
+ {
+ return HelloWorld(ServerType.IISExpress, runtimeFlavor, architecture, applicationBaseUrl, delegateServer);
+ }
+
+ [ConditionalTheory]
+ [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
+ [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x86, "http://localhost:5065/", ServerType.Kestrel)]
+ [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x64, "http://localhost:5066/", ServerType.Kestrel)]
+ [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "http://localhost:5067/", ServerType.Kestrel)]
+ [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, "http://localhost:5068/", ServerType.Kestrel)]
+ public Task HelloWorld_IISExpress_NoSource(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl, ServerType delegateServer)
+ {
+ return HelloWorld(ServerType.IISExpress, runtimeFlavor, architecture, applicationBaseUrl, delegateServer, noSource: true);
+ }
+
+ [ConditionalTheory]
+ [SkipIfIISVariationsNotEnabled]
+ [OSSkipCondition(OperatingSystems.MacOSX | OperatingSystems.Linux)]
+ [SkipIfCurrentRuntimeIsCoreClr]
+ [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x64, "http://localhost:5069/", ServerType.Kestrel)]
+ [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "http://localhost:5070/", ServerType.Kestrel)]
+ public Task HelloWorld_IIS(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl, ServerType delegateServer)
+ {
+ return HelloWorld(ServerType.IIS, runtimeFlavor, architecture, applicationBaseUrl, delegateServer);
+ }
+
+ public async Task HelloWorld(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl, ServerType delegateServer, bool noSource = false)
+ {
+ var logger = new LoggerFactory()
+ .AddConsole()
+ .CreateLogger($"HelloWorld:{serverType}:{runtimeFlavor}:{architecture}:{delegateServer}");
+
+ using (logger.BeginScope("HelloWorldTest"))
+ {
+ var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture)
+ {
+ ApplicationBaseUriHint = applicationBaseUrl,
+ Command = delegateServer == ServerType.WebListener ? "web" : "kestrel",
+ PublishWithNoSource = noSource,
+ EnvironmentName = "HelloWorld", // Will pick the Start class named 'StartupHelloWorld',
+ ApplicationHostConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("Http.config") : null,
+ SiteName = "HttpTestSite", // This is configured in the Http.config
+ };
+
+ using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, logger))
+ {
+ var deploymentResult = deployer.Deploy();
+ var httpClientHandler = new HttpClientHandler();
+ var httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(deploymentResult.ApplicationBaseUri) };
+
+ // 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 httpClient.GetAsync(string.Empty);
+ }, logger, deploymentResult.HostShutdownToken);
+
+ var responseText = await response.Content.ReadAsStringAsync();
+ try
+ {
+ Assert.Equal("Hello World", responseText);
+ }
+ catch (XunitException)
+ {
+ logger.LogWarning(response.ToString());
+ logger.LogWarning(responseText);
+ throw;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Helpers.cs b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Helpers.cs
new file mode 100644
index 0000000000..f25190a14b
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Helpers.cs
@@ -0,0 +1,15 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.IO;
+
+namespace Microsoft.AspNet.IISPlatformHandler.FunctionalTests
+{
+ public class Helpers
+ {
+ public static string GetTestSitesPath()
+ {
+ return Path.GetFullPath(Path.Combine("..", "TestSites"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Http.config b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Http.config
new file mode 100644
index 0000000000..b1b08c8b1d
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Http.config
@@ -0,0 +1,1029 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Https.config b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Https.config
new file mode 100644
index 0000000000..489c8d6891
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Https.config
@@ -0,0 +1,1029 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HttpsTest.cs b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HttpsTest.cs
new file mode 100644
index 0000000000..71df50ee76
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/HttpsTest.cs
@@ -0,0 +1,186 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Net.Http;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Server.Testing;
+using Microsoft.AspNet.Testing.xunit;
+using Microsoft.Framework.Logging;
+using Xunit;
+using Xunit.Sdk;
+
+namespace Microsoft.AspNet.IISPlatformHandler.FunctionalTests
+{
+ // IisExpress preregisteres 44300-44399 ports.
+ public class HttpsTest
+ {
+ [ConditionalTheory]
+ [OSSkipCondition(OperatingSystems.MacOSX | OperatingSystems.Linux)]
+ [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "https://localhost:44399/")]
+ [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x64, "https://localhost:44398/")]
+ public Task Https_HelloWorld(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl)
+ {
+ return HttpsHelloWorld(ServerType.IISExpress, runtimeFlavor, architecture, applicationBaseUrl);
+ }
+
+ public async Task HttpsHelloWorld(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl)
+ {
+ var logger = new LoggerFactory()
+ .AddConsole()
+ .CreateLogger($"HttpsHelloWorld:{serverType}:{runtimeFlavor}:{architecture}");
+
+ using (logger.BeginScope("HttpsHelloWorldTest"))
+ {
+ var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture)
+ {
+ ApplicationBaseUriHint = applicationBaseUrl,
+ EnvironmentName = "HttpsHelloWorld", // Will pick the Start class named 'StartupHttpsHelloWorld',
+ ApplicationHostConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("Https.config") : null,
+ SiteName = "HttpsTestSite", // This is configured in the Https.config
+ };
+
+ using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, logger))
+ {
+ var deploymentResult = deployer.Deploy();
+ var handler = new WebRequestHandler();
+ handler.ServerCertificateValidationCallback = (a, b, c, d) => true;
+ var httpClient = new HttpClient(handler) { BaseAddress = new Uri(deploymentResult.ApplicationBaseUri) };
+
+ // 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 httpClient.GetAsync(string.Empty);
+ }, logger, deploymentResult.HostShutdownToken);
+
+ var responseText = await response.Content.ReadAsStringAsync();
+ try
+ {
+ Assert.Equal("https Hello World", responseText);
+ }
+ catch (XunitException)
+ {
+ logger.LogWarning(response.ToString());
+ logger.LogWarning(responseText);
+ throw;
+ }
+ }
+ }
+ }
+
+ [ConditionalTheory]
+ [OSSkipCondition(OperatingSystems.MacOSX | OperatingSystems.Linux)]
+ [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x64, "https://localhost:44397/")]
+ [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x86, "https://localhost:44396/")]
+ public Task Https_HelloWorld_NoClientCert(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl)
+ {
+ return HttpsHelloWorldCerts(ServerType.IISExpress, runtimeFlavor, architecture, applicationBaseUrl, sendClientCert: false);
+ }
+
+ [ConditionalTheory(Skip = "Manual test only, selecting a client cert is non-determanistic on different machines.")]
+ [OSSkipCondition(OperatingSystems.MacOSX | OperatingSystems.Linux)]
+ [InlineData(RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "https://localhost:44395/")]
+ [InlineData(RuntimeFlavor.Clr, RuntimeArchitecture.x64, "https://localhost:44394/")]
+ public Task Https_HelloWorld_ClientCert(RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl)
+ {
+ return HttpsHelloWorldCerts(ServerType.IISExpress, runtimeFlavor, architecture, applicationBaseUrl, sendClientCert: true);
+ }
+
+ public async Task HttpsHelloWorldCerts(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl, bool sendClientCert)
+ {
+ var logger = new LoggerFactory()
+ .AddConsole()
+ .CreateLogger($"HttpsHelloWorldCerts:{serverType}:{runtimeFlavor}:{architecture}");
+
+ using (logger.BeginScope("HttpsHelloWorldTest"))
+ {
+ var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture)
+ {
+ ApplicationBaseUriHint = applicationBaseUrl,
+ EnvironmentName = "HttpsHelloWorld", // Will pick the Start class named 'StartupHttpsHelloWorld',
+ ApplicationHostConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("Https.config") : null,
+ SiteName = "HttpsTestSite", // This is configured in the Https.config
+ };
+
+ using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, logger))
+ {
+ var deploymentResult = deployer.Deploy();
+ var handler = new WebRequestHandler();
+ handler.ServerCertificateValidationCallback = (a, b, c, d) => true;
+ handler.ClientCertificateOptions = ClientCertificateOption.Manual;
+ if (sendClientCert)
+ {
+ X509Certificate2 clientCert = FindClientCert();
+ Assert.NotNull(clientCert);
+ handler.ClientCertificates.Add(clientCert);
+ }
+ var httpClient = new HttpClient(handler) { BaseAddress = new Uri(deploymentResult.ApplicationBaseUri) };
+
+ // 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 httpClient.GetAsync("checkclientcert");
+ }, logger, deploymentResult.HostShutdownToken);
+
+ var responseText = await response.Content.ReadAsStringAsync();
+ try
+ {
+ if (sendClientCert)
+ {
+ Assert.Equal("https Hello World, has cert? True", responseText);
+ }
+ else
+ {
+ Assert.Equal("https Hello World, has cert? False", responseText);
+ }
+ }
+ catch (XunitException)
+ {
+ logger.LogWarning(response.ToString());
+ logger.LogWarning(responseText);
+ throw;
+ }
+ }
+ }
+ }
+
+ private X509Certificate2 FindClientCert()
+ {
+ var store = new X509Store();
+ store.Open(OpenFlags.ReadOnly);
+
+ foreach (var cert in store.Certificates)
+ {
+ bool isClientAuth = false;
+ bool isSmartCard = false;
+ foreach (var extension in cert.Extensions)
+ {
+ var eku = extension as X509EnhancedKeyUsageExtension;
+ if (eku != null)
+ {
+ foreach (var oid in eku.EnhancedKeyUsages)
+ {
+ if (oid.FriendlyName == "Client Authentication")
+ {
+ isClientAuth = true;
+ }
+ else if (oid.FriendlyName == "Smart Card Logon")
+ {
+ isSmartCard = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (isClientAuth && !isSmartCard)
+ {
+ return cert;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Microsoft.AspNet.IISPlatformHandler.FunctionalTests.xproj b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Microsoft.AspNet.IISPlatformHandler.FunctionalTests.xproj
new file mode 100644
index 0000000000..d131a7a65f
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Microsoft.AspNet.IISPlatformHandler.FunctionalTests.xproj
@@ -0,0 +1,21 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ a83a33d1-3d29-403c-9750-5811ac7b4025
+ Microsoft.AspNet.PlatformHandler.FunctionalTests
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+ 2.0
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentation.config b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentation.config
new file mode 100644
index 0000000000..8f43b98ced
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentation.config
@@ -0,0 +1,1041 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentationTest.cs b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentationTest.cs
new file mode 100644
index 0000000000..02aabf0d1f
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/NtlmAuthentationTest.cs
@@ -0,0 +1,115 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Server.Testing;
+using Microsoft.AspNet.Testing.xunit;
+using Microsoft.Framework.Logging;
+using Xunit;
+using Xunit.Sdk;
+
+namespace Microsoft.AspNet.IISPlatformHandler.FunctionalTests
+{
+ // Uses ports ranging 5050 - 5060.
+ public class NtlmAuthenticationTests
+ {
+ // TODO: The middleware needs to implement auth handlers.
+ [ConditionalTheory, Trait("ServerComparison.FunctionalTests", "ServerComparison.FunctionalTests")]
+ [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
+ [InlineData(ServerType.IISExpress, RuntimeFlavor.CoreClr, RuntimeArchitecture.x86, "http://localhost:5050/")]
+ [InlineData(ServerType.IISExpress, RuntimeFlavor.Clr, RuntimeArchitecture.x64, "http://localhost:5051/")]
+ public async Task NtlmAuthentication(ServerType serverType, RuntimeFlavor runtimeFlavor, RuntimeArchitecture architecture, string applicationBaseUrl)
+ {
+ var logger = new LoggerFactory()
+ .AddConsole()
+ .CreateLogger($"HttpsHelloWorld:{serverType}:{runtimeFlavor}:{architecture}");
+
+ using (logger.BeginScope("NtlmAuthenticationTest"))
+ {
+ var deploymentParameters = new DeploymentParameters(Helpers.GetTestSitesPath(), serverType, runtimeFlavor, architecture)
+ {
+ ApplicationBaseUriHint = applicationBaseUrl,
+ EnvironmentName = "NtlmAuthentication", // Will pick the Start class named 'StartupNtlmAuthentication'
+ ApplicationHostConfigTemplateContent = (serverType == ServerType.IISExpress) ? File.ReadAllText("NtlmAuthentation.config") : null,
+ SiteName = "NtlmAuthenticationTestSite", // This is configured in the NtlmAuthentication.config
+ };
+
+ using (var deployer = ApplicationDeployerFactory.Create(deploymentParameters, logger))
+ {
+ var deploymentResult = deployer.Deploy();
+ var httpClientHandler = new HttpClientHandler();
+ var httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(deploymentResult.ApplicationBaseUri) };
+
+ // 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 httpClient.GetAsync(string.Empty);
+ }, logger, deploymentResult.HostShutdownToken);
+
+ var responseText = await response.Content.ReadAsStringAsync();
+ try
+ {
+ // TODO: Currently we do not implement mixed auth.
+ // https://github.com/aspnet/IISIntegration/issues/1
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString());
+ Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString());
+
+ /*
+ Assert.Equal("Hello World", responseText);
+
+ responseText = await httpClient.GetStringAsync("/Anonymous");
+ Assert.Equal("Anonymous?True", responseText);
+
+ response = await httpClient.GetAsync("/Restricted");
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString());
+ Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString());
+
+ response = await httpClient.GetAsync("/RestrictedNTLM");
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ Assert.Contains("NTLM", response.Headers.WwwAuthenticate.ToString());
+ // Note we can't restrict a challenge to a specific auth type, the native auth modules always add themselves.
+ Assert.Contains("Negotiate", response.Headers.WwwAuthenticate.ToString());
+
+ response = await httpClient.GetAsync("/Forbidden");
+ Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
+ */
+ httpClientHandler = new HttpClientHandler() { UseDefaultCredentials = true };
+ httpClient = new HttpClient(httpClientHandler) { BaseAddress = new Uri(deploymentResult.ApplicationBaseUri) };
+
+ responseText = await httpClient.GetStringAsync("/Anonymous");
+ Assert.Equal("Anonymous?False", responseText);
+
+ /*
+ response = await httpClient.GetAsync("/AutoForbid");
+ Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
+
+ responseText = await httpClient.GetStringAsync("/Restricted");
+ Assert.Equal("Negotiate", responseText);
+
+ responseText = await httpClient.GetStringAsync("/RestrictedNegotiate");
+ Assert.Equal("Negotiate", responseText);
+
+ response = await httpClient.GetAsync("/RestrictedNTLM");
+ // This isn't a Forbidden because we authenticate with Negotiate and challenge for NTLM.
+ // Note we can't restrict a challenge to a specific auth type, the native auth modules always add themselves,
+ // so both Negotiate and NTLM get sent again.
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ */
+ }
+ catch (XunitException)
+ {
+ logger.LogWarning(response.ToString());
+ logger.LogWarning(responseText);
+ throw;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Properties/AssemblyInfo.cs b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..b1fa884228
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/Properties/AssemblyInfo.cs
@@ -0,0 +1,3 @@
+using Xunit;
+
+[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly)]
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/project.json b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/project.json
new file mode 100644
index 0000000000..9869ba4f72
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.FunctionalTests/project.json
@@ -0,0 +1,23 @@
+{
+ "compilationOptions": {
+ "warningsAsErrors": true
+ },
+ "commands": {
+ "test": "xunit.runner.aspnet"
+ },
+ "dependencies": {
+ "Microsoft.AspNet.Server.Testing": "1.0.0-*",
+ "Microsoft.Framework.Logging.Console": "1.0.0-*",
+ "xunit.runner.aspnet": "2.0.0-aspnet-*"
+ },
+ "frameworks": {
+ "dnx451": {
+ "frameworkAssemblies": {
+ "System.Data": "",
+ "System.Net.Http": "",
+ "System.Net.Http.WebRequest": "",
+ "System.Xml": ""
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.Tests/HttpPlatformHandlerMiddlewareTests.cs b/test/Microsoft.AspNet.IISPlatformHandler.Tests/HttpPlatformHandlerMiddlewareTests.cs
new file mode 100644
index 0000000000..d69fdd2298
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.Tests/HttpPlatformHandlerMiddlewareTests.cs
@@ -0,0 +1,80 @@
+// 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.Net.Http;
+using System.Threading.Tasks;
+using Microsoft.AspNet.Builder;
+using Microsoft.AspNet.TestHost;
+using Xunit;
+
+namespace Microsoft.AspNet.IISPlatformHandler
+{
+ public class HttpPlatformHandlerMiddlewareTests
+ {
+ [Fact]
+ public async Task XForwardedForOverrideChangesRemoteIp()
+ {
+ var assertsExecuted = false;
+
+ var server = TestServer.Create(app =>
+ {
+ app.UseIISPlatformHandler();
+ app.Run(context =>
+ {
+ Assert.Equal("11.111.111.11", context.Connection.RemoteIpAddress.ToString());
+ assertsExecuted = true;
+ return Task.FromResult(0);
+ });
+ });
+
+ var req = new HttpRequestMessage(HttpMethod.Get, "");
+ req.Headers.Add("X-Forwarded-For", "11.111.111.11");
+ await server.CreateClient().SendAsync(req);
+ Assert.True(assertsExecuted);
+ }
+
+ [Fact]
+ public async Task XForwardedForOverrideBadIpDoesntChangeRemoteIp()
+ {
+ var assertsExecuted = false;
+
+ var server = TestServer.Create(app =>
+ {
+ app.UseIISPlatformHandler();
+ app.Run(context =>
+ {
+ Assert.Null(context.Connection.RemoteIpAddress);
+ assertsExecuted = true;
+ return Task.FromResult(0);
+ });
+ });
+
+ var req = new HttpRequestMessage(HttpMethod.Get, "");
+ req.Headers.Add("X-Forwarded-For", "BAD-IP");
+ await server.CreateClient().SendAsync(req);
+ Assert.True(assertsExecuted);
+ }
+
+ [Fact]
+ public async Task XForwardedProtoOverrideChangesRequestProtocol()
+ {
+ var assertsExecuted = false;
+
+ var server = TestServer.Create(app =>
+ {
+ app.UseIISPlatformHandler();
+ app.Run(context =>
+ {
+ Assert.Equal("TestProtocol", context.Request.Scheme);
+ assertsExecuted = true;
+ return Task.FromResult(0);
+ });
+ });
+
+ var req = new HttpRequestMessage(HttpMethod.Get, "");
+ req.Headers.Add("X-Forwarded-Proto", "TestProtocol");
+ await server.CreateClient().SendAsync(req);
+ Assert.True(assertsExecuted);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.Tests/Microsoft.AspNet.IISPlatformHandler.Tests.xproj b/test/Microsoft.AspNet.IISPlatformHandler.Tests/Microsoft.AspNet.IISPlatformHandler.Tests.xproj
new file mode 100644
index 0000000000..e8cfa355aa
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.Tests/Microsoft.AspNet.IISPlatformHandler.Tests.xproj
@@ -0,0 +1,21 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ fbbbe015-1ce3-454b-9647-23f8073fb7ab
+ Microsoft.AspNet.PipelineHandler.Tests
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+ 2.0
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.IISPlatformHandler.Tests/project.json b/test/Microsoft.AspNet.IISPlatformHandler.Tests/project.json
new file mode 100644
index 0000000000..a5a8ecc4e0
--- /dev/null
+++ b/test/Microsoft.AspNet.IISPlatformHandler.Tests/project.json
@@ -0,0 +1,15 @@
+{
+ "version": "1.0.0-*",
+ "dependencies": {
+ "Microsoft.AspNet.IISPlatformHandler": "1.0.0-*",
+ "Microsoft.AspNet.TestHost": "1.0.0-*",
+ "xunit.runner.aspnet": "2.0.0-aspnet-*"
+ },
+ "commands": {
+ "test": "xunit.runner.aspnet"
+ },
+ "frameworks": {
+ "dnx451": { },
+ "dnxcore50": { }
+ }
+}
diff --git a/test/TestSites/Properties/launchSettings.json b/test/TestSites/Properties/launchSettings.json
new file mode 100644
index 0000000000..b111483280
--- /dev/null
+++ b/test/TestSites/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNET_ENV": "HelloWorld"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/TestSites/StartupHelloWorld.cs b/test/TestSites/StartupHelloWorld.cs
new file mode 100644
index 0000000000..f6f500ed50
--- /dev/null
+++ b/test/TestSites/StartupHelloWorld.cs
@@ -0,0 +1,22 @@
+// 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.AspNet.Builder;
+using Microsoft.AspNet.Http;
+using Microsoft.Framework.Logging;
+
+namespace TestSites
+{
+ public class StartupHelloWorld
+ {
+ public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
+ {
+ loggerFactory.AddConsole();
+ app.UseIISPlatformHandler();
+ app.Run(ctx =>
+ {
+ return ctx.Response.WriteAsync("Hello World");
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/TestSites/StartupHttpsHelloWorld.cs b/test/TestSites/StartupHttpsHelloWorld.cs
new file mode 100644
index 0000000000..77ad91157c
--- /dev/null
+++ b/test/TestSites/StartupHttpsHelloWorld.cs
@@ -0,0 +1,26 @@
+// 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.AspNet.Builder;
+using Microsoft.AspNet.Http;
+using Microsoft.Framework.Logging;
+
+namespace TestSites
+{
+ public class StartupHttpsHelloWorld
+ {
+ public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
+ {
+ loggerFactory.AddConsole();
+ app.UseIISPlatformHandler();
+ app.Run(ctx =>
+ {
+ if (ctx.Request.Path.Equals(new PathString("/checkclientcert")))
+ {
+ return ctx.Response.WriteAsync(ctx.Request.Scheme + " Hello World, has cert? " + (ctx.Connection.ClientCertificate != null));
+ }
+ return ctx.Response.WriteAsync(ctx.Request.Scheme + " Hello World");
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/TestSites/StartupNtlmAuthentication.cs b/test/TestSites/StartupNtlmAuthentication.cs
new file mode 100644
index 0000000000..ee396e0e16
--- /dev/null
+++ b/test/TestSites/StartupNtlmAuthentication.cs
@@ -0,0 +1,73 @@
+// 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.AspNet.Builder;
+using Microsoft.AspNet.Http;
+using Microsoft.Framework.Logging;
+
+namespace TestSites
+{
+ public class StartupNtlmAuthentication
+ {
+ public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
+ {
+ loggerFactory.AddConsole();
+ app.UseIISPlatformHandler();
+ app.Use((context, next) =>
+ {
+ if (context.Request.Path.Equals("/Anonymous"))
+ {
+ return context.Response.WriteAsync("Anonymous?" + !context.User.Identity.IsAuthenticated);
+ }
+
+ if (context.Request.Path.Equals("/Restricted"))
+ {
+ if (context.User.Identity.IsAuthenticated)
+ {
+ return context.Response.WriteAsync(context.User.Identity.AuthenticationType);
+ }
+ else
+ {
+ return context.Authentication.ChallengeAsync();
+ }
+ }
+
+ if (context.Request.Path.Equals("/Forbidden"))
+ {
+ return context.Authentication.ForbidAsync(string.Empty);
+ }
+
+ if (context.Request.Path.Equals("/AutoForbid"))
+ {
+ return context.Authentication.ChallengeAsync();
+ }
+
+ if (context.Request.Path.Equals("/RestrictedNegotiate"))
+ {
+ if (string.Equals("Negotiate", context.User.Identity.AuthenticationType, System.StringComparison.Ordinal))
+ {
+ return context.Response.WriteAsync("Negotiate");
+ }
+ else
+ {
+ return context.Authentication.ChallengeAsync("Negotiate");
+ }
+ }
+
+ if (context.Request.Path.Equals("/RestrictedNTLM"))
+ {
+ if (string.Equals("NTLM", context.User.Identity.AuthenticationType, System.StringComparison.Ordinal))
+ {
+ return context.Response.WriteAsync("NTLM");
+ }
+ else
+ {
+ return context.Authentication.ChallengeAsync("NTLM");
+ }
+ }
+
+ return context.Response.WriteAsync("Hello World");
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/TestSites/TestSites.xproj b/test/TestSites/TestSites.xproj
new file mode 100644
index 0000000000..db5463e044
--- /dev/null
+++ b/test/TestSites/TestSites.xproj
@@ -0,0 +1,19 @@
+
+
+
+ 14.0
+ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+
+
+ e27453ad-9ca0-49a2-94fa-96337d77677d
+ Sites
+ ..\..\artifacts\obj\$(MSBuildProjectName)
+ ..\..\artifacts\bin\$(MSBuildProjectName)\
+
+
+ 2.0
+ 11738
+
+
+
\ No newline at end of file
diff --git a/test/TestSites/project.json b/test/TestSites/project.json
new file mode 100644
index 0000000000..4d71b5e850
--- /dev/null
+++ b/test/TestSites/project.json
@@ -0,0 +1,39 @@
+{
+ "webroot": "wwwroot",
+ "version": "1.0.0-*",
+ "dependencies": {
+ "Microsoft.AspNet.IISPlatformHandler": "1.0.0-*",
+ "Microsoft.AspNet.Server.Kestrel": "1.0.0-*",
+ "Microsoft.AspNet.Server.WebListener": "1.0.0-*",
+ "Microsoft.AspNet.WebUtilities": "1.0.0-*",
+ "Microsoft.Framework.Configuration.EnvironmentVariables": "1.0.0-*",
+ "Microsoft.Framework.Configuration.Json": "1.0.0-*",
+ "Microsoft.Framework.Logging.Console": "1.0.0-*",
+ "xunit.runner.aspnet": "2.0.0-aspnet-*"
+ },
+ "frameworks": {
+ "dnx451": { },
+ "dnxcore50": {
+ "dependencies": {
+ "System.Net.Primitives": "4.0.11-beta-*",
+ "System.Runtime": "4.0.21-beta-*"
+ }
+ }
+ },
+ "commands": {
+ "web": "Microsoft.AspNet.Server.WebListener",
+ "kestrel": "Microsoft.AspNet.Server.Kestrel"
+ },
+ "publishExclude": [
+ "node_modules",
+ "bower_components",
+ "**.xproj",
+ "**.user",
+ "**.vspscc"
+ ],
+ "exclude": [
+ "wwwroot",
+ "node_modules",
+ "bower_components"
+ ]
+}