Skip to content

Commit 0cdc620

Browse files
committed
Proxy fix for cold start (#3101)
1 parent f71449a commit 0cdc620

File tree

3 files changed

+27
-16
lines changed

3 files changed

+27
-16
lines changed

src/WebJobs.Script.WebHost/Middleware/FunctionInvocationMiddleware.cs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Diagnostics;
6-
using System.IO;
76
using System.Threading;
87
using System.Threading.Tasks;
98
using Microsoft.AspNetCore.Authorization;
@@ -36,26 +35,23 @@ public async Task Invoke(HttpContext context, WebScriptHostManager manager)
3635
{
3736
// flow required context through the request pipeline
3837
// downstream middleware and filters rely on this
39-
context.Items.Add(ScriptConstants.AzureFunctionsHostManagerKey, manager);
38+
context.Items[ScriptConstants.AzureFunctionsHostManagerKey] = manager;
4039
SetRequestId(context.Request);
40+
4141
if (_next != null)
4242
{
4343
await _next(context);
4444
}
4545

4646
IFunctionExecutionFeature functionExecution = context.Features.Get<IFunctionExecutionFeature>();
47-
48-
int nestedProxiesCount = GetNestedProxiesCount(context, functionExecution);
49-
50-
IActionResult result = null;
51-
5247
if (functionExecution != null && !context.Response.HasStarted)
5348
{
54-
result = await GetResultAsync(context, functionExecution);
55-
49+
int nestedProxiesCount = GetNestedProxiesCount(context, functionExecution);
50+
IActionResult result = await GetResultAsync(context, functionExecution);
5651
if (nestedProxiesCount > 0)
5752
{
58-
// if Proxy, the rest of the pipleline will be processed bt Proxies in case there are response overrides and what not.
53+
// if Proxy, the rest of the pipleline will be processed by Proxies in
54+
// case there are response overrides and what not.
5955
SetProxyResult(context, nestedProxiesCount, result);
6056
return;
6157
}
@@ -71,18 +67,17 @@ public async Task Invoke(HttpContext context, WebScriptHostManager manager)
7167

7268
private static void SetProxyResult(HttpContext context, int nestedProxiesCount, IActionResult result)
7369
{
74-
context.Items.Add(ScriptConstants.AzureFunctionsProxyResult, result);
75-
70+
context.Items[ScriptConstants.AzureFunctionsProxyResult] = result;
7671
context.Items[ScriptConstants.AzureFunctionsNestedProxyCount] = nestedProxiesCount - 1;
7772
}
7873

7974
private static int GetNestedProxiesCount(HttpContext context, IFunctionExecutionFeature functionExecution)
8075
{
8176
context.Items.TryGetValue(ScriptConstants.AzureFunctionsNestedProxyCount, out object nestedProxiesCount);
8277

83-
//HttpBufferingService is disabled for non - proxy functions.
8478
if (functionExecution != null && !functionExecution.Descriptor.Metadata.IsProxy && nestedProxiesCount == null)
8579
{
80+
// HttpBufferingService is disabled for non-proxy functions.
8681
var bufferingFeature = context.Features.Get<IHttpBufferingFeature>();
8782
bufferingFeature?.DisableRequestBuffering();
8883
bufferingFeature?.DisableResponseBuffering();
@@ -103,21 +98,21 @@ private async Task<IActionResult> GetResultAsync(HttpContext context, IFunctionE
10398
return new NotFoundResult();
10499
}
105100

106-
if (context.Request.IsColdStart())
101+
if (context.Request.IsColdStart() && !context.Items.ContainsKey(ScriptConstants.AzureFunctionsColdStartKey))
107102
{
108103
// for cold start requests we want to measure the request
109104
// pipeline dispatch time
110105
// important that this stopwatch is started as early as possible
111106
// in the pipeline (in this case, in our first middleware)
112107
var sw = new Stopwatch();
113108
sw.Start();
114-
context.Request.HttpContext.Items.Add(ScriptConstants.AzureFunctionsColdStartKey, sw);
109+
context.Items[ScriptConstants.AzureFunctionsColdStartKey] = sw;
115110
}
116111

117112
// Add route data to request info
118113
// TODO: Keeping this here for now as other code depend on this property, but this can be done in the HTTP binding.
119114
var routingFeature = context.Features.Get<IRoutingFeature>();
120-
context.Items.Add(HttpExtensionConstants.AzureWebJobsHttpRouteDataKey, new Dictionary<string, object>(routingFeature.RouteData.Values));
115+
context.Items[HttpExtensionConstants.AzureWebJobsHttpRouteDataKey] = new Dictionary<string, object>(routingFeature.RouteData.Values);
121116

122117
bool authorized = await AuthenticateAndAuthorizeAsync(context, functionExecution.Descriptor);
123118
if (!authorized)

test/WebJobs.Script.Tests.Integration/WebHostEndToEnd/ProxyEndToEndTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,18 @@ public async Task CatchAllApis()
127127
Assert.Equal("Pong", content);
128128
}
129129

130+
[Fact]
131+
public async Task ColdStartRequest()
132+
{
133+
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "api/proxy/blahblah");
134+
request.Headers.Add("X-MS-COLDSTART", "1");
135+
HttpResponseMessage response = await _fixture.HttpClient.SendAsync(request);
136+
137+
string content = await response.Content.ReadAsStringAsync();
138+
Assert.Equal("200", response.StatusCode.ToString("D"));
139+
Assert.Equal("Pong", content);
140+
}
141+
130142
[Fact]
131143
//backend set as constant - no trailing slash should be added
132144
public async Task TrailingSlashRemoved()

test/WebJobs.Script.Tests/WebJobs.Script.Tests.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@
7878
</None>
7979
</ItemGroup>
8080

81+
<ItemGroup>
82+
<Folder Include="Middleware\" />
83+
</ItemGroup>
84+
8185
<Import Project="..\..\build\GrpcTestFix.targets" />
8286
<Import Project="..\WebJobs.Script.Tests.Shared\WebJobs.Script.Tests.Shared.projitems" Label="Shared" />
8387

0 commit comments

Comments
 (0)