Skip to content

Commit f5ee0c7

Browse files
committed
Fix Host header and request scheme for non-appservice environments. Closes #2713
1 parent 24532aa commit f5ee0c7

File tree

5 files changed

+122
-0
lines changed

5 files changed

+122
-0
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System.Threading.Tasks;
5+
using Microsoft.AspNetCore.Http;
6+
using Microsoft.Azure.WebJobs.Script.Extensions;
7+
using Microsoft.Extensions.Primitives;
8+
9+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Middleware
10+
{
11+
public class AppServiceHeaderFixupMiddleware
12+
{
13+
private const string DisguisedHostHeader = "DISGUISED-HOST";
14+
private const string HostHeader = "HOST";
15+
private const string ForwardedProtocolHeader = "X-Forwarded-Proto";
16+
private readonly RequestDelegate _next;
17+
18+
public AppServiceHeaderFixupMiddleware(RequestDelegate next)
19+
{
20+
_next = next;
21+
}
22+
23+
public async Task Invoke(HttpContext httpContext)
24+
{
25+
if (httpContext.Request.Headers.TryGetValue(DisguisedHostHeader, out StringValues value))
26+
{
27+
httpContext.Request.Headers[HostHeader] = value;
28+
}
29+
30+
if (httpContext.Request.Headers.TryGetValue(ForwardedProtocolHeader, out value))
31+
{
32+
httpContext.Request.Scheme = value;
33+
}
34+
35+
await _next(httpContext);
36+
}
37+
}
38+
}

src/WebJobs.Script.WebHost/WebJobsApplicationBuilderExtension.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.AspNetCore.Builder;
77
using Microsoft.AspNetCore.Hosting;
88
using Microsoft.Azure.WebJobs.Extensions.Http;
9+
using Microsoft.Azure.WebJobs.Script.Config;
910
using Microsoft.Azure.WebJobs.Script.WebHost.Middleware;
1011

1112
namespace Microsoft.Azure.WebJobs.Script.WebHost
@@ -19,6 +20,11 @@ public static IApplicationBuilder UseWebJobsScriptHost(this IApplicationBuilder
1920

2021
public static IApplicationBuilder UseWebJobsScriptHost(this IApplicationBuilder builder, IApplicationLifetime applicationLifetime, Action<WebJobsRouteBuilder> routes)
2122
{
23+
if (!ScriptSettingsManager.Instance.IsAppServiceEnvironment)
24+
{
25+
builder.UseMiddleware<AppServiceHeaderFixupMiddleware>();
26+
}
27+
2228
builder.UseMiddleware<HttpExceptionMiddleware>();
2329
builder.UseMiddleware<ResponseBufferingMiddleware>();
2430
builder.UseMiddleware<HomepageMiddleware>();
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Net.Http;
6+
using System.Threading.Tasks;
7+
using Xunit;
8+
9+
namespace Microsoft.Azure.WebJobs.Script.Tests.Integration.Middlewares
10+
{
11+
[Trait("Category", "E2E")]
12+
[Trait("E2E", nameof(AppServiceHeaderFixupMiddlewareEndToEnd))]
13+
public class AppServiceHeaderFixupMiddlewareEndToEnd : EndToEndTestsBase<AppServiceHeaderFixupMiddlewareEndToEnd.TestFixture>
14+
{
15+
public AppServiceHeaderFixupMiddlewareEndToEnd(TestFixture fixture) : base(fixture)
16+
{
17+
}
18+
19+
[Fact]
20+
public async Task HostHeaderIsCorrect()
21+
{
22+
const string actualHost = "actual-host";
23+
const string actualProtocol = "https";
24+
const string functionPath = "api/httpTriggerHeaderCheck";
25+
HttpRequestMessage request = new HttpRequestMessage
26+
{
27+
RequestUri = new Uri(string.Format($"http://localhost/{functionPath}")),
28+
Method = HttpMethod.Get
29+
};
30+
31+
request.Headers.TryAddWithoutValidation("DISGUISED-HOST", actualHost);
32+
request.Headers.TryAddWithoutValidation("X-Forwarded-Proto", actualProtocol);
33+
34+
var response = await Fixture.Host.HttpClient.SendAsync(request);
35+
response.EnsureSuccessStatusCode();
36+
37+
var url = await response.Content.ReadAsStringAsync();
38+
Assert.Equal($"{actualProtocol}://{actualHost}/{functionPath}", url);
39+
}
40+
41+
public class TestFixture : EndToEndTestFixture
42+
{
43+
private const string ScriptRoot = @"TestScripts\CSharp";
44+
45+
public TestFixture() : base(ScriptRoot, "middleware")
46+
{
47+
}
48+
}
49+
}
50+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"disabled": false,
3+
"bindings": [
4+
{
5+
"authLevel": "anonymous",
6+
"name": "req",
7+
"type": "httpTrigger",
8+
"direction": "in",
9+
"methods": [
10+
"get"
11+
]
12+
},
13+
{
14+
"name": "$return",
15+
"type": "http",
16+
"direction": "out"
17+
}
18+
]
19+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#r "Microsoft.AspNetCore.Http.Extensions"
2+
3+
using Microsoft.AspNetCore.Mvc;
4+
using Microsoft.AspNetCore.Http.Extensions;
5+
6+
public static IActionResult Run(HttpRequest req, TraceWriter log)
7+
{
8+
return new OkObjectResult(req.GetDisplayUrl());
9+
}

0 commit comments

Comments
 (0)