124 lines
5.1 KiB
C#
124 lines
5.1 KiB
C#
// 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.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Authentication;
|
|
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Http.Features;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using Microsoft.Extensions.Primitives;
|
|
|
|
namespace Microsoft.AspNetCore.Server.IISIntegration
|
|
{
|
|
public class IISMiddleware
|
|
{
|
|
private const string MSAspNetCoreClientCert = "MS-ASPNETCORE-CLIENTCERT";
|
|
private const string MSAspNetCoreToken = "MS-ASPNETCORE-TOKEN";
|
|
private const string MSAspNetCoreEvent = "MS-ASPNETCORE-EVENT";
|
|
private const string ANCMShutdownEventHeaderValue = "shutdown";
|
|
private static readonly PathString ANCMRequestPath = new PathString("/iisintegration");
|
|
|
|
private readonly RequestDelegate _next;
|
|
private readonly IISOptions _options;
|
|
private readonly ILogger _logger;
|
|
private readonly string _pairingToken;
|
|
private readonly IApplicationLifetime _applicationLifetime;
|
|
|
|
public IISMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions<IISOptions> options, string pairingToken, IAuthenticationSchemeProvider authentication, IApplicationLifetime applicationLifetime)
|
|
{
|
|
if (next == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(next));
|
|
}
|
|
if (loggerFactory == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(loggerFactory));
|
|
}
|
|
if (options == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(options));
|
|
}
|
|
if (applicationLifetime == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(applicationLifetime));
|
|
}
|
|
if (string.IsNullOrEmpty(pairingToken))
|
|
{
|
|
throw new ArgumentException("Missing or empty pairing token.");
|
|
}
|
|
|
|
_next = next;
|
|
_options = options.Value;
|
|
|
|
if (_options.ForwardWindowsAuthentication)
|
|
{
|
|
authentication.AddScheme(new AuthenticationScheme(IISDefaults.AuthenticationScheme, displayName: null, handlerType: typeof(AuthenticationHandler)));
|
|
}
|
|
|
|
_pairingToken = pairingToken;
|
|
_applicationLifetime = applicationLifetime;
|
|
_logger = loggerFactory.CreateLogger<IISMiddleware>();
|
|
}
|
|
|
|
public async Task Invoke(HttpContext httpContext)
|
|
{
|
|
if (!string.Equals(_pairingToken, httpContext.Request.Headers[MSAspNetCoreToken], StringComparison.Ordinal))
|
|
{
|
|
_logger.LogError($"'{MSAspNetCoreToken}' does not match the expected pairing token '{_pairingToken}', request rejected.");
|
|
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
|
return;
|
|
}
|
|
|
|
// Handle shutdown from ANCM
|
|
if (HttpMethods.IsPost(httpContext.Request.Method) &&
|
|
httpContext.Request.Path.Equals(ANCMRequestPath) &&
|
|
string.Equals(ANCMShutdownEventHeaderValue, httpContext.Request.Headers[MSAspNetCoreEvent], StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Execute shutdown task on background thread without waiting for completion
|
|
var shutdownTask = Task.Run(() => _applicationLifetime.StopApplication());
|
|
httpContext.Response.StatusCode = StatusCodes.Status202Accepted;
|
|
return;
|
|
}
|
|
|
|
if (Debugger.IsAttached && string.Equals("DEBUG", httpContext.Request.Method, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// The Visual Studio debugger tooling sends a DEBUG request to make IIS & AspNetCoreModule launch the process
|
|
// so the debugger can attach. Filter out this request from the app.
|
|
return;
|
|
}
|
|
|
|
var bodySizeFeature = httpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
|
|
if (bodySizeFeature != null && !bodySizeFeature.IsReadOnly)
|
|
{
|
|
// IIS already limits this, no need to do it twice.
|
|
bodySizeFeature.MaxRequestBodySize = null;
|
|
}
|
|
|
|
if (_options.ForwardClientCertificate)
|
|
{
|
|
var header = httpContext.Request.Headers[MSAspNetCoreClientCert];
|
|
if (!StringValues.IsNullOrEmpty(header))
|
|
{
|
|
httpContext.Features.Set<ITlsConnectionFeature>(new ForwardedTlsConnectionFeature(_logger, header));
|
|
}
|
|
}
|
|
|
|
if (_options.ForwardWindowsAuthentication)
|
|
{
|
|
var result = await httpContext.AuthenticateAsync(IISDefaults.AuthenticationScheme);
|
|
if (result.Succeeded)
|
|
{
|
|
httpContext.User = result.Principal;
|
|
}
|
|
}
|
|
|
|
await _next(httpContext);
|
|
}
|
|
}
|
|
}
|