diff --git a/build/dependencies.props b/build/dependencies.props
index 229023f5d0..853f6f2f0e 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -3,23 +3,25 @@
$(MSBuildAllProjects);$(MSBuildThisFileFullPath)
- 2.1.0-preview1-15626
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
- 2.1.0-preview1-27849
+ 2.1.0-preview1-15638
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
+ 2.1.0-preview1-27855
2.0.0
2.1.0-preview1-26016-05
- 2.1.0-preview1-27849
+ 2.1.0-preview1-27855
15.3.0
4.7.49
0.8.0
diff --git a/korebuild-lock.txt b/korebuild-lock.txt
index 8d52a6128c..2e540bdffd 100644
--- a/korebuild-lock.txt
+++ b/korebuild-lock.txt
@@ -1,2 +1,2 @@
-version:2.1.0-preview1-15626
-commithash:fd6410e9c90c428bc01238372303ad09cb9ec889
+version:2.1.0-preview1-15638
+commithash:1d3a0c725dc6b8ae6b0e47800fd6b4d8f8b8d545
diff --git a/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionBuilderExtensions.cs b/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionBuilderExtensions.cs
index d6b827caab..600e29fead 100644
--- a/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionBuilderExtensions.cs
+++ b/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionBuilderExtensions.cs
@@ -2,8 +2,8 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.HttpsPolicy;
-using Microsoft.AspNetCore.Rewrite;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@@ -32,27 +32,7 @@ namespace Microsoft.AspNetCore.Builder
var options = app.ApplicationServices.GetRequiredService>().Value;
- // The tls port set in options will have priority over the one in configuration.
- var httpsPort = options.HttpsPort;
- if (httpsPort == null)
- {
- // Only read configuration if there is no httpsPort
- var config = app.ApplicationServices.GetRequiredService();
- var configHttpsPort = config["HTTPS_PORT"];
- // If the string isn't empty, try to parse it.
- if (!string.IsNullOrEmpty(configHttpsPort)
- && int.TryParse(configHttpsPort, out var intHttpsPort))
- {
- httpsPort = intHttpsPort;
- }
- }
-
- var rewriteOptions = new RewriteOptions();
- rewriteOptions.AddRedirectToHttps(
- options.RedirectStatusCode,
- httpsPort);
-
- app.UseRewriter(rewriteOptions);
+ app.UseMiddleware(app.ServerFeatures.Get());
return app;
}
diff --git a/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionMiddleware.cs b/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionMiddleware.cs
new file mode 100644
index 0000000000..f500bbb2bc
--- /dev/null
+++ b/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionMiddleware.cs
@@ -0,0 +1,124 @@
+// 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.Threading.Tasks;
+using Microsoft.AspNetCore.Hosting.Server.Features;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Extensions;
+using Microsoft.AspNetCore.Http.Internal;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Options;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.HttpsPolicy
+{
+ public class HttpsRedirectionMiddleware
+ {
+ private readonly RequestDelegate _next;
+ private int? _httpsPort;
+ private readonly int _statusCode;
+
+ private readonly IServerAddressesFeature _serverAddressesFeature;
+ private readonly IConfiguration _config;
+
+ ///
+ /// Initializes the HttpsRedirectionMiddleware
+ ///
+ ///
+ /// The
+ ///
+ ///
+ public HttpsRedirectionMiddleware(RequestDelegate next, IServerAddressesFeature serverAddressesFeature, IOptions options, IConfiguration config)
+ {
+ if (options == null)
+ {
+ throw new ArgumentNullException(nameof(options));
+ }
+ _config = config ?? throw new ArgumentException(nameof(config));
+ _next = next ?? throw new ArgumentNullException(nameof(next));
+ _serverAddressesFeature = serverAddressesFeature ?? throw new ArgumentNullException(nameof(serverAddressesFeature));
+
+ var httpsRedirectionOptions = options.Value;
+ _httpsPort = httpsRedirectionOptions.HttpsPort;
+ _statusCode = httpsRedirectionOptions.RedirectStatusCode;
+ }
+
+ ///
+ /// Invokes the HttpsRedirectionMiddleware
+ ///
+ ///
+ ///
+ public Task Invoke(HttpContext context)
+ {
+ if (context.Request.IsHttps)
+ {
+ return _next(context);
+ }
+
+ if (!_httpsPort.HasValue)
+ {
+ CheckForHttpsPorts();
+ }
+
+ var host = context.Request.Host;
+ if (_httpsPort != 443)
+ {
+ host = new HostString(host.Host, _httpsPort.Value);
+ }
+ else
+ {
+ host = new HostString(host.Host);
+ }
+
+ var request = context.Request;
+ var redirectUrl = UriHelper.BuildAbsolute(
+ "https",
+ host,
+ request.PathBase,
+ request.Path,
+ request.QueryString);
+
+ context.Response.StatusCode = _statusCode;
+ context.Response.Headers[HeaderNames.Location] = redirectUrl;
+
+ return Task.CompletedTask;
+ }
+
+ private void CheckForHttpsPorts()
+ {
+ // The IServerAddressesFeature will not be ready until the middleware is Invoked,
+ // Order for finding the HTTPS port:
+ // 1. Set in the HttpsRedirectionOptions
+ // 2. HTTPS_PORT environment variable
+ // 3. IServerAddressesFeature
+ // 4. 443 (or not set)
+
+ _httpsPort = _config.GetValue("HTTPS_PORT");
+ if (_httpsPort.HasValue)
+ {
+ return;
+ }
+
+ int? httpsPort = null;
+ foreach (var address in _serverAddressesFeature.Addresses)
+ {
+ var bindingAddress = BindingAddress.Parse(address);
+ if (bindingAddress.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
+ {
+ // If we find multiple different https ports specified, throw
+ if (httpsPort.HasValue && httpsPort != bindingAddress.Port)
+ {
+ throw new ArgumentException("Cannot determine the https port from IServerAddressesFeature, multiple values were found. " +
+ "Please set the desired port explicitly on HttpsRedirectionOptions.HttpsPort.");
+ }
+ else
+ {
+ httpsPort = bindingAddress.Port;
+ }
+ }
+ }
+ _httpsPort = httpsPort ?? 443;
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionOptions.cs b/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionOptions.cs
index 238f2eb484..52f2b09def 100644
--- a/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionOptions.cs
+++ b/src/Microsoft.AspNetCore.HttpsPolicy/HttpsRedirectionOptions.cs
@@ -16,10 +16,13 @@ namespace Microsoft.AspNetCore.HttpsPolicy
public int RedirectStatusCode { get; set; } = StatusCodes.Status302Found;
///
- /// The TLS port to be added to the redirected URL.
+ /// The HTTPS port to be added to the redirected URL.
///
///
- /// Defaults to 443 if not provided.
+ /// If the HttpsPort is not set, we will try to get the HttpsPort from the following:
+ /// 1. HTTPS_PORT environment variable
+ /// 2. IServerAddressesFeature
+ /// 3. 443 (or not set)
///
public int? HttpsPort { get; set; }
}
diff --git a/src/Microsoft.AspNetCore.HttpsPolicy/Microsoft.AspNetCore.HttpsPolicy.csproj b/src/Microsoft.AspNetCore.HttpsPolicy/Microsoft.AspNetCore.HttpsPolicy.csproj
index b68d33b7dc..ee7e611a47 100644
--- a/src/Microsoft.AspNetCore.HttpsPolicy/Microsoft.AspNetCore.HttpsPolicy.csproj
+++ b/src/Microsoft.AspNetCore.HttpsPolicy/Microsoft.AspNetCore.HttpsPolicy.csproj
@@ -11,6 +11,10 @@
-
+
+
+
+
+
diff --git a/test/Microsoft.AspNetCore.HttpsPolicy.Tests/HttpsPolicyTests.cs b/test/Microsoft.AspNetCore.HttpsPolicy.Tests/HttpsPolicyTests.cs
index 49a3d0a29a..aa874fb8cd 100644
--- a/test/Microsoft.AspNetCore.HttpsPolicy.Tests/HttpsPolicyTests.cs
+++ b/test/Microsoft.AspNetCore.HttpsPolicy.Tests/HttpsPolicyTests.cs
@@ -8,7 +8,9 @@ using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;
@@ -55,7 +57,9 @@ namespace Microsoft.AspNetCore.HttpsPolicy.Tests
});
});
- var server = new TestServer(builder);
+ var featureCollection = new FeatureCollection();
+ featureCollection.Set(new ServerAddressesFeature());
+ var server = new TestServer(builder, featureCollection);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "");
diff --git a/test/Microsoft.AspNetCore.HttpsPolicy.Tests/HttpsRedirectionMiddlewareTests.cs b/test/Microsoft.AspNetCore.HttpsPolicy.Tests/HttpsRedirectionMiddlewareTests.cs
index aabba1324e..14f7c5444f 100644
--- a/test/Microsoft.AspNetCore.HttpsPolicy.Tests/HttpsRedirectionMiddlewareTests.cs
+++ b/test/Microsoft.AspNetCore.HttpsPolicy.Tests/HttpsRedirectionMiddlewareTests.cs
@@ -8,7 +8,9 @@ using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;
@@ -34,7 +36,9 @@ namespace Microsoft.AspNetCore.HttpsPolicy.Tests
});
});
- var server = new TestServer(builder);
+ var featureCollection = new FeatureCollection();
+ featureCollection.Set(new ServerAddressesFeature());
+ var server = new TestServer(builder, featureCollection);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "");
@@ -73,7 +77,9 @@ namespace Microsoft.AspNetCore.HttpsPolicy.Tests
});
});
- var server = new TestServer(builder);
+ var featureCollection = new FeatureCollection();
+ featureCollection.Set(new ServerAddressesFeature());
+ var server = new TestServer(builder, featureCollection);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "");
@@ -111,7 +117,9 @@ namespace Microsoft.AspNetCore.HttpsPolicy.Tests
});
});
- var server = new TestServer(builder);
+ var featureCollection = new FeatureCollection();
+ featureCollection.Set(new ServerAddressesFeature());
+ var server = new TestServer(builder, featureCollection);
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "");
@@ -123,13 +131,17 @@ namespace Microsoft.AspNetCore.HttpsPolicy.Tests
}
[Theory]
- [InlineData(null, null, "https://localhost/")]
- [InlineData(null, "5000", "https://localhost:5000/")]
- [InlineData(null, "443", "https://localhost/")]
- [InlineData(443, "5000", "https://localhost/")]
- [InlineData(4000, "5000", "https://localhost:4000/")]
- [InlineData(5000, null, "https://localhost:5000/")]
- public async Task SetHttpsPortEnvironmentVariable_ReturnsCorrectStatusCodeOnResponse(int? optionsHttpsPort, string configHttpsPort, string expectedUrl)
+ [InlineData(null, null, null, "https://localhost/")]
+ [InlineData(null, null, "https://localhost:4444/", "https://localhost:4444/")]
+ [InlineData(null, null, "https://localhost:443/", "https://localhost/")]
+ [InlineData(null, null, "http://localhost:5044/", "https://localhost/")]
+ [InlineData(null, null, "https://localhost/", "https://localhost/")]
+ [InlineData(null, "5000", "https://localhost:4444/", "https://localhost:5000/")]
+ [InlineData(null, "443", "https://localhost:4444/", "https://localhost/")]
+ [InlineData(443, "5000", "https://localhost:4444/", "https://localhost/")]
+ [InlineData(4000, "5000", "https://localhost:4444/", "https://localhost:4000/")]
+ [InlineData(5000, null, "https://localhost:4444/", "https://localhost:5000/")]
+ public async Task SetHttpsPortEnvironmentVariableAndServerFeature_ReturnsCorrectStatusCodeOnResponse(int? optionsHttpsPort, string configHttpsPort, string serverAddressFeatureUrl, string expectedUrl)
{
var builder = new WebHostBuilder()
.ConfigureServices(services =>
@@ -147,8 +159,18 @@ namespace Microsoft.AspNetCore.HttpsPolicy.Tests
return context.Response.WriteAsync("Hello world");
});
});
+
builder.UseSetting("HTTPS_PORT", configHttpsPort);
- var server = new TestServer(builder);
+
+ var featureCollection = new FeatureCollection();
+ featureCollection.Set(new ServerAddressesFeature());
+
+ var server = new TestServer(builder, featureCollection);
+ if (serverAddressFeatureUrl != null)
+ {
+ server.Features.Get().Addresses.Add(serverAddressFeatureUrl);
+ }
+
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "");
@@ -157,5 +179,106 @@ namespace Microsoft.AspNetCore.HttpsPolicy.Tests
Assert.Equal(expectedUrl, response.Headers.Location.ToString());
}
+
+ [Fact]
+ public async Task SetServerAddressesFeature_SingleHttpsAddress_Success()
+ {
+ var builder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddHttpsRedirection(options =>
+ {
+ });
+ })
+ .Configure(app =>
+ {
+ app.UseHttpsRedirection();
+ app.Run(context =>
+ {
+ return context.Response.WriteAsync("Hello world");
+ });
+ });
+
+ var featureCollection = new FeatureCollection();
+ featureCollection.Set(new ServerAddressesFeature());
+ var server = new TestServer(builder, featureCollection);
+
+ server.Features.Get().Addresses.Add("https://localhost:5050");
+ var client = server.CreateClient();
+
+ var request = new HttpRequestMessage(HttpMethod.Get, "");
+
+ var response = await client.SendAsync(request);
+
+ Assert.Equal("https://localhost:5050/", response.Headers.Location.ToString());
+ }
+
+ [Fact]
+ public async Task SetServerAddressesFeature_MultipleHttpsAddresses_ThrowInMiddleware()
+ {
+ var builder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddHttpsRedirection(options =>
+ {
+ });
+ })
+ .Configure(app =>
+ {
+ app.UseHttpsRedirection();
+ app.Run(context =>
+ {
+ return context.Response.WriteAsync("Hello world");
+ });
+ });
+
+ var featureCollection = new FeatureCollection();
+ featureCollection.Set(new ServerAddressesFeature());
+ var server = new TestServer(builder, featureCollection);
+
+ server.Features.Get().Addresses.Add("https://localhost:5050");
+ server.Features.Get().Addresses.Add("https://localhost:5051");
+
+ var client = server.CreateClient();
+
+ var request = new HttpRequestMessage(HttpMethod.Get, "");
+
+ await Assert.ThrowsAsync(async () => await client.SendAsync(request));
+ }
+
+ [Fact]
+ public async Task SetServerAddressesFeature_MultipleHttpsAddressesWithSamePort_Success()
+ {
+ var builder = new WebHostBuilder()
+ .ConfigureServices(services =>
+ {
+ services.AddHttpsRedirection(options =>
+ {
+ });
+ })
+ .Configure(app =>
+ {
+ app.UseHttpsRedirection();
+ app.Run(context =>
+ {
+ return context.Response.WriteAsync("Hello world");
+ });
+ });
+
+ var featureCollection = new FeatureCollection();
+ featureCollection.Set(new ServerAddressesFeature());
+ var server = new TestServer(builder, featureCollection);
+
+ server.Features.Get().Addresses.Add("https://localhost:5050");
+ server.Features.Get().Addresses.Add("https://localhost:5050");
+
+ var client = server.CreateClient();
+
+ var request = new HttpRequestMessage(HttpMethod.Get, "");
+
+ var response = await client.SendAsync(request);
+
+ Assert.Equal("https://localhost:5050/", response.Headers.Location.ToString());
+ }
}
}