Skip to content

Commit

Permalink
Several proxy fixes as described in:
Browse files Browse the repository at this point in the history
  • Loading branch information
safihamid committed Dec 7, 2017
1 parent 88360e7 commit 96d0178
Show file tree
Hide file tree
Showing 16 changed files with 108 additions and 110 deletions.
2 changes: 1 addition & 1 deletion src/WebJobs.Script.NuGet/WebJobs.Script.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<dependency id="FSharp.Core" version="4.0.0.1" />
<dependency id="Microsoft.Azure.WebJobs" version="2.1.0-beta4-11100" />
<dependency id="Microsoft.ApplicationInsights.WindowsServer" version="2.4.1" />
<dependency id="Microsoft.Azure.AppService.Proxy" version="0.3.2.20" />
<dependency id="Microsoft.Azure.AppService.Proxy" version="1.0.0.1" />
<dependency id="Microsoft.Azure.ServiceBus.EventProcessorHost" version="2.2.10" />
<dependency id="Microsoft.Azure.WebJobs.Extensions.ApiHub" version="1.0.0-beta6-10554" />
<dependency id="Microsoft.Azure.WebJobs.Extensions.BotFramework" version="1.0.15-beta" />
Expand Down
3 changes: 2 additions & 1 deletion src/WebJobs.Script.WebHost/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,5 @@
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.WebHost.DefaultSecretManagerFactory.#Create(Microsoft.Azure.WebJobs.Script.Config.ScriptSettingsManager,Microsoft.Azure.WebJobs.Host.TraceWriter,Microsoft.Extensions.Logging.ILoggerFactory,Microsoft.Azure.WebJobs.Script.WebHost.ISecretsRepository)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.WebHost.SecretManager.#.ctor(Microsoft.Azure.WebJobs.Script.WebHost.ISecretsRepository,Microsoft.Azure.WebJobs.Script.WebHost.IKeyValueConverterFactory,Microsoft.Azure.WebJobs.Host.TraceWriter,Microsoft.Extensions.Logging.ILoggerFactory,System.Boolean)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "host", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.WebHost.StandbyManager.#WarmUp(Microsoft.Azure.WebJobs.Script.ScriptHost)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "settings", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.WebHost.WebHostResolver.#GetPerformanceManager(Microsoft.Azure.WebJobs.Script.WebHost.WebHostSettings)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "settings", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.WebHost.WebHostResolver.#GetPerformanceManager(Microsoft.Azure.WebJobs.Script.WebHost.WebHostSettings)")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "_httpFunctionsOnlyRoutes", Scope = "member", Target = "Microsoft.Azure.WebJobs.Script.WebHost.WebScriptHostManager.#Dispose(System.Boolean)")]
70 changes: 2 additions & 68 deletions src/WebJobs.Script.WebHost/ProxyFunctionExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public class ProxyFunctionExecutor : IFuncExecutor
{
private readonly WebScriptHostManager _scriptHostManager;
private readonly ISecretManager _secretManager;
private readonly string _httpPrefix;

private WebHookReceiverManager _webHookReceiverManager;

Expand All @@ -31,81 +30,16 @@ internal ProxyFunctionExecutor(WebScriptHostManager scriptHostManager, WebHookRe
_scriptHostManager = scriptHostManager;
_webHookReceiverManager = webHookReceiverManager;
_secretManager = secretManager;

_httpPrefix = HttpExtensionConstants.DefaultRoutePrefix;

if (_scriptHostManager.Instance != null)
{
var json = _scriptHostManager.Instance.ScriptConfig.HostConfig.HostConfigMetadata;

if (json != null && json["http"] != null && json["http"]["routePrefix"] != null)
{
_httpPrefix = json["http"]["routePrefix"].ToString().Trim(new char[] { '/' });
}
}
}

public async Task ExecuteFuncAsync(string funcName, Dictionary<string, object> arguments, CancellationToken cancellationToken)
{
HttpRequestMessage request = arguments[ScriptConstants.AzureFunctionsHttpRequestKey] as HttpRequestMessage;

FunctionDescriptor function = null;
var path = request.RequestUri.AbsolutePath.Trim(new char[] { '/' });

if (path.StartsWith(_httpPrefix))
{
path = path.Remove(0, _httpPrefix.Length);
}

path = path.Trim(new char[] { '/' });

// This is a call to the local function app, before handing the route to asp.net to pick the FunctionDescriptor the following will be run:
// 1. If the request maps to a local http trigger function name then that function will be picked.
// 2. Else if the request maps to a custom route of a local http trigger function then that function will be picked
// 3. Otherwise the request will be given to asp.net to pick the appropriate route.
foreach (var func in _scriptHostManager.HttpFunctions.Values)
{
if (!func.Metadata.IsProxy)
{
if (path.Equals(func.Metadata.Name, StringComparison.OrdinalIgnoreCase))
{
function = func;
break;
}
else
{
foreach (var binding in func.InputBindings)
{
if (binding.Metadata.IsTrigger)
{
string functionRoute = null;
var jsonContent = binding.Metadata.Raw;
if (jsonContent != null && jsonContent["route"] != null)
{
functionRoute = jsonContent["route"].ToString();
}

// BUG: Known issue, This does not work on dynamic routes like products/{category:alpha}/{id:int?}
if (!string.IsNullOrEmpty(functionRoute) && path.Equals(functionRoute, StringComparison.OrdinalIgnoreCase))
{
function = func;
break;
}
}
}

if (function != null)
{
break;
}
}
}
}

if (function == null)
{
function = _scriptHostManager.GetHttpFunctionOrNull(request);
}
// This is a call to the local function app from proxy, in this scenario first match will be against local http triggers then rest of the proxies to avoid infinite redirect for * mappings in proxies.
function = _scriptHostManager.GetHttpFunctionOrNull(request, functionRoutesFirst: true);

var functionRequestInvoker = new FunctionRequestInvoker(function, _secretManager);
var response = await functionRequestInvoker.PreprocessRequestAsync(request);
Expand Down
14 changes: 7 additions & 7 deletions src/WebJobs.Script.WebHost/WebJobs.Script.WebHost.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
<HintPath>..\..\packages\Microsoft.ApplicationInsights.2.4.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.Razor, Version=4.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.0.3.2.20\lib\net461\Microsoft.AspNet.Razor.dll</HintPath>
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.1.0.0.1\lib\net461\Microsoft.AspNet.Razor.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.WebHooks.Common, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.AspNet.WebHooks.Common.1.2.0\lib\net45\Microsoft.AspNet.WebHooks.Common.dll</HintPath>
Expand Down Expand Up @@ -176,14 +176,14 @@
<HintPath>..\..\packages\Microsoft.Azure.ApiHub.Sdk.0.7.2-alpha\lib\net45\Microsoft.Azure.ApiHub.Sdk.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Azure.AppService.Proxy.Client, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.0.3.2.20\lib\net461\Microsoft.Azure.AppService.Proxy.Client.dll</HintPath>
<Reference Include="Microsoft.Azure.AppService.Proxy.Client, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.1.0.0.1\lib\net461\Microsoft.Azure.AppService.Proxy.Client.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Azure.AppService.Proxy.Common, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.0.3.2.20\lib\net461\Microsoft.Azure.AppService.Proxy.Common.dll</HintPath>
<Reference Include="Microsoft.Azure.AppService.Proxy.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.1.0.0.1\lib\net461\Microsoft.Azure.AppService.Proxy.Common.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Azure.AppService.Proxy.Runtime, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.0.3.2.20\lib\net461\Microsoft.Azure.AppService.Proxy.Runtime.dll</HintPath>
<Reference Include="Microsoft.Azure.AppService.Proxy.Runtime, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.1.0.0.1\lib\net461\Microsoft.Azure.AppService.Proxy.Runtime.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Azure.Documents.ChangeFeedProcessor, Version=1.14.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.DocumentDB.ChangeFeedProcessor.1.0.0\lib\net45\Microsoft.Azure.Documents.ChangeFeedProcessor.dll</HintPath>
Expand Down
30 changes: 28 additions & 2 deletions src/WebJobs.Script.WebHost/WebScriptHostManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class WebScriptHostManager : ScriptHostManager
private bool _hostStarted = false;
private IDictionary<IHttpRoute, FunctionDescriptor> _httpFunctions;
private HttpRouteCollection _httpRoutes;
private HttpRouteCollection _httpFunctionsOnlyRoutes;
private HttpRequestManager _httpRequestManager;

public WebScriptHostManager(ScriptHostConfiguration config,
Expand Down Expand Up @@ -218,6 +219,7 @@ protected override void Dispose(bool disposing)
{
(_secretManager as IDisposable)?.Dispose();
_metricsLogger?.Dispose();
_httpFunctionsOnlyRoutes?.Dispose();
_httpRoutes?.Dispose();
}

Expand Down Expand Up @@ -276,7 +278,7 @@ private static object GetWebHookData(Type dataType, WebHookHandlerContext contex
return getDataMethod.Invoke(null, new object[] { context });
}

public FunctionDescriptor GetHttpFunctionOrNull(HttpRequestMessage request)
public FunctionDescriptor GetHttpFunctionOrNull(HttpRequestMessage request, bool functionRoutesFirst = false)
{
if (request == null)
{
Expand All @@ -289,7 +291,25 @@ public FunctionDescriptor GetHttpFunctionOrNull(HttpRequestMessage request)
}

FunctionDescriptor function = null;
var routeData = _httpRoutes.GetRouteData(request);

// if true, the match will first be against local http trigger functions otherwise it will be against proxies and functions combined with proxies matching first which is the default behavior.
if (functionRoutesFirst)
{
function = GetFunctionDescriptorFromRoute(request, _httpFunctionsOnlyRoutes.GetRouteData(request));
}

if (function == null)
{
function = GetFunctionDescriptorFromRoute(request, _httpRoutes.GetRouteData(request));
}

return function;
}

private FunctionDescriptor GetFunctionDescriptorFromRoute(HttpRequestMessage request, IHttpRouteData routeData)
{
FunctionDescriptor function = null;

if (routeData != null)
{
_httpFunctions.TryGetValue(routeData.Route, out function);
Expand Down Expand Up @@ -392,6 +412,7 @@ private void InitializeHttpFunctions(IEnumerable<FunctionDescriptor> functions,

_httpFunctions = new Dictionary<IHttpRoute, FunctionDescriptor>();
_httpRoutes = new HttpRouteCollection();
_httpFunctionsOnlyRoutes = new HttpRouteCollection();

// Proxy routes will take precedence over http trigger functions and http trigger
// routes so they will be added first to the list of http routes.
Expand All @@ -413,6 +434,11 @@ private void InitializeHttpFunctions(IEnumerable<FunctionDescriptor> functions,
{
_httpFunctions.Add(httpRoute, function);
}

if (!function.Metadata.IsProxy)
{
functionHttpRouteFactory.TryAddRoute(function.Metadata.Name, httpTrigger.Route, httpMethods, _httpFunctionsOnlyRoutes, out httpRoute);
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/WebJobs.Script.WebHost/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<package id="Microsoft.AspNetCore.Http.Features" version="1.0.2" targetFramework="net46" />
<package id="Microsoft.AspNetCore.WebUtilities" version="1.0.0" targetFramework="net46" />
<package id="Microsoft.Azure.ApiHub.Sdk" version="0.7.2-alpha" targetFramework="net46" />
<package id="Microsoft.Azure.AppService.Proxy" version="0.3.2.20" targetFramework="net461" />
<package id="Microsoft.Azure.AppService.Proxy" version="1.0.0.1" targetFramework="net461" />
<package id="Microsoft.Azure.DocumentDB" version="1.13.2" targetFramework="net46" />
<package id="Microsoft.Azure.DocumentDB.ChangeFeedProcessor" version="1.0.0" targetFramework="net46" />
<package id="Microsoft.Azure.KeyVault.Core" version="1.0.0" targetFramework="net46" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal class ProxyFunctionInvoker : FunctionInvokerBase
private ProxyClientExecutor _proxyClient;

public ProxyFunctionInvoker(ScriptHost host, FunctionMetadata functionMetadata, ProxyClientExecutor proxyClient)
: base(host, functionMetadata, logDirName: "Proxy")
: base(host, functionMetadata)
{
_proxyClient = proxyClient;
}
Expand Down
14 changes: 7 additions & 7 deletions src/WebJobs.Script/WebJobs.Script.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,20 @@
<HintPath>..\..\packages\Microsoft.ApplicationInsights.2.4.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.Razor, Version=4.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.0.3.2.20\lib\net461\Microsoft.AspNet.Razor.dll</HintPath>
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.1.0.0.1\lib\net461\Microsoft.AspNet.Razor.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Azure.ApiHub.Sdk, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.ApiHub.Sdk.0.7.2-alpha\lib\net45\Microsoft.Azure.ApiHub.Sdk.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.Azure.AppService.Proxy.Client, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.0.3.2.20\lib\net461\Microsoft.Azure.AppService.Proxy.Client.dll</HintPath>
<Reference Include="Microsoft.Azure.AppService.Proxy.Client, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.1.0.0.1\lib\net461\Microsoft.Azure.AppService.Proxy.Client.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Azure.AppService.Proxy.Common, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.0.3.2.20\lib\net461\Microsoft.Azure.AppService.Proxy.Common.dll</HintPath>
<Reference Include="Microsoft.Azure.AppService.Proxy.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.1.0.0.1\lib\net461\Microsoft.Azure.AppService.Proxy.Common.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Azure.AppService.Proxy.Runtime, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.0.3.2.20\lib\net461\Microsoft.Azure.AppService.Proxy.Runtime.dll</HintPath>
<Reference Include="Microsoft.Azure.AppService.Proxy.Runtime, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.AppService.Proxy.1.0.0.1\lib\net461\Microsoft.Azure.AppService.Proxy.Runtime.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Azure.Documents.ChangeFeedProcessor, Version=1.14.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Azure.DocumentDB.ChangeFeedProcessor.1.0.0\lib\net45\Microsoft.Azure.Documents.ChangeFeedProcessor.dll</HintPath>
Expand Down
2 changes: 1 addition & 1 deletion src/WebJobs.Script/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.Azure.ApiHub.Sdk" version="0.7.2-alpha" targetFramework="net46" />
<package id="Microsoft.Azure.AppService.Proxy" version="0.3.2.20" targetFramework="net461" />
<package id="Microsoft.Azure.AppService.Proxy" version="1.0.0.1" targetFramework="net461" />
<package id="Microsoft.Azure.DocumentDB" version="1.13.2" targetFramework="net46" />
<package id="Microsoft.Azure.DocumentDB.ChangeFeedProcessor" version="1.0.0" targetFramework="net46" />
<package id="Microsoft.Azure.KeyVault.Core" version="1.0.0" targetFramework="net46" />
Expand Down
22 changes: 20 additions & 2 deletions test/WebJobs.Script.Tests.Integration/ProxyEndToEndTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,23 @@ public async Task CatchAllApis()
}

[Fact]
public async Task CatchAll()
public async Task CatchAllWithCustomRoutes()
{
HttpResponseMessage response = await _fixture.HttpClient.GetAsync($"proxy/blahblah");
HttpResponseMessage response = await _fixture.HttpClient.GetAsync($"proxy/api/myroute/mysubroute");

string content = await response.Content.ReadAsStringAsync();
Assert.Equal("200", response.StatusCode.ToString("D"));
Assert.Equal("Pong", content);
}

[Fact]
public async Task CatchAllWithCustomRoutesWithInvalidVerb()
{
HttpResponseMessage response = await _fixture.HttpClient.PutAsync($"proxy/api/myroute/mysubroute", null);

Assert.Equal("404", response.StatusCode.ToString("D"));
}

[Fact]
public async Task LongRoute()
{
Expand All @@ -116,6 +124,16 @@ public async Task LongRoute()
Assert.Equal("200", response.StatusCode.ToString("D"));
}

[Fact]
public async Task ProxyCallingLocalProxy()
{
HttpResponseMessage response = await _fixture.HttpClient.GetAsync($"/pr1/api/Ping");

string content = await response.Content.ReadAsStringAsync();
Assert.Equal("200", response.StatusCode.ToString("D"));
Assert.Equal("Pong", content);
}

public class TestFixture : IDisposable
{
private readonly ScriptSettingsManager _settingsManager;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"name": "req",
"type": "httpTrigger",
"direction": "in",
"route": "myroute"
"route": "myroute/mysubroute"
},
{
"name": "$return",
Expand Down
Loading

0 comments on commit 96d0178

Please sign in to comment.