diff --git a/release_notes.md b/release_notes.md index b123c479ca..3ff0177864 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,6 +1,7 @@ -### Release notes - - -- Memory allocation optimizations in `ScriptStartupTypeLocator.GetExtensionsStartupTypesAsync` (#11012) +### Release notes + + +- Memory allocation optimizations in `ScriptStartupTypeLocator.GetExtensionsStartupTypesAsync` (#11012) +- Fix invocation timeout when incoming request contains "x-ms-invocation-id" header (#10980) diff --git a/src/WebJobs.Script.Grpc/Properties/AssemblyInfo.cs b/src/WebJobs.Script.Grpc/Properties/AssemblyInfo.cs index e3874ae556..6c2d8837b2 100644 --- a/src/WebJobs.Script.Grpc/Properties/AssemblyInfo.cs +++ b/src/WebJobs.Script.Grpc/Properties/AssemblyInfo.cs @@ -5,4 +5,5 @@ [assembly: InternalsVisibleTo("Microsoft.Azure.WebJobs.Script.Benchmarks")] [assembly: InternalsVisibleTo("Microsoft.Azure.WebJobs.Script.Tests")] -[assembly: InternalsVisibleTo("Microsoft.Azure.WebJobs.Script.Tests.Integration")] \ No newline at end of file +[assembly: InternalsVisibleTo("Microsoft.Azure.WebJobs.Script.Tests.Integration")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file diff --git a/src/WebJobs.Script.Grpc/Server/DefaultHttpProxyService.cs b/src/WebJobs.Script.Grpc/Server/DefaultHttpProxyService.cs index 235bbee981..1d164c2c04 100644 --- a/src/WebJobs.Script.Grpc/Server/DefaultHttpProxyService.cs +++ b/src/WebJobs.Script.Grpc/Server/DefaultHttpProxyService.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -96,8 +95,8 @@ public void StartForwarding(ScriptInvocationContext context, Uri httpUri) HttpContext httpContext = httpRequest.HttpContext; httpContext.Items[ScriptConstants.HttpProxyingEnabled] = bool.TrueString; - // add invocation id as correlation id - httpRequest.Headers.TryAdd(ScriptConstants.HttpProxyCorrelationHeader, context.ExecutionContext.InvocationId.ToString()); + // add invocation id as correlation id, override existing header if present + httpRequest.Headers[ScriptConstants.HttpProxyCorrelationHeader] = context.ExecutionContext.InvocationId.ToString(); var forwardingTask = _httpForwarder.SendAsync(httpContext, httpUri.ToString(), _messageInvoker, _forwarderRequestConfig).AsTask(); context.Properties[ScriptConstants.HttpProxyTask] = forwardingTask; diff --git a/test/WebJobs.Script.Tests/HttpProxyService/DefaultHttpProxyServiceTests.cs b/test/WebJobs.Script.Tests/HttpProxyService/DefaultHttpProxyServiceTests.cs new file mode 100644 index 0000000000..3356ed2017 --- /dev/null +++ b/test/WebJobs.Script.Tests/HttpProxyService/DefaultHttpProxyServiceTests.cs @@ -0,0 +1,82 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Http; +using Microsoft.Azure.WebJobs.Script.Description; +using Microsoft.Azure.WebJobs.Script.Grpc; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; +using Yarp.ReverseProxy.Forwarder; + +namespace Microsoft.Azure.WebJobs.Script.Tests +{ + public class DefaultHttpProxyServiceTests + { + private readonly Mock _httpForwarderMock; + private readonly Mock> _loggerMock; + private readonly DefaultHttpProxyService _proxyService; + + public DefaultHttpProxyServiceTests() + { + _httpForwarderMock = new Mock(); + _loggerMock = new Mock>(); + _proxyService = new DefaultHttpProxyService(_httpForwarderMock.Object, _loggerMock.Object); + } + + [Fact] + public void StartForwarding_SetsCorrelationHeader() + { + var httpContext = new DefaultHttpContext(); + httpContext.Items.Add(ScriptConstants.AzureFunctionsHttpTriggerContext, new()); + var invocationId = Guid.NewGuid(); + var context = new ScriptInvocationContext + { + FunctionMetadata = new FunctionMetadata { Name = "TestFunction" }, + ExecutionContext = new ExecutionContext { InvocationId = invocationId }, + Inputs = new List<(string Name, DataType Type, object Val)> + { + ("req", DataType.String, httpContext.Request) + }, + Properties = new Dictionary() + }; + + var httpUri = new Uri("http://localhost"); + + _proxyService.StartForwarding(context, httpUri); + + var httpRequest = (HttpRequest)context.Inputs.First().Val; + Assert.True(httpRequest.Headers.ContainsKey(ScriptConstants.HttpProxyCorrelationHeader)); + Assert.Equal(invocationId.ToString(), httpRequest.Headers[ScriptConstants.HttpProxyCorrelationHeader]); + } + + [Fact] + public void StartForwarding_OverridesExistingCorrelationHeader() + { + var httpContext = new DefaultHttpContext(); + httpContext.Items.Add(ScriptConstants.AzureFunctionsHttpTriggerContext, new()); + var invocationId = Guid.NewGuid(); + var context = new ScriptInvocationContext + { + FunctionMetadata = new FunctionMetadata { Name = "TestFunction" }, + ExecutionContext = new ExecutionContext { InvocationId = invocationId }, + Inputs = new List<(string Name, DataType Type, object Val)> + { + ("req", DataType.String, httpContext.Request) + }, + Properties = new Dictionary() + }; + + var httpUri = new Uri("http://localhost"); + var existingCorrelationId = Guid.NewGuid().ToString(); + var httpRequest = (HttpRequest)context.Inputs.First().Val; + httpRequest.Headers[ScriptConstants.HttpProxyCorrelationHeader] = existingCorrelationId; + + _proxyService.StartForwarding(context, httpUri); + Assert.Equal(invocationId.ToString(), httpRequest.Headers[ScriptConstants.HttpProxyCorrelationHeader]); + } + } +} \ No newline at end of file