#95 Add the IISMiddleware via a IStartupFilter.
This commit is contained in:
parent
46eef2eafb
commit
9e6dc5b2da
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace TestSites
|
|||
{
|
||||
var host = new WebHostBuilder()
|
||||
.UseDefaultConfiguration(args)
|
||||
.UseIISUrl()
|
||||
.UseIIS()
|
||||
.UseStartup("TestSites")
|
||||
.UseServer("Microsoft.AspNetCore.Server.Kestrel")
|
||||
.Build();
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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")))
|
||||
|
|
|
|||
|
|
@ -37,8 +37,6 @@ namespace TestSites
|
|||
}
|
||||
});
|
||||
|
||||
app.UseIIS();
|
||||
|
||||
app.Use((context, next) =>
|
||||
{
|
||||
if (context.Request.Path.Equals("/Anonymous"))
|
||||
|
|
|
|||
Loading…
Reference in New Issue