Skip to content

Commit 2e1f44e

Browse files
committed
Ensuring function route mapping happens prior to initialized state.
1 parent 6cd27cf commit 2e1f44e

File tree

8 files changed

+83
-36
lines changed

8 files changed

+83
-36
lines changed

src/WebJobs.Script.WebHost/DependencyInjection/DependencyValidator/DependencyValidator.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ private static ExpectedDependencyBuilder CreateExpectedDependencies()
4242
expected.ExpectCollection<IHostedService>()
4343
.Expect<JobHostService>("Microsoft.Azure.WebJobs.Hosting.OptionsLoggingService")
4444
.Expect<PrimaryHostCoordinator>()
45-
.Expect<HttpInitializationService>()
4645
.Expect<FileMonitoringService>()
4746
.Expect<LanguageWorkerConsoleLogService>()
4847
.Optional<FunctionsScaleMonitorService>()

src/WebJobs.Script.WebHost/WebScriptHostBuilderExtension.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public static IHostBuilder AddWebScriptHost(this IHostBuilder builder, IServiceP
4646
configureWebJobs?.Invoke(webJobsBuilder);
4747

4848
ConfigureRegisteredBuilders(webJobsBuilder, rootServiceProvider);
49+
50+
webJobsBuilder.Services.AddSingleton<IHttpRoutesManager, WebScriptHostHttpRoutesManager>();
4951
})
5052
.ConfigureAppConfiguration(configurationBuilder =>
5153
{
@@ -95,7 +97,6 @@ public static IHostBuilder AddWebScriptHost(this IHostBuilder builder, IServiceP
9597
services.AddSingleton<IEventCollectorProvider, FunctionInstanceLogCollectorProvider>();
9698

9799
// Hosted services
98-
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, HttpInitializationService>());
99100
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService, FileMonitoringService>());
100101

101102
ConfigureRegisteredBuilders(services, rootServiceProvider);
Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,46 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

4-
using System.Collections.Generic;
5-
using System.Threading;
6-
using System.Threading.Tasks;
4+
using System;
5+
using System.Text;
76
using Microsoft.AspNetCore.Routing;
87
using Microsoft.AspNetCore.Routing.Constraints;
98
using Microsoft.Azure.WebJobs.Extensions.Http;
10-
using Microsoft.Azure.WebJobs.Script.Description;
11-
using Microsoft.Extensions.Hosting;
129
using Microsoft.Extensions.Logging;
1310
using Microsoft.Extensions.Options;
1411

1512
namespace Microsoft.Azure.WebJobs.Script.WebHost
1613
{
17-
/// <summary>
18-
/// An <see cref="IHostedService"/> responsible for HTTP function initialization.
19-
/// </summary>
20-
public class HttpInitializationService : IHostedService
14+
internal sealed partial class WebScriptHostHttpRoutesManager : IHttpRoutesManager
2115
{
16+
private static readonly string[] _allMethods = new string[] { "all" };
2217
private readonly IOptions<HttpOptions> _httpOptions;
2318
private readonly IWebJobsRouter _router;
2419
private readonly ILoggerFactory _loggerFactory;
25-
private readonly IScriptJobHost _host;
2620
private readonly IEnvironment _environment;
2721

28-
public HttpInitializationService(IOptions<HttpOptions> httpOptions, IWebJobsRouter router, ILoggerFactory loggerFactory, IScriptJobHost host, IEnvironment environment)
22+
public WebScriptHostHttpRoutesManager(IOptions<HttpOptions> httpOptions, IWebJobsRouter router, ILoggerFactory loggerFactory, IEnvironment environment)
2923
{
3024
_httpOptions = httpOptions;
3125
_router = router;
3226
_loggerFactory = loggerFactory;
33-
_host = host;
3427
_environment = environment;
3528
}
3629

37-
public Task StartAsync(CancellationToken cancellationToken)
30+
public void InitializeHttpFunctionRoutes(IScriptJobHost host)
3831
{
39-
InitializeHttpFunctions(_host.Functions, _httpOptions.Value);
32+
var routesLogBuilder = new StringBuilder();
33+
routesLogBuilder.AppendLine("Initializing function HTTP routes");
4034

41-
return Task.CompletedTask;
42-
}
43-
44-
public Task StopAsync(CancellationToken cancellationToken)
45-
{
46-
return Task.CompletedTask;
47-
}
48-
49-
private void InitializeHttpFunctions(IEnumerable<FunctionDescriptor> functions, HttpOptions httpOptions)
50-
{
5135
_router.ClearRoutes();
5236

5337
// TODO: FACAVAL Instantiation of the ScriptRouteHandler should be cleaned up
54-
WebJobsRouteBuilder routesBuilder = _router.CreateBuilder(new ScriptRouteHandler(_loggerFactory, _host, _environment, false), httpOptions.RoutePrefix);
38+
WebJobsRouteBuilder routesBuilder = _router.CreateBuilder(new ScriptRouteHandler(_loggerFactory, host, _environment, false), _httpOptions.Value.RoutePrefix);
5539

5640
// Proxies do not honor the route prefix defined in host.json
57-
WebJobsRouteBuilder proxiesRoutesBuilder = _router.CreateBuilder(new ScriptRouteHandler(_loggerFactory, _host, _environment, true), routePrefix: null);
41+
WebJobsRouteBuilder proxiesRoutesBuilder = _router.CreateBuilder(new ScriptRouteHandler(_loggerFactory, host, _environment, true), routePrefix: null);
5842

59-
foreach (var function in functions)
43+
foreach (var function in host.Functions)
6044
{
6145
var httpTrigger = function.GetTriggerAttributeOrNull<HttpTriggerAttribute>();
6246
if (httpTrigger != null)
@@ -76,23 +60,50 @@ private void InitializeHttpFunctions(IEnumerable<FunctionDescriptor> functions,
7660

7761
WebJobsRouteBuilder builder = function.Metadata.IsProxy ? proxiesRoutesBuilder : routesBuilder;
7862
builder.MapFunctionRoute(function.Metadata.Name, route, constraints, function.Metadata.Name);
63+
64+
LogRouteMap(routesLogBuilder, function.Metadata.Name, route, httpTrigger.Methods, function.Metadata.IsProxy, _httpOptions.Value.RoutePrefix);
7965
}
8066
}
8167

8268
IRouter proxyRouter = null;
8369
IRouter functionRouter = null;
8470

85-
if (proxiesRoutesBuilder.Count > 0)
71+
if (routesBuilder.Count == 0 && proxiesRoutesBuilder.Count == 0)
8672
{
87-
proxyRouter = proxiesRoutesBuilder.Build();
73+
routesLogBuilder.AppendLine("No HTTP routes mapped");
8874
}
89-
90-
if (routesBuilder.Count > 0)
75+
else
9176
{
92-
functionRouter = routesBuilder.Build();
77+
if (proxiesRoutesBuilder.Count > 0)
78+
{
79+
proxyRouter = proxiesRoutesBuilder.Build();
80+
}
81+
82+
if (routesBuilder.Count > 0)
83+
{
84+
functionRouter = routesBuilder.Build();
85+
}
9386
}
9487

9588
_router.AddFunctionRoutes(functionRouter, proxyRouter);
89+
90+
ILogger logger = _loggerFactory.CreateLogger("Host.HttpRoutes");
91+
logger.LogInformation(routesLogBuilder.ToString());
92+
}
93+
94+
private void LogRouteMap(StringBuilder builder, string functionName, string route, string[] methods, bool isProxy, string prefix)
95+
{
96+
methods = methods ?? _allMethods;
97+
string methodList = string.Join(',', methods);
98+
99+
if (isProxy)
100+
{
101+
builder.AppendLine($"Mapped proxy route '{route}' [{methodList}] to '{functionName}'");
102+
}
103+
else
104+
{
105+
builder.AppendLine($"Mapped function route '{prefix}/{route}' [{methodList}] to '{functionName}'");
106+
}
96107
}
97108
}
98109
}

src/WebJobs.Script/Host/ScriptHost.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class ScriptHost : JobHost, IScriptJobHost
4545
private readonly IDistributedLockManager _distributedLockManager;
4646
private readonly IFunctionMetadataManager _functionMetadataManager;
4747
private readonly IHostIdProvider _hostIdProvider;
48+
private readonly IHttpRoutesManager _httpRoutesManager;
4849
private readonly IProxyMetadataManager _proxyMetadataManager;
4950
private readonly IEnumerable<WorkerConfig> _workerConfigs;
5051
private readonly IMetricsLogger _metricsLogger = null;
@@ -80,7 +81,6 @@ public ScriptHost(IOptions<JobHostOptions> options,
8081
IJobHostContextFactory jobHostContextFactory,
8182
IConfiguration configuration,
8283
IDistributedLockManager distributedLockManager,
83-
IScriptHostManager scriptHostManager,
8484
IScriptEventManager eventManager,
8585
ILoggerFactory loggerFactory,
8686
IFunctionDispatcher functionDispatcher,
@@ -95,6 +95,7 @@ public ScriptHost(IOptions<JobHostOptions> options,
9595
IPrimaryHostStateProvider primaryHostStateProvider,
9696
IJobHostMetadataProvider metadataProvider,
9797
IHostIdProvider hostIdProvider,
98+
IHttpRoutesManager httpRoutesManager,
9899
ScriptSettingsManager settingsManager = null)
99100
: base(options, jobHostContextFactory)
100101
{
@@ -109,6 +110,7 @@ public ScriptHost(IOptions<JobHostOptions> options,
109110
_distributedLockManager = distributedLockManager;
110111
_functionMetadataManager = functionMetadataManager;
111112
_hostIdProvider = hostIdProvider;
113+
_httpRoutesManager = httpRoutesManager;
112114
_proxyMetadataManager = proxyMetadataManager;
113115
_workerConfigs = languageWorkerOptions.Value.WorkerConfigs;
114116
ScriptOptions = scriptHostOptions.Value;
@@ -845,6 +847,8 @@ protected override void OnHostInitialized()
845847
{
846848
ApplyJobHostMetadata();
847849

850+
_httpRoutesManager.InitializeHttpFunctionRoutes(this);
851+
848852
_logger.ScriptHostInitialized(_stopwatch.ElapsedMilliseconds);
849853

850854
HostInitialized?.Invoke(this, EventArgs.Empty);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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 Microsoft.Azure.WebJobs.Script.WebHost;
6+
7+
namespace Microsoft.Azure.WebJobs.Script.Http
8+
{
9+
internal class DefaultHttpRouteManager : IHttpRoutesManager
10+
{
11+
public void InitializeHttpFunctionRoutes(IScriptJobHost host)
12+
{
13+
// noop
14+
}
15+
}
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
6+
namespace Microsoft.Azure.WebJobs.Script
7+
{
8+
public interface IHttpRoutesManager
9+
{
10+
void InitializeHttpFunctionRoutes(IScriptJobHost host);
11+
}
12+
}

src/WebJobs.Script/ScriptHostBuilderExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using Microsoft.Azure.WebJobs.Script.FileProvisioning;
2323
using Microsoft.Azure.WebJobs.Script.Grpc;
2424
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
25+
using Microsoft.Azure.WebJobs.Script.Http;
2526
using Microsoft.Azure.WebJobs.Script.ManagedDependencies;
2627
using Microsoft.Azure.WebJobs.Script.Rpc;
2728
using Microsoft.Azure.WebJobs.Script.Scale;
@@ -134,6 +135,7 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp
134135
services.AddSingleton<ITypeLocator, ScriptTypeLocator>();
135136
services.AddSingleton<ScriptSettingsManager>();
136137
services.AddTransient<IExtensionsManager, ExtensionsManager>();
138+
services.TryAddSingleton<IHttpRoutesManager, DefaultHttpRouteManager>();
137139
services.TryAddSingleton<IMetricsLogger, MetricsLogger>();
138140
services.TryAddSingleton<IScriptJobHostEnvironment, ConsoleScriptJobHostEnvironment>();
139141
services.AddTransient<IExtensionBundleContentProvider, ExtensionBundleContentProvider>();

test/WebJobs.Script.Tests.Integration/ApplicationInsights/ApplicationInsightsEndToEndTestsBase.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ await TestHelpers.Await(() =>
274274
!t.Message.StartsWith("Host Status")
275275
).ToArray();
276276

277-
int expectedCount = 13;
277+
int expectedCount = 14;
278278
Assert.True(traces.Length == expectedCount, $"Expected {expectedCount} messages, but found {traces.Length}. Actual logs:{Environment.NewLine}{string.Join(Environment.NewLine, traces.Select(t => t.Message))}");
279279

280280
int idx = 0;
@@ -286,6 +286,8 @@ await TestHelpers.Await(() =>
286286
ValidateTrace(traces[idx++], "Host initialized (", LogCategories.Startup);
287287
ValidateTrace(traces[idx++], "Host lock lease acquired by instance ID", ScriptConstants.LogCategoryHostGeneral);
288288
ValidateTrace(traces[idx++], "Host started (", LogCategories.Startup);
289+
ValidateTrace(traces[idx++], "Initializing function HTTP routes" + Environment.NewLine
290+
+ "Mapped function route 'api/HttpTrigger-Scenarios'", "Host.HttpRoutes");
289291
ValidateTrace(traces[idx++], "Initializing Host", LogCategories.Startup);
290292
ValidateTrace(traces[idx++], "Initializing Warmup Extension", LogCategories.CreateTriggerCategory("Warmup"));
291293
ValidateTrace(traces[idx++], "Job host started", LogCategories.Startup);

0 commit comments

Comments
 (0)