do not wait for coordinator if not HttpTrigger (#1626)

This commit is contained in:
Brett Samblanet 2023-06-13 14:06:10 -07:00 коммит произвёл Fabio Cavalcante
Родитель 3ab48709df
Коммит f4c854ea7e
2 изменённых файлов: 68 добавлений и 24 удалений

Просмотреть файл

@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
@ -12,7 +14,10 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore
{
internal class FunctionsHttpProxyingMiddleware : IFunctionsWorkerMiddleware
{
private const string HttpTrigger = "httpTrigger";
private readonly IHttpCoordinator _coordinator;
private readonly ConcurrentDictionary<string, bool> _isHttpTrigger = new();
public FunctionsHttpProxyingMiddleware(IHttpCoordinator httpCoordinator)
{
@ -21,9 +26,16 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
// Only use the coordinator for HttpTriggers
if (!_isHttpTrigger.GetOrAdd(context.FunctionId, static (_, c) => IsHttpTriggerFunction(c), context))
{
await next(context);
return;
}
var invocationId = context.InvocationId;
// this call will block until the ASP.NET middleware pipleline has signaled that it's ready to run the function
// this call will block until the ASP.NET middleware pipeline has signaled that it's ready to run the function
var httpContext = await _coordinator.SetFunctionContextAsync(invocationId, context);
AddHttpContextToFunctionContext(context, httpContext);
@ -45,5 +57,11 @@ namespace Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore
{
funcContext.Items.Add(Constants.HttpContextKey, httpContext);
}
private static bool IsHttpTriggerFunction(FunctionContext funcContext)
{
return funcContext.FunctionDefinition.InputBindings
.Any(p => p.Value.Type.Equals(HttpTrigger, System.StringComparison.OrdinalIgnoreCase));
}
}
}

Просмотреть файл

@ -14,39 +14,65 @@ namespace Microsoft.Azure.Functions.Worker.Tests.AspNetCore
{
public class FunctionsHttpProxyingMiddlewareTests
{
private TestFunctionContext _functionContext;
private HttpContext _httpContext;
private Mock<IHttpCoordinator> _mockCoordinator;
public FunctionsHttpProxyingMiddlewareTests()
[Fact]
public async Task Middleware_AddsHttpContextToFunctionContext_Success()
{
_functionContext = new TestFunctionContext(new TestFunctionDefinition(), new TestFunctionInvocation(), CancellationToken.None);
_functionContext.Items = new Dictionary<object, object>();
var test = SetupInputs("httpTrigger");
var mockDelegate = new Mock<FunctionExecutionDelegate>();
_httpContext = new DefaultHttpContext();
_httpContext.Request.Headers.Add(Constants.CorrelationHeader, _functionContext.InvocationId);
var funcMiddleware = new FunctionsHttpProxyingMiddleware(test.MockCoordinator.Object);
await funcMiddleware.Invoke(test.FunctionContext, mockDelegate.Object);
_mockCoordinator = new Mock<IHttpCoordinator>();
_mockCoordinator
.Setup(p => p.SetHttpContextAsync(_functionContext.InvocationId, _httpContext))
.ReturnsAsync(_functionContext);
_mockCoordinator
.Setup(p => p.SetFunctionContextAsync(_functionContext.InvocationId, _functionContext))
.ReturnsAsync(_httpContext);
Assert.NotNull(test.FunctionContext.GetHttpContext());
Assert.Equal(test.FunctionContext.GetHttpContext(), test.HttpContext);
mockDelegate.Verify(p => p.Invoke(test.FunctionContext), Times.Once());
test.MockCoordinator.Verify(p => p.SetFunctionContextAsync(It.IsAny<string>(), It.IsAny<FunctionContext>()), Times.Once());
test.MockCoordinator.Verify(p => p.CompleteFunctionInvocation(It.IsAny<string>()), Times.Once());
}
[Fact]
public async Task MiddlewareAddsHttpContextToFunctionContext_Sucess()
public async Task Middleware_NoOpsOnNonHttpTriggers()
{
var funcMiddleware = new FunctionsHttpProxyingMiddleware(_mockCoordinator.Object);
var test = SetupInputs("someTrigger");
var mockDelegate = new Mock<FunctionExecutionDelegate>();
await funcMiddleware.Invoke(_functionContext, mockDelegate.Object);
var funcMiddleware = new FunctionsHttpProxyingMiddleware(test.MockCoordinator.Object);
await funcMiddleware.Invoke(test.FunctionContext, mockDelegate.Object);
Assert.NotNull(_functionContext.GetHttpContext());
Assert.Equal(_functionContext.GetHttpContext(), _httpContext);
mockDelegate.Verify(p => p.Invoke(test.FunctionContext), Times.Once());
test.MockCoordinator.Verify(p => p.SetFunctionContextAsync(It.IsAny<string>(), It.IsAny<FunctionContext>()), Times.Never());
test.MockCoordinator.Verify(p => p.CompleteFunctionInvocation(It.IsAny<string>()), Times.Never());
}
private static (FunctionContext FunctionContext, HttpContext HttpContext, Mock<IHttpCoordinator> MockCoordinator) SetupInputs(string triggerType)
{
var inputBindings = new Dictionary<string, BindingMetadata>()
{
{ "test", new TestBindingMetadata("test", triggerType, BindingDirection.In ) }
};
var functionDef = new TestFunctionDefinition(inputBindings: inputBindings);
var functionContext = new TestFunctionContext(functionDef, new TestFunctionInvocation(), CancellationToken.None)
{
Items = new Dictionary<object, object>()
};
var httpContext = new DefaultHttpContext();
httpContext.Request.Headers.Add(Constants.CorrelationHeader, functionContext.InvocationId);
var mockCoordinator = new Mock<IHttpCoordinator>();
mockCoordinator
.Setup(p => p.SetHttpContextAsync(functionContext.InvocationId, httpContext))
.ReturnsAsync(functionContext);
mockCoordinator
.Setup(p => p.SetFunctionContextAsync(functionContext.InvocationId, functionContext))
.ReturnsAsync(httpContext);
return new(functionContext, httpContext, mockCoordinator);
}
}
}