Skip to content

Commit 245e33b

Browse files
committed
Loosen host running status check (#2114)
1 parent d9728af commit 245e33b

File tree

8 files changed

+64
-33
lines changed

8 files changed

+64
-33
lines changed

src/WebJobs.Script.WebHost/Controllers/AdminController.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public AdminController(WebScriptHostManager scriptHostManager, WebHostSettings w
4343
}
4444

4545
[HttpPost]
46+
[RequiresRunningHost]
4647
[Route("admin/functions/{name}")]
4748
public HttpResponseMessage Invoke(string name, [FromBody] FunctionInvocation invocation)
4849
{
@@ -68,6 +69,7 @@ public HttpResponseMessage Invoke(string name, [FromBody] FunctionInvocation inv
6869
}
6970

7071
[HttpGet]
72+
[RequiresRunningHost]
7173
[Route("admin/functions/{name}/status")]
7274
public FunctionStatus GetFunctionStatus(string name)
7375
{
@@ -211,6 +213,7 @@ public override Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext con
211213
[HttpGet]
212214
[HttpPost]
213215
[AllowAnonymous]
216+
[RequiresRunningHost]
214217
public async Task<HttpResponseMessage> ExtensionWebHookHandler(string name, CancellationToken token)
215218
{
216219
var provider = _scriptHostManager.BindingWebHookProvider;

src/WebJobs.Script.WebHost/Controllers/FunctionsController.cs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,13 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
5-
using System.IO;
6-
using System.Net;
75
using System.Net.Http;
8-
using System.Text;
96
using System.Threading;
107
using System.Threading.Tasks;
118
using System.Web.Http;
129
using System.Web.Http.Controllers;
13-
using System.Web.Http.Dependencies;
1410
using Microsoft.Azure.AppService.Proxy.Client;
15-
using Microsoft.Azure.WebJobs.Extensions.Http;
16-
using Microsoft.Azure.WebJobs.Script.Description;
1711
using Microsoft.Azure.WebJobs.Script.Host;
18-
using Microsoft.Azure.WebJobs.Script.WebHost.Filters;
19-
using Microsoft.Azure.WebJobs.Script.WebHost.Properties;
2012
using Microsoft.Azure.WebJobs.Script.WebHost.WebHooks;
2113

2214
namespace Microsoft.Azure.WebJobs.Script.WebHost.Controllers
@@ -37,6 +29,8 @@ public FunctionsController(WebScriptHostManager scriptHostManager, WebHookReceiv
3729

3830
public override async Task<HttpResponseMessage> ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)
3931
{
32+
await _scriptHostManager.DelayUntilHostReady();
33+
4034
var request = controllerContext.Request;
4135
var function = _scriptHostManager.GetHttpFunctionOrNull(request);
4236

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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.Threading;
6+
using System.Threading.Tasks;
7+
using System.Web.Http.Controllers;
8+
using System.Web.Http.Filters;
9+
10+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Filters
11+
{
12+
/// <summary>
13+
/// Filter applied to actions that require the host instance to be in a state
14+
/// where functions can be invoked.
15+
/// </summary>
16+
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
17+
public sealed class RequiresRunningHostAttribute : ActionFilterAttribute
18+
{
19+
public RequiresRunningHostAttribute(int timeoutSeconds = ScriptConstants.HostTimeoutSeconds, int pollingIntervalMilliseconds = ScriptConstants.HostPollingIntervalMilliseconds)
20+
{
21+
TimeoutSeconds = timeoutSeconds;
22+
PollingIntervalMilliseconds = pollingIntervalMilliseconds;
23+
}
24+
25+
public int TimeoutSeconds { get; }
26+
27+
public int PollingIntervalMilliseconds { get; }
28+
29+
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
30+
{
31+
var resolver = actionContext.ControllerContext.Configuration.DependencyResolver;
32+
var scriptHostManager = resolver.GetService<WebScriptHostManager>();
33+
34+
// If the host is not ready, we'll wait a bit for it to initialize.
35+
// This might happen if http requests come in while the host is starting
36+
// up for the first time, or if it is restarting.
37+
await scriptHostManager.DelayUntilHostReady(TimeoutSeconds, PollingIntervalMilliseconds);
38+
}
39+
}
40+
}

src/WebJobs.Script.WebHost/Handlers/WebScriptHostHandler.cs

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,15 @@ namespace Microsoft.Azure.WebJobs.Script.WebHost.Handlers
1111
{
1212
public class WebScriptHostHandler : DelegatingHandler
1313
{
14-
public const int HostTimeoutSeconds = 30;
15-
public const int HostPollingIntervalMilliseconds = 25;
16-
17-
private readonly int _hostTimeoutSeconds;
18-
private readonly int _hostRunningPollIntervalMilliseconds;
1914
private readonly HttpConfiguration _config;
2015

21-
public WebScriptHostHandler(HttpConfiguration config, int hostTimeoutSeconds = HostTimeoutSeconds, int hostPollingIntervalMilliseconds = HostPollingIntervalMilliseconds)
16+
public WebScriptHostHandler(HttpConfiguration config)
2217
{
2318
if (config == null)
2419
{
2520
throw new ArgumentNullException("config");
2621
}
2722

28-
_hostRunningPollIntervalMilliseconds = hostPollingIntervalMilliseconds;
29-
_hostTimeoutSeconds = hostTimeoutSeconds;
3023
_config = config;
3124
}
3225

@@ -38,6 +31,8 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
3831
var scriptHostManager = resolver.GetService<WebScriptHostManager>();
3932
if (!scriptHostManager.Initialized)
4033
{
34+
// need to ensure the host manager is initilized early in the pipeline
35+
// before any other request code runs
4136
scriptHostManager.Initialize();
4237
}
4338

@@ -47,16 +42,6 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
4742
request.SetProperty(ScriptConstants.AzureFunctionsHttpRequestAuthorizationDisabledKey, true);
4843
}
4944

50-
// some routes do not require the host to be running (most do)
51-
bool bypassHostCheck = request.MatchRoute("admin/host/status");
52-
if (!bypassHostCheck)
53-
{
54-
// If the host is not running, we'll wait a bit for it to fully
55-
// initialize. This might happen if http requests come in while the
56-
// host is starting up for the first time, or if it is restarting.
57-
await scriptHostManager.DelayUntilHostReady(_hostTimeoutSeconds, _hostRunningPollIntervalMilliseconds);
58-
}
59-
6045
if (StandbyManager.IsWarmUpRequest(request))
6146
{
6247
await StandbyManager.WarmUp(request, scriptHostManager);

src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,7 @@
437437
<Compile Include="App_Start\WebHostResolver.cs" />
438438
<Compile Include="App_Start\WebHostSettings.cs" />
439439
<Compile Include="Diagnostics\Sanitizer.cs" />
440+
<Compile Include="Filters\RequiresRunningHostAttribute.cs" />
440441
<Compile Include="StandbyManager.cs" />
441442
<Compile Include="Controllers\AdminController.cs" />
442443
<Compile Include="Controllers\FunctionsController.cs" />

src/WebJobs.Script.WebHost/WebScriptHostManager.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
using Microsoft.Azure.WebJobs.Script.Eventing;
2727
using Microsoft.Azure.WebJobs.Script.Scale;
2828
using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics;
29+
using Microsoft.Azure.WebJobs.Script.WebHost.Filters;
2930
using Microsoft.Azure.WebJobs.Script.WebHost.Handlers;
3031
using Microsoft.Extensions.Logging;
3132

@@ -59,8 +60,8 @@ public WebScriptHostManager(ScriptHostConfiguration config,
5960
IScriptHostFactory scriptHostFactory = null,
6061
ISecretsRepositoryFactory secretsRepositoryFactory = null,
6162
HostPerformanceManager hostPerformanceManager = null,
62-
int hostTimeoutSeconds = WebScriptHostHandler.HostTimeoutSeconds,
63-
int hostPollingIntervalMilliseconds = WebScriptHostHandler.HostPollingIntervalMilliseconds)
63+
int hostTimeoutSeconds = ScriptConstants.HostTimeoutSeconds,
64+
int hostPollingIntervalMilliseconds = ScriptConstants.HostPollingIntervalMilliseconds)
6465
: base(config, settingsManager, scriptHostFactory, eventManager, null, hostPerformanceManager)
6566
{
6667
_config = config;
@@ -158,8 +159,7 @@ public IReadOnlyDictionary<IHttpRoute, FunctionDescriptor> HttpFunctions
158159

159160
public async Task<HttpResponseMessage> HandleRequestAsync(FunctionDescriptor function, HttpRequestMessage request, CancellationToken cancellationToken)
160161
{
161-
// ensure that the host is ready to process requests
162-
await DelayUntilHostReady(_hostTimeoutSeconds, _hostRunningPollIntervalMilliseconds);
162+
await DelayUntilHostReady();
163163

164164
// All authentication is assumed to have been done on the request
165165
// BEFORE this method is called
@@ -429,7 +429,13 @@ public override void Shutdown()
429429
HostingEnvironment.InitiateShutdown();
430430
}
431431

432-
public async Task DelayUntilHostReady(int timeoutSeconds = WebScriptHostHandler.HostTimeoutSeconds, int pollingIntervalMilliseconds = WebScriptHostHandler.HostPollingIntervalMilliseconds, bool throwOnFailure = true)
432+
public async Task DelayUntilHostReady()
433+
{
434+
// ensure that the host is ready to process requests
435+
await DelayUntilHostReady(_hostTimeoutSeconds, _hostRunningPollIntervalMilliseconds);
436+
}
437+
438+
public async Task DelayUntilHostReady(int timeoutSeconds = ScriptConstants.HostTimeoutSeconds, int pollingIntervalMilliseconds = ScriptConstants.HostPollingIntervalMilliseconds, bool throwOnFailure = true)
433439
{
434440
TimeSpan timeout = TimeSpan.FromSeconds(timeoutSeconds);
435441
TimeSpan delay = TimeSpan.FromMilliseconds(pollingIntervalMilliseconds);

src/WebJobs.Script/ScriptConstants.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,7 @@ public static class ScriptConstants
7676

7777
public const int MaximumHostIdLength = 32;
7878
public const int DynamicSkuConnectionLimit = 50;
79+
public const int HostTimeoutSeconds = 30;
80+
public const int HostPollingIntervalMilliseconds = 25;
7981
}
8082
}

test/WebJobs.Script.Tests/Handlers/WebScriptHostHandlerTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public WebScriptHostHandlerTests()
3232
_settingsManager = ScriptSettingsManager.Instance;
3333
var eventManager = new Mock<IScriptEventManager>();
3434
_managerMock = new Mock<WebScriptHostManager>(MockBehavior.Strict, new ScriptHostConfiguration(), new TestSecretManagerFactory(), eventManager.Object,
35-
_settingsManager, new WebHostSettings { SecretsPath = _secretsDirectory.Path });
35+
_settingsManager, new WebHostSettings { SecretsPath = _secretsDirectory.Path }, null, null, null, 1, 50);
3636

3737
_managerMock.SetupGet(p => p.Initialized).Returns(true);
3838
Mock<IDependencyResolver> mockResolver = new Mock<IDependencyResolver>(MockBehavior.Strict);
@@ -43,7 +43,7 @@ public WebScriptHostHandlerTests()
4343

4444
HttpConfiguration config = new HttpConfiguration();
4545
config.DependencyResolver = mockResolver.Object;
46-
WebScriptHostHandler handler = new WebScriptHostHandler(config, hostTimeoutSeconds: 1, hostPollingIntervalMilliseconds: 50)
46+
WebScriptHostHandler handler = new WebScriptHostHandler(config)
4747
{
4848
InnerHandler = new TestHandler()
4949
};

0 commit comments

Comments
 (0)