diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ForwardedHost.Shared.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ForwardedHost.Shared.cs
index 22a9fdfaa..aa0127933 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ForwardedHost.Shared.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ForwardedHost.Shared.cs
@@ -12,19 +12,23 @@ namespace Microsoft.AspNetCore.SystemWebAdapters;
internal readonly struct ForwardedHost
{
+ private readonly int? _port;
+
public ForwardedHost(string host, string? proto)
{
var hostString = HostString.FromUriComponent(host);
+ IsSecure = string.Equals("https", proto, StringComparison.OrdinalIgnoreCase);
ServerName = hostString.Host;
- Port = hostString.Port is int p ? p : GetDefaultPort(proto);
+ _port = hostString.Port;
}
- private static int GetDefaultPort(string? proto)
- => string.Equals("https", proto, StringComparison.OrdinalIgnoreCase) ? 443 : 80;
+ private int DefaultPort => IsSecure ? 443 : 80;
+
+ public bool IsSecure { get; }
public string ServerName { get; }
- public int Port { get; }
+ public int Port => _port is int port ? port : DefaultPort;
}
#endif
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ProxyHeaderModule.Framework.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ProxyHeaderModule.Framework.cs
index 2f1a7cb6b..8031f1f00 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ProxyHeaderModule.Framework.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ProxyHeaderModule.Framework.cs
@@ -9,20 +9,25 @@
namespace Microsoft.AspNetCore.SystemWebAdapters;
+///
+/// Updates server and request variables based on proxy headers. See https://docs.microsoft.com/en-us/iis/web-dev-reference/server-variables for reference on what server variables should be used.
+///
internal class ProxyHeaderModule : IHttpModule
{
private const string Host = "Host";
+ private const string ServerHttps = "HTTPS";
private const string ServerName = "SERVER_NAME";
private const string ServerPort = "SERVER_PORT";
- private const string ServerProtocol = "SERVER_PROTOCOL";
private const string ForwardedProto = "x-forwarded-proto";
private const string ForwardedHost = "x-forwarded-host";
+ private const string On = "ON";
+ private const string Off = "OFF";
- private readonly ProxyOptions _options;
+ private readonly IOptions _options;
public ProxyHeaderModule(IOptions options)
{
- _options = options?.Value ?? throw new ArgumentNullException(nameof(options));
+ _options = options ?? throw new ArgumentNullException(nameof(options));
}
public void Dispose()
@@ -31,7 +36,9 @@ public void Dispose()
public void Init(HttpApplication context)
{
- if (_options.UseForwardedHeaders)
+ var options = _options.Value;
+
+ if (options.UseForwardedHeaders)
{
context.BeginRequest += (s, e) =>
{
@@ -41,15 +48,12 @@ public void Init(HttpApplication context)
}
else
{
- if (_options.ServerName is null)
- {
- throw new InvalidOperationException("Server name must be set for proxy options.");
- }
+ var values = new ServerValues(options);
context.BeginRequest += (s, e) =>
{
var request = ((HttpApplication)s).Context.Request;
- UseOptions(request.Headers, request.ServerVariables);
+ UseOptions(values, request.Headers, request.ServerVariables);
};
}
}
@@ -64,33 +68,30 @@ public void UseHeaders(NameValueCollection requestHeaders, NameValueCollection s
{
if (requestHeaders[Host] is { } originalHost)
{
- requestHeaders[_options.OriginalHostHeaderName] = originalHost;
+ requestHeaders[_options.Value.OriginalHostHeaderName] = originalHost;
}
var value = new ForwardedHost(host, proto);
serverVariables.Set(ServerName, value.ServerName);
serverVariables.Set(ServerPort, value.Port.ToString(CultureInfo.InvariantCulture));
+ serverVariables.Set(ServerHttps, value.IsSecure ? On : Off);
requestHeaders[Host] = host;
}
-
- if (proto is { })
- {
- serverVariables.Set(ServerProtocol, proto);
- }
}
- private void UseOptions(NameValueCollection requestHeaders, NameValueCollection serverVariables)
+ private static void UseOptions(ServerValues values, NameValueCollection requestHeaders, NameValueCollection serverVariables)
{
UseForwardedFor(requestHeaders, serverVariables);
- serverVariables.Set(ServerName, _options.ServerName);
- serverVariables.Set(ServerPort, _options.ServerPortString);
- serverVariables.Set(ServerProtocol, _options.Scheme);
- requestHeaders[Host] = _options.ServerHostString;
+ serverVariables.Set(ServerName, values.Name);
+ serverVariables.Set(ServerPort, values.Port);
+ serverVariables.Set(ServerHttps, values.Https);
+ requestHeaders[Host] = values.Host;
}
+
private static void UseForwardedFor(NameValueCollection requestHeaders, NameValueCollection serverVariables)
{
if (requestHeaders["x-forwarded-for"] is { } remote)
@@ -99,4 +100,28 @@ private static void UseForwardedFor(NameValueCollection requestHeaders, NameValu
serverVariables.Set("REMOTE_HOST", remote);
}
}
+
+ private class ServerValues
+ {
+ public ServerValues(ProxyOptions options)
+ {
+ if (options.ServerName is null)
+ {
+ throw new InvalidOperationException("Server name must be set for proxy options.");
+ }
+
+ Name = options.ServerName;
+ Port = options.ServerPort.ToString(CultureInfo.InvariantCulture);
+ Https = string.Equals("https", options.Scheme, StringComparison.OrdinalIgnoreCase) ? On : Off;
+ Host = $"{Name}:{Port}";
+ }
+
+ public string Name { get; }
+
+ public string Port { get; }
+
+ public string Https { get; }
+
+ public string Host { get; }
+ }
}
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ProxyOptions.Framework.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ProxyOptions.Framework.cs
index 29ef158c0..b3d7aba12 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ProxyOptions.Framework.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/ProxyOptions.Framework.cs
@@ -1,15 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Globalization;
-
namespace Microsoft.AspNetCore.SystemWebAdapters;
public class ProxyOptions
{
- private string? _port;
- private string? _serverHostString;
-
///
/// Gets or sets whether the X-Forwarded-* headers should be used for incoming requests.
///
@@ -31,8 +26,4 @@ public class ProxyOptions
/// Gets or sets the scheme.
///
public string Scheme { get; set; } = "https";
-
- internal string ServerPortString => _port ??= ServerPort.ToString(CultureInfo.InvariantCulture);
-
- internal string ServerHostString => _serverHostString ??= $"{ServerName}:{ServerPortString}";
}
diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.Framework.Tests/ProxyHeaderModuleTests.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.Framework.Tests/ProxyHeaderModuleTests.cs
index 603b916e0..b454b472f 100644
--- a/test/Microsoft.AspNetCore.SystemWebAdapters.Framework.Tests/ProxyHeaderModuleTests.cs
+++ b/test/Microsoft.AspNetCore.SystemWebAdapters.Framework.Tests/ProxyHeaderModuleTests.cs
@@ -17,6 +17,9 @@ public class ProxyHeaderModuleTests
private const string RemoteHost = "REMOTE_HOST";
private const string ServerName = "SERVER_NAME";
private const string ServerPort = "SERVER_PORT";
+ private const string ServerHttps = "HTTPS";
+ private const string On = "ON";
+ private const string Off = "OFF";
[Fact]
public void NoHeaderChange()
@@ -57,6 +60,7 @@ public void HostWithPortNoProto()
Assert.Null(serverVariables[RemoteHost]);
Assert.Equal("localhost:81", requestHeaders[Host]);
Assert.Null(requestHeaders[options.OriginalHostHeaderName]);
+ Assert.Equal(Off, serverVariables[ServerHttps]);
}
[Fact]
@@ -81,6 +85,7 @@ public void HostWithNoPortNoProto()
Assert.Null(serverVariables[RemoteHost]);
Assert.Equal("localhost", requestHeaders[Host]);
Assert.Null(requestHeaders[options.OriginalHostHeaderName]);
+ Assert.Equal(Off, serverVariables[ServerHttps]);
}
[Fact]
@@ -106,6 +111,7 @@ public void HostWithNoPortHttp()
Assert.Null(serverVariables[RemoteHost]);
Assert.Equal("localhost", requestHeaders[Host]);
Assert.Null(requestHeaders[options.OriginalHostHeaderName]);
+ Assert.Equal(Off, serverVariables[ServerHttps]);
}
[Fact]
@@ -132,6 +138,7 @@ public void HostAlreadySet()
Assert.Null(serverVariables[RemoteHost]);
Assert.Equal("localhost", requestHeaders[Host]);
Assert.Equal("localhost2:90", requestHeaders[options.OriginalHostHeaderName]);
+ Assert.Equal(Off, serverVariables[ServerHttps]);
}
[Fact]
@@ -157,6 +164,7 @@ public void HostWithNoPortHttps()
Assert.Null(serverVariables[RemoteHost]);
Assert.Equal("localhost", requestHeaders[Host]);
Assert.Null(requestHeaders[options.OriginalHostHeaderName]);
+ Assert.Equal(On, serverVariables[ServerHttps]);
}
[Fact]
@@ -182,6 +190,7 @@ public void IPv6NoPort()
Assert.Null(serverVariables[RemoteHost]);
Assert.Equal("::1", requestHeaders[Host]);
Assert.Null(requestHeaders[options.OriginalHostHeaderName]);
+ Assert.Equal(On, serverVariables[ServerHttps]);
}
[Fact]
@@ -207,6 +216,7 @@ public void IPv6WithPort()
Assert.Null(serverVariables[RemoteHost]);
Assert.Equal("[::1]:81", requestHeaders[Host]);
Assert.Null(requestHeaders[options.OriginalHostHeaderName]);
+ Assert.Equal(On, serverVariables[ServerHttps]);
}
[Fact]
@@ -230,6 +240,7 @@ public void ForwardedForSet()
Assert.Null(requestHeaders[Host]);
Assert.Null(serverVariables[ServerName]);
Assert.Null(serverVariables[ServerPort]);
+ Assert.Null(serverVariables[ServerHttps]);
Assert.Equal(ForwardedForValue, serverVariables[RemoteAddress]);
Assert.Equal(ForwardedForValue, serverVariables[RemoteHost]);
Assert.Null(requestHeaders[options.OriginalHostHeaderName]);