Skip to content

Add ForwardedHeaders to CreateDefaultBuilder #10273

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 17, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<UserSecretsId>aspnetcore-MetaPackagesSampleApp-20170406180413</UserSecretsId>
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
</PropertyGroup>

<ItemGroup>
Expand Down
21 changes: 9 additions & 12 deletions src/DefaultBuilder/samples/SampleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
Expand All @@ -16,19 +16,16 @@ public class Program
{
public static void Main(string[] args)
{
HelloWorld();

CustomUrl();

CustomRouter();

CustomApplicationBuilder();

StartupClass(args);

HostBuilderWithWebHost(args);
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});

private static void HelloWorld()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How relevant are these samples anymore?

{
using (WebHost.Start(context => context.Response.WriteAsync("Hello, World!")))
Expand Down
48 changes: 46 additions & 2 deletions src/DefaultBuilder/samples/SampleApp/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,9 +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.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace SampleApp
Expand All @@ -15,12 +21,50 @@ public void ConfigureServices(IServiceCollection services)

}

public void Configure(IApplicationBuilder app)
public void Configure(IApplicationBuilder app, IConfiguration config)
{
app.Run(async (context) =>
{
await context.Response.WriteAsync($"Hello from {nameof(Startup)}!");
await context.Response.WriteAsync($"Hello from {context.Request.GetDisplayUrl()}\r\n");
await context.Response.WriteAsync("\r\n");

await context.Response.WriteAsync("Headers:\r\n");
foreach (var header in context.Request.Headers)
{
await context.Response.WriteAsync($"{header.Key}: {header.Value}\r\n");
}
await context.Response.WriteAsync("\r\n");

await context.Response.WriteAsync("Connection:\r\n");
await context.Response.WriteAsync("RemoteIp: " + context.Connection.RemoteIpAddress + "\r\n");
await context.Response.WriteAsync("RemotePort: " + context.Connection.RemotePort + "\r\n");
await context.Response.WriteAsync("LocalIp: " + context.Connection.LocalIpAddress + "\r\n");
await context.Response.WriteAsync("LocalPort: " + context.Connection.LocalPort + "\r\n");
await context.Response.WriteAsync("ClientCert: " + context.Connection.ClientCertificate + "\r\n");
await context.Response.WriteAsync("\r\n");

await context.Response.WriteAsync("Environment Variables:\r\n");
var vars = Environment.GetEnvironmentVariables();
foreach (var key in vars.Keys.Cast<string>().OrderBy(key => key, StringComparer.OrdinalIgnoreCase))
{
var value = vars[key];
await context.Response.WriteAsync($"{key}: {value}\r\n");
}
await context.Response.WriteAsync("\r\n");

await context.Response.WriteAsync("Config:\r\n");
await ShowConfig(context.Response, config);
await context.Response.WriteAsync("\r\n");
});
}

private static async Task ShowConfig(HttpResponse response, IConfiguration config)
{
foreach (var pair in config.GetChildren())
{
await response.WriteAsync($"{pair.Path}: {pair.Value}\r\n");
await ShowConfig(response, pair);
}
}
}
}
53 changes: 53 additions & 0 deletions src/DefaultBuilder/samples/SampleApp/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"AllowedHosts": "example.com;localhost",
"Kestrel": {
"EndPoints": {
"Http": {
"Url": "http://localhost:5005"
},
"Https": {
"Url": "https://localhost:5006"
}

// To enable HTTPS using a certificate file, set the path to a .pfx file in
// the "Path" property below and configure the password in user secrets.
// The "Password" property should be set in user secrets.
//"HttpsInlineCertFile": {
// "Url": "http://localhost:5005"
// "Certificate": {
// "Path": "<path to .pfx file>",
// "Password: "<cert password>"
// }
//},

//"HttpsInlineCertStore": {
// "Url": "http://localhost:5005"
// "Certificate": {
// "Subject": "",
// "Store": "",
// "Location": "",
// "AllowInvalid": "" // Set to "true" to allow invalid certificates (e.g. expired)
// }
//},

// This uses the cert defined under Certificates/Default or the development cert.
//"HttpsDefaultCert": {
// "Url": "http://localhost:5005"
//}
}
},
"Certificates": {
//"Default": {
// "Path": "<file>",
// "Password": "<password>"
//},

// From cert store:
//"Default": {
// "Subject": "",
// "Store": "",
// "Location": "",
// "AllowInvalid": "" // Set to "true" to allow invalid certificates (e.g. expired certificates)
//}
}
}
48 changes: 0 additions & 48 deletions src/DefaultBuilder/samples/SampleApp/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,50 +1,2 @@
{
"AllowedHosts": "example.com;localhost",
"Kestrel": {
"EndPoints": {
"Http": {
"Url": "http://localhost:5005"
}

// To enable HTTPS using a certificate file, set the path to a .pfx file in
// the "Path" property below and configure the password in user secrets.
// The "Password" property should be set in user secrets.
//"HttpsInlineCertFile": {
// "Url": "http://localhost:5005"
// "Certificate": {
// "Path": "<path to .pfx file>",
// "Password: "<cert password>"
// }
//},

//"HttpsInlineCertStore": {
// "Url": "http://localhost:5005"
// "Certificate": {
// "Subject": "",
// "Store": "",
// "Location": "",
// "AllowInvalid": "" // Set to "true" to allow invalid certificates (e.g. expired)
// }
//},

// This uses the cert defined under Certificates/Default or the development cert.
//"HttpsDefaultCert": {
// "Url": "http://localhost:5005"
//}
}
},
"Certificates": {
//"Default": {
// "Path": "<file>",
// "Password": "<password>"
//},

// From cert store:
//"Default": {
// "Subject": "",
// "Store": "",
// "Location": "",
// "AllowInvalid": "" // Set to "true" to allow invalid certificates (e.g. expired certificates)
//}
}
}
21 changes: 21 additions & 0 deletions src/DefaultBuilder/src/ForwardedHeadersStartupFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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
{
internal class ForwardedHeadersStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseForwardedHeaders();
next(app);
};
}
}
}
4 changes: 3 additions & 1 deletion src/DefaultBuilder/src/GenericHostBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore;

Expand All @@ -15,6 +15,8 @@ public static class GenericHostBuilderExtensions
/// <remarks>
/// The following defaults are applied to the <see cref="IWebHostBuilder"/>:
/// use Kestrel as the web server and configure it using the application's configuration providers,
/// adds the HostFiltering middleware,
/// adds the ForwardedHeaders middleware if ASPNETCORE_FORWARDEDHEADERS_ENABLED=true,
/// and enable IIS integration.
/// </remarks>
/// <param name="builder">The <see cref="IHostBuilder" /> instance to configure</param>
Expand Down
19 changes: 19 additions & 0 deletions src/DefaultBuilder/src/WebHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.AspNetCore.HostFiltering;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -124,6 +125,8 @@ private static IWebHost StartWith(string url, Action<IServiceCollection> configu
/// load <see cref="IConfiguration"/> from User Secrets when <see cref="IHostEnvironment.EnvironmentName"/> is 'Development' using the entry assembly,
/// load <see cref="IConfiguration"/> from environment variables,
/// configure the <see cref="ILoggerFactory"/> to log to the console and debug output,
/// adds the HostFiltering middleware,
/// adds the ForwardedHeaders middleware if ASPNETCORE_FORWARDEDHEADERS_ENABLED=true,
/// and enable IIS integration.
/// </remarks>
/// <returns>The initialized <see cref="IWebHostBuilder"/>.</returns>
Expand All @@ -142,6 +145,8 @@ public static IWebHostBuilder CreateDefaultBuilder() =>
/// load <see cref="IConfiguration"/> from environment variables,
/// load <see cref="IConfiguration"/> from supplied command line args,
/// configure the <see cref="ILoggerFactory"/> to log to the console and debug output,
/// adds the HostFiltering middleware,
/// adds the ForwardedHeaders middleware if ASPNETCORE_FORWARDEDHEADERS_ENABLED=true,
/// and enable IIS integration.
/// </remarks>
/// <param name="args">The command line args.</param>
Expand Down Expand Up @@ -224,6 +229,20 @@ internal static void ConfigureWebDefaults(IWebHostBuilder builder)

services.AddTransient<IStartupFilter, HostFilteringStartupFilter>();

if (string.Equals("true", hostingContext.Configuration["ForwardedHeaders_Enabled"], StringComparison.OrdinalIgnoreCase))
{
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// Only loopback proxies are allowed by default. Clear that restriction because forwarders are
// being enabled by explicit configuration.
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});

services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>();
}

services.AddRouting();
})
.UseIIS()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<ItemGroup>
<Reference Include="Microsoft.AspNetCore" />
<Reference Include="Microsoft.AspNetCore.TestHost" />
</ItemGroup>

</Project>
31 changes: 31 additions & 0 deletions src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebHostTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.HostFiltering;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -57,6 +59,35 @@ public async Task WebHostConfiguration_HostFilterOptionsAreReloadable()
Assert.Contains("NewHost", options.AllowedHosts);
}

[Fact]
public async Task WebHostConfiguration_EnablesForwardedHeadersFromConfig()
{
using var host = WebHost.CreateDefaultBuilder()
.ConfigureAppConfiguration(configBuilder =>
{
configBuilder.AddInMemoryCollection(new[]
{
new KeyValuePair<string, string>("FORWARDEDHEADERS_ENABLED", "true" ),
});
})
.UseTestServer()
.Configure(app =>
{
Assert.True(app.Properties.ContainsKey("ForwardedHeadersAdded"), "Forwarded Headers");
app.Run(context =>
{
Assert.Equal("https", context.Request.Scheme);
return Task.CompletedTask;
});
}).Build();

await host.StartAsync();
var client = host.GetTestClient();
client.DefaultRequestHeaders.Add("x-forwarded-proto", "https");
var result = await client.GetAsync("http://localhost/");
result.EnsureSuccessStatusCode();
}

[Fact]
public void CreateDefaultBuilder_RegistersRouting()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public static partial class WebHostBuilderExtensions
{
public static Microsoft.AspNetCore.Hosting.IWebHostBuilder ConfigureTestContainer<TContainer>(this Microsoft.AspNetCore.Hosting.IWebHostBuilder webHostBuilder, System.Action<TContainer> servicesConfiguration) { throw null; }
public static Microsoft.AspNetCore.Hosting.IWebHostBuilder ConfigureTestServices(this Microsoft.AspNetCore.Hosting.IWebHostBuilder webHostBuilder, System.Action<Microsoft.Extensions.DependencyInjection.IServiceCollection> servicesConfiguration) { throw null; }
public static System.Net.Http.HttpClient GetTestClient(this Microsoft.AspNetCore.Hosting.IWebHost host) { throw null; }
public static Microsoft.AspNetCore.TestHost.TestServer GetTestServer(this Microsoft.AspNetCore.Hosting.IWebHost host) { throw null; }
public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSolutionRelativeContentRoot(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, string solutionRelativePath, string solutionName = "*.sln") { throw null; }
public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseSolutionRelativeContentRoot(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, string solutionRelativePath, string applicationBasePath, string solutionName = "*.sln") { throw null; }
public static Microsoft.AspNetCore.Hosting.IWebHostBuilder UseTestServer(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder) { throw null; }
Expand Down
Loading