#95 Add the IISMiddleware via a IStartupFilter.

This commit is contained in:
Chris R 2016-03-14 09:59:49 -07:00
parent 46eef2eafb
commit 9e6dc5b2da
11 changed files with 139 additions and 124 deletions

View File

@ -14,8 +14,6 @@ namespace IISSample
loggerfactory.AddConsole(LogLevel.Debug);
var logger = loggerfactory.CreateLogger("Requests");
app.UseIIS();
app.Run(async (context) =>
{
@ -49,7 +47,7 @@ namespace IISSample
var host = new WebHostBuilder()
.UseDefaultConfiguration(args)
.UseServer("Microsoft.AspNetCore.Server.Kestrel")
.UseIISUrl()
.UseIIS()
.UseStartup<Startup>()
.Build();

View File

@ -1,38 +0,0 @@
// 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;
namespace Microsoft.AspNetCore.Hosting
{
public static class IISAddressExtensions
{
// This is defined by IIS's AspNetCoreModule.
private static readonly string ServerPort = "ASPNETCORE_PORT";
private static readonly string ServerPath = "ASPNETCORE_APPL_PATH";
/// <summary>
/// Configures the port and base path the server should listen on when running behind AspNetCoreModule.
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static IWebHostBuilder UseIISUrl(this IWebHostBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
var port = Environment.GetEnvironmentVariable(ServerPort);
var path = Environment.GetEnvironmentVariable(ServerPath);
if (!string.IsNullOrEmpty(port))
{
var address = "http://localhost:" + port + path;
app.UseSetting(WebHostDefaults.ServerUrlsKey, address);
}
return app;
}
}
}

View File

@ -21,15 +21,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{
private const string MSAspNetCoreWinAuthToken = "MS-ASPNETCORE-WINAUTHTOKEN";
private const string MSAspNetCoreClientCert = "MS-ASPNETCORE-CLIENTCERT";
private const string AspNetCoreToken = "ASPNETCORE_TOKEN";
private const string MSAspNetCoreToken = "MS-ASPNETCORE-TOKEN";
private readonly RequestDelegate _next;
private readonly IISOptions _options;
private readonly ILogger _logger;
private readonly string _platformToken;
private readonly string _pairingToken;
public IISMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions<IISOptions> options)
public IISMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions<IISOptions> options, string pairingToken)
{
if (next == null)
{
@ -43,24 +42,22 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
{
throw new ArgumentNullException(nameof(options));
}
if (string.IsNullOrEmpty(pairingToken))
{
throw new ArgumentException("Missing or empty pairing token.");
}
_next = next;
_options = options.Value;
_pairingToken = pairingToken;
_logger = loggerFactory.CreateLogger<IISMiddleware>();
_platformToken = Environment.GetEnvironmentVariable(AspNetCoreToken);
if (string.IsNullOrEmpty(_platformToken))
{
_logger.LogInformation($"{AspNetCoreToken} not detected, {nameof(IISMiddleware)} will be skipped.");
}
}
public async Task Invoke(HttpContext httpContext)
{
if (string.IsNullOrEmpty(_platformToken)
|| !string.Equals(_platformToken, httpContext.Request.Headers[MSAspNetCoreToken], StringComparison.Ordinal))
if (!string.Equals(_pairingToken, httpContext.Request.Headers[MSAspNetCoreToken], StringComparison.Ordinal))
{
_logger.LogTrace($"{MSAspNetCoreToken} not detected, skipping {nameof(IISMiddleware)}.");
_logger.LogTrace($"'{MSAspNetCoreToken}' does not match the expected pairing token '{_pairingToken}', skipping {nameof(IISMiddleware)}.");
await _next(httpContext);
return;
}

View File

@ -1,49 +0,0 @@
// 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 Microsoft.AspNetCore.Server.IISIntegration;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Builder
{
public static class IISMiddlewareExtensions
{
/// <summary>
/// Adds middleware for interacting with the IIS AspNetCoreModule reverse proxy module.
/// This will handle forwarded Windows Authentication, client certificates, etc..
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static IApplicationBuilder UseIIS(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
return app.UseMiddleware<IISMiddleware>();
}
/// <summary>
/// Adds middleware for interacting with the IIS AspNetCoreModule reverse proxy module.
/// This will handle forwarded Windows Authentication, client certificates, etc..
/// </summary>
/// <param name="app"></param>
/// <param name="options"></param>
/// <returns></returns>
public static IApplicationBuilder UseIIS(this IApplicationBuilder app, IISOptions options)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
return app.UseMiddleware<IISMiddleware>(Options.Create(options));
}
}
}

View File

@ -0,0 +1,28 @@
// 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 Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
namespace Microsoft.AspNetCore.Server.IISIntegration
{
internal class IISSetupFilter : IStartupFilter
{
private string _pairingToken;
internal IISSetupFilter(string pairingToken)
{
_pairingToken = pairingToken;
}
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseMiddleware<IISMiddleware>(_pairingToken);
next(app);
};
}
}
}

View File

@ -0,0 +1,47 @@
// 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 Microsoft.AspNetCore.Server.IISIntegration;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Hosting
{
public static class IISWebHostExtensions
{
// These are defined as ASPNETCORE_ environment variables by IIS's AspNetCoreModule.
private static readonly string ServerPort = "PORT";
private static readonly string ServerPath = "APPL_PATH";
private static readonly string PairingToken = "TOKEN";
/// <summary>
/// Configures the port and base path the server should listen on when running behind AspNetCoreModule.
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static IWebHostBuilder UseIIS(this IWebHostBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
var port = app.GetSetting(ServerPort);
var path = app.GetSetting(ServerPath);
var pairingToken = app.GetSetting(PairingToken);
if (!string.IsNullOrEmpty(port) && !string.IsNullOrEmpty(path) && !string.IsNullOrEmpty(pairingToken))
{
var address = "http://localhost:" + port + path;
app.UseSetting(WebHostDefaults.ServerUrlsKey, address);
app.ConfigureServices(services =>
{
services.AddSingleton<IStartupFilter>(new IISSetupFilter(pairingToken));
});
}
return app;
}
}
}

View File

@ -8,6 +8,7 @@ using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Microsoft.AspNetCore.Server.IISIntegration
@ -20,6 +21,37 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
var assertsExecuted = false;
var builder = new WebHostBuilder()
.UseSetting("PORT", "12345")
.UseSetting("APPL_PATH", "/")
.UseIIS()
.Configure(app =>
{
app.Run(context =>
{
var auth = context.Features.Get<IHttpAuthenticationFeature>();
Assert.Null(auth);
assertsExecuted = true;
return Task.FromResult(0);
});
});
var server = new TestServer(builder);
var req = new HttpRequestMessage(HttpMethod.Get, "");
req.Headers.TryAddWithoutValidation("MS-ASPNETCORE-TOKEN", "TestToken");
await server.CreateClient().SendAsync(req);
Assert.True(assertsExecuted);
}
[Fact]
public async Task MiddlewareSkippedIfTokenHeaderIsMissing()
{
var assertsExecuted = false;
var builder = new WebHostBuilder()
.UseSetting("TOKEN", "TestToken")
.UseSetting("PORT", "12345")
.UseSetting("APPL_PATH", "/")
.UseIIS()
.Configure(app =>
{
app.Run(context =>
@ -43,15 +75,12 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
var assertsExecuted = false;
var builder = new WebHostBuilder()
.UseSetting("TOKEN", "TestToken")
.UseSetting("PORT", "12345")
.UseSetting("APPL_PATH", "/")
.UseIIS()
.Configure(app =>
{
Environment.SetEnvironmentVariable("ASPNETCORE_TOKEN", "TestToken");
app.Use((context, next) =>
{
context.Request.Headers["MS-ASPNETCORE-TOKEN"] = "TestToken";
return next();
});
app.UseIIS();
app.Run(context =>
{
var auth = context.Features.Get<IHttpAuthenticationFeature>();
@ -64,7 +93,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
var server = new TestServer(builder);
var req = new HttpRequestMessage(HttpMethod.Get, "");
req.Headers.TryAddWithoutValidation("MS-ASPNETCORE-TOKEN", "TestToken");
await server.CreateClient().SendAsync(req);
Assert.True(assertsExecuted);
}
@ -75,18 +106,19 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
var assertsExecuted = false;
var builder = new WebHostBuilder()
.UseSetting("TOKEN", "TestToken")
.UseSetting("PORT", "12345")
.UseSetting("APPL_PATH", "/")
.UseIIS()
.ConfigureServices(services =>
{
services.Configure<IISOptions>(options =>
{
options.ForwardWindowsAuthentication = false;
});
})
.Configure(app =>
{
Environment.SetEnvironmentVariable("ASPNETCORE_TOKEN", "TestToken");
app.Use((context, next) =>
{
context.Request.Headers["MS-ASPNETCORE-TOKEN"] = "TestToken";
return next();
});
app.UseIIS(new IISOptions
{
ForwardWindowsAuthentication = false
});
app.Run(context =>
{
var auth = context.Features.Get<IHttpAuthenticationFeature>();
@ -98,7 +130,9 @@ namespace Microsoft.AspNetCore.Server.IISIntegration
var server = new TestServer(builder);
var req = new HttpRequestMessage(HttpMethod.Get, "");
req.Headers.TryAddWithoutValidation("MS-ASPNETCORE-TOKEN", "TestToken");
await server.CreateClient().SendAsync(req);
Assert.True(assertsExecuted);
}
}

View File

@ -11,7 +11,7 @@ namespace TestSites
{
var host = new WebHostBuilder()
.UseDefaultConfiguration(args)
.UseIISUrl()
.UseIIS()
.UseStartup("TestSites")
.UseServer("Microsoft.AspNetCore.Server.Kestrel")
.Build();

View File

@ -12,7 +12,7 @@ namespace TestSites
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
app.UseIIS();
app.Run(ctx =>
{
if (ctx.Request.Path.Value.StartsWith("/Path"))

View File

@ -12,7 +12,7 @@ namespace TestSites
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
app.UseIIS();
app.Run(ctx =>
{
if (ctx.Request.Path.Equals(new PathString("/checkclientcert")))

View File

@ -37,8 +37,6 @@ namespace TestSites
}
});
app.UseIIS();
app.Use((context, next) =>
{
if (context.Request.Path.Equals("/Anonymous"))