Skip to content

Commit 0c38f7a

Browse files
committed
Http binding refactor (stage 1)
1 parent a105cb4 commit 0c38f7a

29 files changed

+358
-355
lines changed

src/Packages/Packages.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
<SkipCopyBuildProduct>true</SkipCopyBuildProduct>
77
<ProjectGuid>{34AB8F63-18DE-4E0D-B21C-15E33B091634}</ProjectGuid>
88
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
9-
<NuGetPackageImportStamp>d7fae0d2</NuGetPackageImportStamp>
9+
<NuGetPackageImportStamp>
10+
</NuGetPackageImportStamp>
1011
<TargetFrameworkProfile />
1112
</PropertyGroup>
1213
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -70,7 +71,9 @@
7071
<PropertyGroup>
7172
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
7273
</PropertyGroup>
74+
<Error Condition="!Exists('..\..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" />
7375
</Target>
76+
<Import Project="..\..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\..\packages\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" />
7477
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
7578
Other similar extension points exist, see Microsoft.Common.targets.
7679
<Target Name="BeforeBuild">

src/Packages/packages.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3+
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net46" />
34
<package id="StyleCop.Analyzers" version="1.1.0-beta001" targetFramework="net46" developmentDependency="true" />
45
</packages>

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33

44
using System;
55
using System.IO;
6-
using System.Linq;
76
using System.Net;
87
using System.Net.Http;
98
using System.Threading;
109
using System.Threading.Tasks;
1110
using System.Web.Http;
1211
using System.Web.Http.Controllers;
1312
using System.Web.Http.Dependencies;
13+
using Microsoft.Azure.WebJobs.Script.Binding;
1414
using Microsoft.Azure.WebJobs.Script.Description;
1515
using Microsoft.Azure.WebJobs.Script.WebHost.Filters;
1616
using Microsoft.Azure.WebJobs.Script.WebHost.WebHooks;
@@ -74,8 +74,8 @@ public static async Task<AuthorizationLevel> DetermineAuthorizationLevelAsync(Ht
7474

7575
private async Task<HttpResponseMessage> ProcessRequestAsync(HttpRequestMessage request, FunctionDescriptor function, CancellationToken cancellationToken)
7676
{
77-
HttpTriggerBindingMetadata httpFunctionMetadata = (HttpTriggerBindingMetadata)function.Metadata.InputBindings.FirstOrDefault(p => string.Compare("HttpTrigger", p.Type, StringComparison.OrdinalIgnoreCase) == 0);
78-
bool isWebHook = !string.IsNullOrEmpty(httpFunctionMetadata.WebHookType);
77+
var httpTrigger = function.GetTriggerAttributeOrNull<HttpTriggerAttribute>();
78+
bool isWebHook = !string.IsNullOrEmpty(httpTrigger.WebHookType);
7979
var authorizationLevel = request.GetAuthorizationLevel();
8080
HttpResponseMessage response = null;
8181

@@ -105,7 +105,7 @@ private async Task<HttpResponseMessage> ProcessRequestAsync(HttpRequestMessage r
105105
else
106106
{
107107
// Authorize
108-
if (authorizationLevel < httpFunctionMetadata.AuthLevel)
108+
if (authorizationLevel < httpTrigger.AuthLevel)
109109
{
110110
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
111111
}

src/WebJobs.Script.WebHost/WebHooks/WebHookReceiverManager.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using Microsoft.AspNet.WebHooks;
1616
using Microsoft.AspNet.WebHooks.Config;
1717
using Microsoft.Azure.WebJobs.Host;
18+
using Microsoft.Azure.WebJobs.Script.Binding;
1819
using Microsoft.Azure.WebJobs.Script.Description;
1920
using Microsoft.Azure.WebJobs.Script.WebHost.Filters;
2021

@@ -57,8 +58,8 @@ public async Task<HttpResponseMessage> HandleRequestAsync(FunctionDescriptor fun
5758
{
5859
// First check if there is a registered WebHook Receiver for this request, and if
5960
// so use it
60-
HttpTriggerBindingMetadata httpFunctionMetadata = (HttpTriggerBindingMetadata)function.Metadata.InputBindings.FirstOrDefault(p => string.Compare("HttpTrigger", p.Type, StringComparison.OrdinalIgnoreCase) == 0);
61-
string webHookReceiver = httpFunctionMetadata.WebHookType;
61+
var httpTrigger = function.GetTriggerAttributeOrNull<HttpTriggerAttribute>();
62+
string webHookReceiver = httpTrigger.WebHookType;
6263
IWebHookReceiver receiver = null;
6364
if (string.IsNullOrEmpty(webHookReceiver) || !_receiverLookup.TryGetValue(webHookReceiver, out receiver))
6465
{

src/WebJobs.Script.WebHost/WebScriptHostManager.cs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -306,8 +306,8 @@ private static Dictionary<string, object> GetFunctionArguments(FunctionDescripto
306306

307307
if (triggerParameter.Type != typeof(HttpRequestMessage))
308308
{
309-
HttpTriggerBindingMetadata httpFunctionMetadata = (HttpTriggerBindingMetadata)function.Metadata.InputBindings.FirstOrDefault(p => string.Compare("HttpTrigger", p.Type, StringComparison.OrdinalIgnoreCase) == 0);
310-
if (!string.IsNullOrEmpty(httpFunctionMetadata.WebHookType))
309+
var httpTrigger = function.GetTriggerAttributeOrNull<HttpTriggerAttribute>();
310+
if (httpTrigger != null && !string.IsNullOrEmpty(httpTrigger.WebHookType))
311311
{
312312
WebHookHandlerContext webHookContext;
313313
if (request.Properties.TryGetValue(ScriptConstants.AzureFunctionsWebHookContextKey, out webHookContext))
@@ -423,13 +423,7 @@ protected override void OnInitializeConfig(ScriptHostConfiguration config)
423423

424424
protected override void OnHostCreated()
425425
{
426-
// whenever the host is created (or recreated) we build a cache map of
427-
// all http function routes
428-
InitializeHttpFunctions(Instance.Functions);
429-
430-
// since the request manager is created based on configurable
431-
// settings, it has to be recreated when host config changes
432-
_httpRequestManager = new WebScriptHostRequestManager(Instance.ScriptConfig.HttpConfiguration, PerformanceManager, _metricsLogger, _config.TraceWriter);
426+
InitializeHttp();
433427

434428
base.OnHostCreated();
435429
}
@@ -442,22 +436,42 @@ protected override void OnHostStarted()
442436
base.OnHostStarted();
443437
}
444438

445-
internal void InitializeHttpFunctions(IEnumerable<FunctionDescriptor> functions)
439+
private void InitializeHttp()
440+
{
441+
// get the registered http configuration from the extension registry
442+
var extensions = Instance.ScriptConfig.HostConfig.GetService<IExtensionRegistry>();
443+
var httpConfig = extensions.GetExtensions<IExtensionConfigProvider>().OfType<Microsoft.Azure.WebJobs.Script.Binding.Http.HttpConfiguration>().Single();
444+
445+
// whenever the host is created (or recreated) we build a cache map of
446+
// all http function routes
447+
InitializeHttpFunctions(Instance.Functions, httpConfig);
448+
449+
// since the request manager is created based on configurable
450+
// settings, it has to be recreated when host config changes
451+
_httpRequestManager = new WebScriptHostRequestManager(httpConfig, PerformanceManager, _metricsLogger, _config.TraceWriter);
452+
}
453+
454+
private void InitializeHttpFunctions(IEnumerable<FunctionDescriptor> functions, Microsoft.Azure.WebJobs.Script.Binding.Http.HttpConfiguration httpConfig)
446455
{
447456
// we must initialize the route factory here AFTER full configuration
448457
// has been resolved so we apply any route prefix customizations
449-
var httpRouteFactory = new HttpRouteFactory(_config.HttpConfiguration.RoutePrefix);
458+
var httpRouteFactory = new HttpRouteFactory(httpConfig.RoutePrefix);
450459

451460
_httpFunctions = new Dictionary<IHttpRoute, FunctionDescriptor>();
452461
_httpRoutes = new HttpRouteCollection();
453462

454463
foreach (var function in functions)
455464
{
456-
var httpTriggerBinding = function.Metadata.InputBindings.OfType<HttpTriggerBindingMetadata>().SingleOrDefault();
457-
if (httpTriggerBinding != null)
465+
var httpTrigger = function.GetTriggerAttributeOrNull<HttpTriggerAttribute>();
466+
if (httpTrigger != null)
458467
{
459468
IHttpRoute httpRoute = null;
460-
if (httpRouteFactory.TryAddRoute(function.Metadata.Name, httpTriggerBinding.Route, httpTriggerBinding.Methods, _httpRoutes, out httpRoute))
469+
IEnumerable<HttpMethod> httpMethods = null;
470+
if (httpTrigger.Methods != null)
471+
{
472+
httpMethods = httpTrigger.Methods.Select(p => new HttpMethod(p)).ToArray();
473+
}
474+
if (httpRouteFactory.TryAddRoute(function.Metadata.Name, httpTrigger.Route, httpMethods, _httpRoutes, out httpRoute))
461475
{
462476
_httpFunctions.Add(httpRoute, function);
463477
}

src/WebJobs.Script/Binding/ExtensionBinding.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,19 @@ namespace Microsoft.Azure.WebJobs.Script.Binding
2222
public class ExtensionBinding : FunctionBinding, IResultProcessingBinding
2323
{
2424
private ScriptBinding _binding;
25-
private Collection<Attribute> _attributes;
2625

2726
public ExtensionBinding(ScriptHostConfiguration config, ScriptBinding binding, BindingMetadata metadata) : base(config, metadata, binding.Context.Access)
2827
{
2928
_binding = binding;
30-
_attributes = _binding.GetAttributes();
29+
Attributes = _binding.GetAttributes();
3130
}
3231

32+
internal Collection<Attribute> Attributes { get; private set; }
33+
3334
public override Collection<CustomAttributeBuilder> GetCustomAttributes(Type parameterType)
3435
{
3536
Collection<CustomAttributeBuilder> attributeBuilders = new Collection<CustomAttributeBuilder>();
36-
foreach (var attribute in _attributes)
37+
foreach (var attribute in Attributes)
3738
{
3839
CustomAttributeBuilder builder = GetAttributeBuilder(attribute);
3940
attributeBuilders.Add(builder);
@@ -44,7 +45,7 @@ public override Collection<CustomAttributeBuilder> GetCustomAttributes(Type para
4445

4546
public override async Task BindAsync(BindingContext context)
4647
{
47-
context.Attributes = _attributes.ToArray();
48+
context.Attributes = Attributes.ToArray();
4849

4950
if (_binding.DefaultType == typeof(IAsyncCollector<byte[]>))
5051
{

src/WebJobs.Script/Binding/Http/HttpConfiguration.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System.Threading.Tasks.Dataflow;
5+
using Microsoft.Azure.WebJobs.Host.Config;
56

67
namespace Microsoft.Azure.WebJobs.Script.Binding.Http
78
{
8-
public class HttpConfiguration
9+
public class HttpConfiguration : IExtensionConfigProvider
910
{
1011
public HttpConfiguration()
1112
{
@@ -40,5 +41,10 @@ public HttpConfiguration()
4041
/// checks should be enabled.
4142
/// </summary>
4243
public bool DynamicThrottlesEnabled { get; set; }
44+
45+
public void Initialize(ExtensionConfigContext context)
46+
{
47+
context.Config.RegisterBindingExtension(new HttpTriggerAttributeBindingProvider());
48+
}
4349
}
4450
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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.Binding.Http;
6+
7+
namespace Microsoft.Azure.WebJobs
8+
{
9+
/// <summary>
10+
/// Extension methods for Http integration
11+
/// </summary>
12+
public static class HttpJobHostConfigurationExtensions
13+
{
14+
/// <summary>
15+
/// Enables use of Http extensions
16+
/// </summary>
17+
/// <param name="config">The <see cref="JobHostConfiguration"/> to configure.</param>
18+
/// <param name="httpConfig">The <see cref="HttpConfiguration"/> to use.</param>
19+
public static void UseHttp(this JobHostConfiguration config, HttpConfiguration httpConfig = null)
20+
{
21+
if (config == null)
22+
{
23+
throw new ArgumentNullException("config");
24+
}
25+
26+
if (httpConfig == null)
27+
{
28+
httpConfig = new HttpConfiguration();
29+
}
30+
31+
config.RegisterExtensionConfigProvider(httpConfig);
32+
}
33+
}
34+
}

src/WebJobs.Script/Binding/Http/HttpTriggerAttribute.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,46 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
5+
using Microsoft.Azure.WebJobs.Description;
56

67
namespace Microsoft.Azure.WebJobs.Script.Binding
78
{
9+
[Binding]
810
[AttributeUsage(AttributeTargets.Parameter)]
911
public sealed class HttpTriggerAttribute : Attribute
1012
{
1113
public HttpTriggerAttribute()
1214
{
15+
AuthLevel = AuthorizationLevel.Function;
1316
}
1417

15-
public string RouteTemplate { get; set; }
18+
public HttpTriggerAttribute(AuthorizationLevel authLevel, params string[] methods)
19+
{
20+
AuthLevel = authLevel;
21+
Methods = methods;
22+
}
23+
24+
/// <summary>
25+
/// Gets or sets the route template for the function. Can include
26+
/// route parameters using WebApi supported syntax. If not specified,
27+
/// will default to the function name.
28+
/// See: https://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#constraints
29+
/// </summary>
30+
public string Route { get; set; }
31+
32+
/// <summary>
33+
/// Gets the http methods that are supported for the function.
34+
/// </summary>
35+
public string[] Methods { get; private set; }
36+
37+
/// <summary>
38+
/// Gets the authorization level for the function.
39+
/// </summary>
40+
public AuthorizationLevel AuthLevel { get; private set; }
41+
42+
/// <summary>
43+
/// Gets or sets the WebHook type, if this function represents a WebHook.
44+
/// </summary>
45+
public string WebHookType { get; set; }
1646
}
1747
}

src/WebJobs.Script/Binding/Http/HttpTriggerAttributeBindingProvider.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ public HttpTriggerBinding(HttpTriggerAttribute attribute, ParameterInfo paramete
8080
}
8181

8282
// add any route parameters to the contract
83-
if (!string.IsNullOrEmpty(attribute.RouteTemplate))
83+
if (!string.IsNullOrEmpty(attribute.Route))
8484
{
85-
var routeParameters = _httpRouteFactory.GetRouteParameters(attribute.RouteTemplate);
85+
var routeParameters = _httpRouteFactory.GetRouteParameters(attribute.Route);
8686
var parameters = ((MethodInfo)parameter.Member).GetParameters().ToDictionary(p => p.Name, p => p.ParameterType, StringComparer.OrdinalIgnoreCase);
8787
foreach (string parameterName in routeParameters)
8888
{

0 commit comments

Comments
 (0)