Reload HostFilter options on change #317
This commit is contained in:
parent
fc3ab083d2
commit
768cad8a8a
|
|
@ -14,6 +14,7 @@
|
|||
<MicrosoftAspNetCoreTestHostPackageVersion>2.1.0-preview3-32110</MicrosoftAspNetCoreTestHostPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationExtensionsPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsConfigurationExtensionsPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview3-32110</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ namespace HostFilteringSample
|
|||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
|
||||
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);
|
||||
})
|
||||
.UseKestrel()
|
||||
.UseStartup<Startup>();
|
||||
|
|
|
|||
|
|
@ -1,12 +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;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.HostFiltering;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace HostFilteringSample
|
||||
{
|
||||
|
|
@ -23,9 +26,22 @@ namespace HostFilteringSample
|
|||
{
|
||||
services.AddHostFiltering(options =>
|
||||
{
|
||||
// If this is excluded then it will fall back to the server's addresses
|
||||
options.AllowedHosts = Config.GetSection("AllowedHosts").Get<List<string>>();
|
||||
|
||||
});
|
||||
|
||||
// Fallback
|
||||
services.PostConfigure<HostFilteringOptions>(options =>
|
||||
{
|
||||
if (options.AllowedHosts == null || options.AllowedHosts.Count == 0)
|
||||
{
|
||||
// "AllowedHosts": "localhost;127.0.0.1;[::1]"
|
||||
var hosts = Config["AllowedHosts"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
// Fall back to "*" to disable.
|
||||
options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" });
|
||||
}
|
||||
});
|
||||
// Change notification
|
||||
services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>(new ConfigurationChangeTokenSource<HostFilteringOptions>(Config));
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"AllowedHosts": [ "localhost", "127.0.0.1", "[::1]" ]
|
||||
"AllowedHosts": "localhost;127.0.0.1;[::1]"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"AllowedHosts": [ "example.com", "localhost" ]
|
||||
"AllowedHosts": "example.com;localhost"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ namespace Microsoft.AspNetCore.HostFiltering
|
|||
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<HostFilteringMiddleware> _logger;
|
||||
private readonly HostFilteringOptions _options;
|
||||
private readonly IOptionsMonitor<HostFilteringOptions> _optionsMonitor;
|
||||
private HostFilteringOptions _options;
|
||||
private IList<StringSegment> _allowedHosts;
|
||||
private bool? _allowAnyNonEmptyHost;
|
||||
|
||||
|
|
@ -39,13 +40,21 @@ namespace Microsoft.AspNetCore.HostFiltering
|
|||
/// </summary>
|
||||
/// <param name="next"></param>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="optionsMonitor"></param>
|
||||
public HostFilteringMiddleware(RequestDelegate next, ILogger<HostFilteringMiddleware> logger,
|
||||
IOptions<HostFilteringOptions> options)
|
||||
IOptionsMonitor<HostFilteringOptions> optionsMonitor)
|
||||
{
|
||||
_next = next ?? throw new ArgumentNullException(nameof(next));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
|
||||
_optionsMonitor = optionsMonitor ?? throw new ArgumentNullException(nameof(optionsMonitor));
|
||||
_options = _optionsMonitor.CurrentValue;
|
||||
_optionsMonitor.OnChange(options =>
|
||||
{
|
||||
// Clear the cached settings so the next EnsureConfigured will re-evaluate.
|
||||
_options = options;
|
||||
_allowedHosts = new List<StringSegment>();
|
||||
_allowAnyNonEmptyHost = null;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -55,9 +64,9 @@ namespace Microsoft.AspNetCore.HostFiltering
|
|||
/// <returns></returns>
|
||||
public Task Invoke(HttpContext context)
|
||||
{
|
||||
EnsureConfigured();
|
||||
var allowedHosts = EnsureConfigured();
|
||||
|
||||
if (!CheckHost(context))
|
||||
if (!CheckHost(context, allowedHosts))
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
if (_options.IncludeFailureMessage)
|
||||
|
|
@ -72,19 +81,20 @@ namespace Microsoft.AspNetCore.HostFiltering
|
|||
return _next(context);
|
||||
}
|
||||
|
||||
private void EnsureConfigured()
|
||||
private IList<StringSegment> EnsureConfigured()
|
||||
{
|
||||
if (_allowAnyNonEmptyHost == true || _allowedHosts?.Count > 0)
|
||||
{
|
||||
return;
|
||||
return _allowedHosts;
|
||||
}
|
||||
|
||||
var allowedHosts = new List<StringSegment>();
|
||||
if (_options.AllowedHosts?.Count > 0 && !TryProcessHosts(_options.AllowedHosts, allowedHosts))
|
||||
{
|
||||
_logger.LogDebug("Wildcard detected, all requests with hosts will be allowed.");
|
||||
_allowedHosts = allowedHosts;
|
||||
_allowAnyNonEmptyHost = true;
|
||||
return;
|
||||
return _allowedHosts;
|
||||
}
|
||||
|
||||
if (allowedHosts.Count == 0)
|
||||
|
|
@ -94,6 +104,7 @@ namespace Microsoft.AspNetCore.HostFiltering
|
|||
|
||||
_logger.LogDebug("Allowed hosts: " + string.Join("; ", allowedHosts));
|
||||
_allowedHosts = allowedHosts;
|
||||
return _allowedHosts;
|
||||
}
|
||||
|
||||
// returns false if any wildcards were found
|
||||
|
|
@ -127,7 +138,7 @@ namespace Microsoft.AspNetCore.HostFiltering
|
|||
}
|
||||
|
||||
// This does not duplicate format validations that are expected to be performed by the host.
|
||||
private bool CheckHost(HttpContext context)
|
||||
private bool CheckHost(HttpContext context, IList<StringSegment> allowedHosts)
|
||||
{
|
||||
var host = new StringSegment(context.Request.Headers[HeaderNames.Host].ToString()).Trim();
|
||||
|
||||
|
|
@ -153,7 +164,7 @@ namespace Microsoft.AspNetCore.HostFiltering
|
|||
return true;
|
||||
}
|
||||
|
||||
if (HostString.MatchesAny(host, _allowedHosts))
|
||||
if (HostString.MatchesAny(host, allowedHosts))
|
||||
{
|
||||
_logger.LogTrace($"The host '{host}' matches an allowed host.");
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -2,10 +2,14 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
|
@ -179,5 +183,58 @@ namespace Microsoft.AspNetCore.HostFiltering
|
|||
var response = await server.CreateRequest("/").GetAsync();
|
||||
Assert.Equal(400, (int)response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SupportsDynamicOptionsReload()
|
||||
{
|
||||
var config = new ConfigurationBuilder().Add(new ReloadableMemorySource()).Build();
|
||||
config["AllowedHosts"] = "localhost";
|
||||
var currentHost = "otherHost";
|
||||
|
||||
var builder = new WebHostBuilder()
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddHostFiltering(options =>
|
||||
{
|
||||
options.AllowedHosts = new[] { config["AllowedHosts"] };
|
||||
});
|
||||
services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>(new ConfigurationChangeTokenSource<HostFilteringOptions>(config));
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.Use((ctx, next) =>
|
||||
{
|
||||
ctx.Request.Headers[HeaderNames.Host] = currentHost;
|
||||
return next();
|
||||
});
|
||||
app.UseHostFiltering();
|
||||
app.Run(c => Task.CompletedTask);
|
||||
});
|
||||
var server = new TestServer(builder);
|
||||
var response = await server.CreateRequest("/").GetAsync();
|
||||
Assert.Equal(400, (int)response.StatusCode);
|
||||
|
||||
config["AllowedHosts"] = "otherHost";
|
||||
|
||||
response = await server.CreateRequest("/").GetAsync();
|
||||
Assert.Equal(200, (int)response.StatusCode);
|
||||
}
|
||||
|
||||
private class ReloadableMemorySource : IConfigurationSource
|
||||
{
|
||||
public IConfigurationProvider Build(IConfigurationBuilder builder)
|
||||
{
|
||||
return new ReloadableMemoryProvider();
|
||||
}
|
||||
}
|
||||
|
||||
internal class ReloadableMemoryProvider : ConfigurationProvider
|
||||
{
|
||||
public override void Set(string key, string value)
|
||||
{
|
||||
base.Set(key, value);
|
||||
OnReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.HostFiltering\Microsoft.AspNetCore.HostFiltering.csproj" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="$(MicrosoftExtensionsConfigurationExtensionsPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
Loading…
Reference in New Issue