diff --git a/build/common.props b/build/common.props index 233a45991c..b8dc66faef 100644 --- a/build/common.props +++ b/build/common.props @@ -5,7 +5,7 @@ latest 4 $(MinorVersionPrefix)34 - 0 + 1 0 diff --git a/release_notes.md b/release_notes.md index 38f9257fa5..4766b0c761 100644 --- a/release_notes.md +++ b/release_notes.md @@ -3,9 +3,4 @@ -- Update Python Worker Version to [4.28.1](https://github.com/Azure/azure-functions-python-worker/releases/tag/4.28.1) -- Fixed an issue causing sporadic HTTP request failures when worker listeners were not fully initialized on first request #9954 -- Update Node.js Worker Version to [3.10.0](https://github.com/Azure/azure-functions-nodejs-worker/releases/tag/v3.10.0) (#9999) -- Update PowerShell worker 7.2 to [4.0.3220](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.3220) -- Update PowerShell worker 7.4 to [4.0.3219](https://github.com/Azure/azure-functions-powershell-worker/releases/tag/v4.0.3219) -- Update Azure.Identity to 1.11.0 (#10002) +- Ensuring non http invocation responses are not processed by IHttpProxyService (#10085) diff --git a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs index 00f94cbf97..1778269eda 100644 --- a/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs +++ b/src/WebJobs.Script.Grpc/Channel/GrpcWorkerChannel.cs @@ -1094,7 +1094,7 @@ internal async Task InvokeResponse(InvocationResponse invokeResponse) try { - if (IsHttpProxyingWorker) + if (IsHttpProxyingWorker && context.FunctionMetadata.IsHttpTriggerFunction()) { await _httpProxyService.EnsureSuccessfulForwardingAsync(context); } diff --git a/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs b/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs index ec7ed11127..fa3d0760c9 100644 --- a/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs +++ b/test/WebJobs.Script.Tests/Workers/Rpc/GrpcWorkerChannelTests.cs @@ -1396,6 +1396,35 @@ public async Task GetFunctionMetadata_MultipleCalls_ReturnSameTask() Assert.Same(functionsTask1, functionsTask2); } + [Fact] + public async Task Ensure_SuccessfulForwardingAsync_Is_Invoked_OnlyFor_HttpInvocationResponses() + { + await CreateDefaultWorkerChannel(capabilities: new Dictionary() { { RpcWorkerConstants.HttpUri, "http://localhost:1234" } }); + + var httpInvocationId = Guid.NewGuid(); + ScriptInvocationContext httpInvocationContext = GetTestScriptInvocationContext(httpInvocationId, new TaskCompletionSource(), logger: _logger); + httpInvocationContext.FunctionMetadata = BuildFunctionMetadataForHttpTrigger("httpTrigger"); + + var timerInvocationId = Guid.NewGuid(); + ScriptInvocationContext timerInvocationContext = GetTestScriptInvocationContext(timerInvocationId, new TaskCompletionSource(), logger: _logger); + timerInvocationContext.FunctionMetadata = BuildFunctionMetadataForTimerTrigger("timerTrigger"); + + // Send http trigger and timer trigger invocation invocation requests. + await _workerChannel.SendInvocationRequest(httpInvocationContext); + await _workerChannel.SendInvocationRequest(timerInvocationContext); + + // Send http trigger and timer trigger invocation responses. + await _workerChannel.InvokeResponse(BuildSuccessfulInvocationResponse(timerInvocationId.ToString())); + await _workerChannel.InvokeResponse(BuildSuccessfulInvocationResponse(httpInvocationId.ToString())); + + var logs = _logger.GetLogMessages().ToArray(); + Assert.Single(logs.Where(m => m.FormattedMessage.Contains($"InvocationResponse received for invocation: '{timerInvocationId}'"))); + Assert.Single(logs.Where(m => m.FormattedMessage.Contains($"InvocationResponse received for invocation: '{httpInvocationId}'"))); + + // IHttpProxyService.EnsureSuccessfulForwardingAsync method should be invoked only for http invocation response. + _mockHttpProxyService.Verify(m => m.EnsureSuccessfulForwardingAsync(It.IsAny()), Times.Once); + } + [Fact] public async Task Log_And_InvocationResult_OrderedCorrectly() { @@ -1574,5 +1603,49 @@ private Task CreateSharedMemoryEnabledWorkerChannel(bool setEnvironmentVariable // Send worker init request and enable the capabilities return CreateDefaultWorkerChannel(capabilities: capabilities); } + + private static InvocationResponse BuildSuccessfulInvocationResponse(string invocationId) + { + return new InvocationResponse + { + InvocationId = invocationId, + Result = new StatusResult + { + Status = StatusResult.Types.Status.Success + }, + }; + } + + private static FunctionMetadata BuildFunctionMetadataForHttpTrigger(string name, string language = null) + { + var functionMetadata = new FunctionMetadata() { Name = name, Language = language }; + functionMetadata.Bindings.Add(new BindingMetadata() + { + Type = "httpTrigger", + Direction = BindingDirection.In, + Name = "req" + }); + functionMetadata.Bindings.Add(new BindingMetadata() + { + Type = "http", + Direction = BindingDirection.Out, + Name = "$return" + }); + + return functionMetadata; + } + + private static FunctionMetadata BuildFunctionMetadataForTimerTrigger(string name, string language = null) + { + var functionMetadata = new FunctionMetadata() { Name = name, Language = language }; + functionMetadata.Bindings.Add(new BindingMetadata() + { + Type = "timerTrigger", + Direction = BindingDirection.In, + Name = "myTimer" + }); + + return functionMetadata; + } } }