diff --git a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs
index 88fcda9b91..789322d3f6 100644
--- a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs
+++ b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs
@@ -26,6 +26,7 @@
using Microsoft.Azure.WebJobs.Script.Grpc.Eventing;
using Microsoft.Azure.WebJobs.Script.Grpc.Extensions;
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
+using Microsoft.Azure.WebJobs.Script.HttpProxyService;
using Microsoft.Azure.WebJobs.Script.ManagedDependencies;
using Microsoft.Azure.WebJobs.Script.Workers;
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
diff --git a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannelFactory.cs b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannelFactory.cs
index 5bdb4386e2..b808325b72 100644
--- a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannelFactory.cs
+++ b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannelFactory.cs
@@ -8,6 +8,7 @@
using Microsoft.Azure.WebJobs.Script.Diagnostics;
using Microsoft.Azure.WebJobs.Script.Eventing;
using Microsoft.Azure.WebJobs.Script.Grpc.Eventing;
+using Microsoft.Azure.WebJobs.Script.HttpProxyService;
using Microsoft.Azure.WebJobs.Script.Workers;
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
using Microsoft.Azure.WebJobs.Script.Workers.SharedMemoryDataTransfer;
diff --git a/src/WebJobs.Script.Grpc/GrpcServiceCollectionsExtensions.cs b/src/WebJobs.Script.Grpc/GrpcServiceCollectionsExtensions.cs
index 19a8578263..3e148d0513 100644
--- a/src/WebJobs.Script.Grpc/GrpcServiceCollectionsExtensions.cs
+++ b/src/WebJobs.Script.Grpc/GrpcServiceCollectionsExtensions.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
+using Microsoft.Azure.WebJobs.Script.HttpProxyService;
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
using Microsoft.Extensions.DependencyInjection;
diff --git a/src/WebJobs.Script.Grpc/WebJobs.Script.Grpc.csproj b/src/WebJobs.Script.Grpc/WebJobs.Script.Grpc.csproj
index d544f908e5..cb059eea7b 100644
--- a/src/WebJobs.Script.Grpc/WebJobs.Script.Grpc.csproj
+++ b/src/WebJobs.Script.Grpc/WebJobs.Script.Grpc.csproj
@@ -18,7 +18,6 @@
-
diff --git a/src/WebJobs.Script.WebHost/Middleware/FunctionInvocationMiddleware.cs b/src/WebJobs.Script.WebHost/Middleware/FunctionInvocationMiddleware.cs
index 2d4ff1b23f..ee18bb9504 100644
--- a/src/WebJobs.Script.WebHost/Middleware/FunctionInvocationMiddleware.cs
+++ b/src/WebJobs.Script.WebHost/Middleware/FunctionInvocationMiddleware.cs
@@ -4,10 +4,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization.Policy;
-using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
@@ -57,9 +55,9 @@ public async Task Invoke(HttpContext context)
int nestedProxiesCount = GetNestedProxiesCount(context, functionExecution);
IActionResult result = await GetResultAsync(context, functionExecution);
- if (context.Items.TryGetValue(ScriptConstants.HttpProxyingEnabled, out var value))
+ if (context.Items.TryGetValue(ScriptConstants.HttpProxyingEnabled, out var httpProxyingEnabled))
{
- if (value?.ToString() == bool.TrueString)
+ if (httpProxyingEnabled?.ToString() == bool.TrueString)
{
return;
}
diff --git a/src/WebJobs.Script.Grpc/Exceptions/HttpForwardingException.cs b/src/WebJobs.Script/Exceptions/HttpForwardingException.cs
similarity index 90%
rename from src/WebJobs.Script.Grpc/Exceptions/HttpForwardingException.cs
rename to src/WebJobs.Script/Exceptions/HttpForwardingException.cs
index b60daba49d..d00a25c2a7 100644
--- a/src/WebJobs.Script.Grpc/Exceptions/HttpForwardingException.cs
+++ b/src/WebJobs.Script/Exceptions/HttpForwardingException.cs
@@ -3,7 +3,7 @@
using System;
-namespace Microsoft.Azure.WebJobs.Script.Grpc.Exceptions
+namespace Microsoft.Azure.WebJobs.Script.Exceptions
{
internal class HttpForwardingException : Exception
{
diff --git a/src/WebJobs.Script.Grpc/Server/DefaultHttpProxyService.cs b/src/WebJobs.Script/HttpProxyService/DefaultHttpProxyService.cs
similarity index 97%
rename from src/WebJobs.Script.Grpc/Server/DefaultHttpProxyService.cs
rename to src/WebJobs.Script/HttpProxyService/DefaultHttpProxyService.cs
index 1d164c2c04..c7f2b7b9e9 100644
--- a/src/WebJobs.Script.Grpc/Server/DefaultHttpProxyService.cs
+++ b/src/WebJobs.Script/HttpProxyService/DefaultHttpProxyService.cs
@@ -7,12 +7,12 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Script.Description;
-using Microsoft.Azure.WebJobs.Script.Grpc.Exceptions;
+using Microsoft.Azure.WebJobs.Script.Exceptions;
using Microsoft.Azure.WebJobs.Script.Workers;
using Microsoft.Extensions.Logging;
using Yarp.ReverseProxy.Forwarder;
-namespace Microsoft.Azure.WebJobs.Script.Grpc
+namespace Microsoft.Azure.WebJobs.Script.HttpProxyService
{
internal class DefaultHttpProxyService : IHttpProxyService, IDisposable
{
diff --git a/src/WebJobs.Script.Grpc/Server/IHttpProxyService.cs b/src/WebJobs.Script/HttpProxyService/IHttpProxyService.cs
similarity index 93%
rename from src/WebJobs.Script.Grpc/Server/IHttpProxyService.cs
rename to src/WebJobs.Script/HttpProxyService/IHttpProxyService.cs
index 7db2eeaf12..6f17d3f8e9 100644
--- a/src/WebJobs.Script.Grpc/Server/IHttpProxyService.cs
+++ b/src/WebJobs.Script/HttpProxyService/IHttpProxyService.cs
@@ -5,7 +5,7 @@
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Script.Description;
-namespace Microsoft.Azure.WebJobs.Script.Grpc
+namespace Microsoft.Azure.WebJobs.Script.HttpProxyService
{
public interface IHttpProxyService
{
diff --git a/src/WebJobs.Script.Grpc/Server/RetryProxyHandler.cs b/src/WebJobs.Script/HttpProxyService/RetryProxyHandler.cs
similarity index 97%
rename from src/WebJobs.Script.Grpc/Server/RetryProxyHandler.cs
rename to src/WebJobs.Script/HttpProxyService/RetryProxyHandler.cs
index 8659a0403b..3bbed3ae4c 100644
--- a/src/WebJobs.Script.Grpc/Server/RetryProxyHandler.cs
+++ b/src/WebJobs.Script/HttpProxyService/RetryProxyHandler.cs
@@ -7,7 +7,7 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
-namespace Microsoft.Azure.WebJobs.Script.Grpc
+namespace Microsoft.Azure.WebJobs.Script.HttpProxyService
{
internal sealed class RetryProxyHandler : DelegatingHandler
{
diff --git a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs
index 278bf4a78f..372c5954d2 100644
--- a/src/WebJobs.Script/ScriptHostBuilderExtensions.cs
+++ b/src/WebJobs.Script/ScriptHostBuilderExtensions.cs
@@ -32,6 +32,7 @@
using Microsoft.Azure.WebJobs.Script.ExtensionBundle;
using Microsoft.Azure.WebJobs.Script.FileProvisioning;
using Microsoft.Azure.WebJobs.Script.Http;
+using Microsoft.Azure.WebJobs.Script.HttpProxyService;
using Microsoft.Azure.WebJobs.Script.ManagedDependencies;
using Microsoft.Azure.WebJobs.Script.Scale;
using Microsoft.Azure.WebJobs.Script.Workers;
@@ -289,6 +290,11 @@ public static IHostBuilder AddScriptHostCore(this IHostBuilder builder, ScriptAp
// Core WebJobs/Script Host services
services.AddSingleton();
+ // Add http proxying services - this is used with http streaming workers and custom handlers when enabled
+ // http streaming capabilities are known following worker initialization so that info isn't available at this stage
+ services.AddHttpForwarder();
+ services.AddSingleton();
+
// HTTP Worker
services.AddSingleton();
services.AddSingleton();
diff --git a/src/WebJobs.Script/WebJobs.Script.csproj b/src/WebJobs.Script/WebJobs.Script.csproj
index bcccab5b9c..2117fb7ad7 100644
--- a/src/WebJobs.Script/WebJobs.Script.csproj
+++ b/src/WebJobs.Script/WebJobs.Script.csproj
@@ -73,6 +73,7 @@
+
diff --git a/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptions.cs b/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptions.cs
index efd253febc..df3de0bd3a 100644
--- a/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptions.cs
+++ b/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptions.cs
@@ -15,8 +15,16 @@ public class HttpWorkerOptions
public int Port { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the host will rebuild the initial invocation HTTP Request and send the copy to the worker process.
+ ///
public bool EnableForwardingHttpRequest { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the host will proxy the invocation HTTP request to the worker process.
+ ///
+ public bool EnableProxyingHttpRequest { get; set; }
+
public TimeSpan InitializationTimeout { get; set; } = TimeSpan.FromSeconds(30);
}
}
diff --git a/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptionsSetup.cs b/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptionsSetup.cs
index c2817d291e..78c3b62ab5 100644
--- a/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptionsSetup.cs
+++ b/src/WebJobs.Script/Workers/Http/Configuration/HttpWorkerOptionsSetup.cs
@@ -46,7 +46,7 @@ public void Configure(HttpWorkerOptions options)
ConfigureWorkerDescription(options, customHandlerSection);
if (options.Type == CustomHandlerType.None)
{
- // CustomHandlerType.None is only for maitaining backward compatibilty with httpWorker section.
+ // CustomHandlerType.None is only for maintaining backward compatability with httpWorker section.
_logger.LogWarning($"CustomHandlerType {CustomHandlerType.None} is not supported. Defaulting to {CustomHandlerType.Http}.");
options.Type = CustomHandlerType.Http;
}
diff --git a/src/WebJobs.Script/Workers/Http/DefaultHttpWorkerService.cs b/src/WebJobs.Script/Workers/Http/DefaultHttpWorkerService.cs
index 825a8c24c7..c83d54196a 100644
--- a/src/WebJobs.Script/Workers/Http/DefaultHttpWorkerService.cs
+++ b/src/WebJobs.Script/Workers/Http/DefaultHttpWorkerService.cs
@@ -13,6 +13,7 @@
using Microsoft.Azure.WebJobs.Script.Description;
using Microsoft.Azure.WebJobs.Script.Diagnostics.Extensions;
using Microsoft.Azure.WebJobs.Script.Extensions;
+using Microsoft.Azure.WebJobs.Script.HttpProxyService;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -24,17 +25,24 @@ public class DefaultHttpWorkerService : IHttpWorkerService
private readonly HttpWorkerOptions _httpWorkerOptions;
private readonly ILogger _logger;
private readonly bool _enableRequestTracing;
-
- public DefaultHttpWorkerService(IOptions httpWorkerOptions, ILoggerFactory loggerFactory, IEnvironment environment, IOptions scriptHostOptions)
- : this(CreateHttpClient(httpWorkerOptions), httpWorkerOptions, loggerFactory.CreateLogger(), environment, scriptHostOptions)
+ private readonly IHttpProxyService _httpProxyService;
+ private readonly ScriptInvocationResult _successfulInvocationResult;
+ private readonly Uri _destinationPrefix;
+ private readonly string _userAgentString;
+
+ public DefaultHttpWorkerService(IOptions httpWorkerOptions, ILoggerFactory loggerFactory, IEnvironment environment,
+ IOptions scriptHostOptions, IHttpProxyService httpProxyService)
+ : this(CreateHttpClient(httpWorkerOptions), httpWorkerOptions, loggerFactory.CreateLogger(), environment, scriptHostOptions, httpProxyService)
{
}
- internal DefaultHttpWorkerService(HttpClient httpClient, IOptions httpWorkerOptions, ILogger logger, IEnvironment environment, IOptions scriptHostOptions)
+ internal DefaultHttpWorkerService(HttpClient httpClient, IOptions httpWorkerOptions, ILogger logger, IEnvironment environment,
+ IOptions scriptHostOptions, IHttpProxyService httpProxyService)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_httpWorkerOptions = httpWorkerOptions.Value ?? throw new ArgumentNullException(nameof(httpWorkerOptions.Value));
+ _httpProxyService = httpProxyService ?? throw new ArgumentNullException(nameof(httpProxyService));
_enableRequestTracing = environment.IsCoreTools();
if (scriptHostOptions.Value.FunctionTimeout == null)
{
@@ -47,6 +55,14 @@ internal DefaultHttpWorkerService(HttpClient httpClient, IOptions()
+ };
+
+ _destinationPrefix = new UriBuilder(WorkerConstants.HttpScheme, WorkerConstants.HostName, _httpWorkerOptions.Port).Uri;
+ _userAgentString = $"{HttpWorkerConstants.UserAgentHeaderValue}/{ScriptHost.Version}";
}
private static HttpClient CreateHttpClient(IOptions httpWorkerOptions)
@@ -61,6 +77,11 @@ public Task InvokeAsync(ScriptInvocationContext scriptInvocationContext)
{
if (scriptInvocationContext.FunctionMetadata.IsHttpInAndOutFunction())
{
+ if (_httpWorkerOptions.EnableProxyingHttpRequest)
+ {
+ return ProxyInvocationRequest(scriptInvocationContext);
+ }
+
// type is empty for httpWorker section. EnableForwardingHttpRequest is opt-in for custom handler section.
if (_httpWorkerOptions.Type == CustomHandlerType.None || _httpWorkerOptions.EnableForwardingHttpRequest)
{
@@ -71,6 +92,29 @@ public Task InvokeAsync(ScriptInvocationContext scriptInvocationContext)
return ProcessDefaultInvocationRequest(scriptInvocationContext);
}
+ internal async Task ProxyInvocationRequest(ScriptInvocationContext scriptInvocationContext)
+ {
+ try
+ {
+ if (!scriptInvocationContext.TryGetHttpRequest(out HttpRequest httpRequest))
+ {
+ throw new InvalidOperationException($"Cannot proxy the HttpTrigger function {scriptInvocationContext.FunctionMetadata.Name} without an input of type {nameof(HttpRequest)}.");
+ }
+
+ AddProxyingHeaders(httpRequest, scriptInvocationContext.ExecutionContext.InvocationId.ToString());
+
+ // YARP only requires the destination prefix. The path and query string are added by the YARP proxy during SendAsync using info from the HttpContext.
+ _httpProxyService.StartForwarding(scriptInvocationContext, _destinationPrefix);
+
+ await _httpProxyService.EnsureSuccessfulForwardingAsync(scriptInvocationContext);
+ scriptInvocationContext.ResultSource.SetResult(_successfulInvocationResult);
+ }
+ catch (Exception exc)
+ {
+ scriptInvocationContext.ResultSource.TrySetException(exc);
+ }
+ }
+
internal async Task ProcessHttpInAndOutInvocationRequest(ScriptInvocationContext scriptInvocationContext)
{
_logger.CustomHandlerForwardingHttpTriggerInvocation(scriptInvocationContext.FunctionMetadata.Name, scriptInvocationContext.ExecutionContext.InvocationId);
@@ -162,7 +206,15 @@ internal void AddHeaders(HttpRequestMessage httpRequest, string invocationId)
{
httpRequest.Headers.Add(HttpWorkerConstants.HostVersionHeaderName, ScriptHost.Version);
httpRequest.Headers.Add(HttpWorkerConstants.InvocationIdHeaderName, invocationId);
- httpRequest.Headers.UserAgent.ParseAdd($"{HttpWorkerConstants.UserAgentHeaderValue}/{ScriptHost.Version}");
+ httpRequest.Headers.UserAgent.ParseAdd(_userAgentString);
+ }
+
+ private void AddProxyingHeaders(HttpRequest httpRequest, string invocationId)
+ {
+ // if there are existing headers, override them
+ httpRequest.Headers[HttpWorkerConstants.HostVersionHeaderName] = ScriptHost.Version;
+ httpRequest.Headers[HttpWorkerConstants.InvocationIdHeaderName] = invocationId;
+ httpRequest.Headers.UserAgent = _userAgentString;
}
internal string GetPathValue(HttpWorkerOptions httpWorkerOptions, string functionName, HttpRequest httpRequest)
diff --git a/test/WebJobs.Script.Tests.Integration/ApplicationInsights/ApplicationInsightsTestFixture.cs b/test/WebJobs.Script.Tests.Integration/ApplicationInsights/ApplicationInsightsTestFixture.cs
index a927b6a82b..42dde6d50b 100644
--- a/test/WebJobs.Script.Tests.Integration/ApplicationInsights/ApplicationInsightsTestFixture.cs
+++ b/test/WebJobs.Script.Tests.Integration/ApplicationInsights/ApplicationInsightsTestFixture.cs
@@ -11,6 +11,7 @@
using Microsoft.Azure.WebJobs.Script.Diagnostics;
using Microsoft.Azure.WebJobs.Script.Eventing;
using Microsoft.Azure.WebJobs.Script.Grpc;
+using Microsoft.Azure.WebJobs.Script.HttpProxyService;
using Microsoft.Azure.WebJobs.Script.Workers;
using Microsoft.Azure.WebJobs.Script.Workers.Rpc;
using Microsoft.Azure.WebJobs.Script.Workers.SharedMemoryDataTransfer;
diff --git a/test/WebJobs.Script.Tests/HttpProxyService/DefaultHttpProxyServiceTests.cs b/test/WebJobs.Script.Tests/HttpProxyService/DefaultHttpProxyServiceTests.cs
index 3356ed2017..6dfa7b1aba 100644
--- a/test/WebJobs.Script.Tests/HttpProxyService/DefaultHttpProxyServiceTests.cs
+++ b/test/WebJobs.Script.Tests/HttpProxyService/DefaultHttpProxyServiceTests.cs
@@ -6,7 +6,7 @@
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.WebJobs.Script.Description;
-using Microsoft.Azure.WebJobs.Script.Grpc;
+using Microsoft.Azure.WebJobs.Script.HttpProxyService;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
diff --git a/test/WebJobs.Script.Tests/HttpWorker/DefaultHttpWorkerServiceTests.cs b/test/WebJobs.Script.Tests/HttpWorker/DefaultHttpWorkerServiceTests.cs
index 66435a166c..0d06160f47 100644
--- a/test/WebJobs.Script.Tests/HttpWorker/DefaultHttpWorkerServiceTests.cs
+++ b/test/WebJobs.Script.Tests/HttpWorker/DefaultHttpWorkerServiceTests.cs
@@ -12,9 +12,13 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
+using Microsoft.Azure.WebJobs.Script.Description;
+using Microsoft.Azure.WebJobs.Script.Exceptions;
using Microsoft.Azure.WebJobs.Script.Extensions;
+using Microsoft.Azure.WebJobs.Script.HttpProxyService;
using Microsoft.Azure.WebJobs.Script.Workers;
using Microsoft.Azure.WebJobs.Script.Workers.Http;
+using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
@@ -39,6 +43,7 @@ public class DefaultHttpWorkerServiceTests
private int _defaultPort = 8090;
private TestLogger _testLogger = new TestLogger("ServiceLogger");
private TestLogger _functionLogger = new TestLogger(TestFunctionName);
+ private Mock _mockHttpProxyService;
public DefaultHttpWorkerServiceTests()
{
@@ -54,6 +59,7 @@ public DefaultHttpWorkerServiceTests()
{
FunctionTimeout = TimeSpan.FromMinutes(15)
};
+ _mockHttpProxyService = new Mock(MockBehavior.Strict);
}
public static IEnumerable