Skip to content

Commit

Permalink
[Bug Fix] ASP.NET pipeline hangs when function execution throws an un…
Browse files Browse the repository at this point in the history
…handled exception (#2527)
  • Loading branch information
satvu committed Jun 12, 2024
1 parent ab70ee0 commit efd0aef
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 13 deletions.
4 changes: 2 additions & 2 deletions extensions/Worker.Extensions.Http.AspNetCore/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
- My change description (#PR/#issue)
-->

### Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore <version>
### Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore 1.3.2

- <entry>
- Fixes a bug where the invocation hangs when the function has an unhandled exception (#2527).
Original file line number Diff line number Diff line change
Expand Up @@ -49,19 +49,24 @@ public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next
// Register additional context features
context.Features.Set<IFromBodyConversionFeature>(FromBodyConversionFeature.Instance);

await next(context);
try
{
await next(context);

var responseHandled = await TryHandleHttpResult(context.GetInvocationResult().Value, context, httpContext, true)
|| await TryHandleOutputBindingsHttpResult(context, httpContext);

var responseHandled = await TryHandleHttpResult(context.GetInvocationResult().Value, context, httpContext, true)
|| await TryHandleOutputBindingsHttpResult(context, httpContext);

if (!responseHandled)
if (!responseHandled)
{
var logger = context.InstanceServices.GetRequiredService<ExtensionTrace>();
logger.NoHttpResponseReturned(context.FunctionDefinition.Name, context.InvocationId);
}
}
finally
{
var logger = context.InstanceServices.GetRequiredService<ExtensionTrace>();
logger.NoHttpResponseReturned(context.FunctionDefinition.Name, context.InvocationId);
// Allow ASP.NET Core middleware to continue
_coordinator.CompleteFunctionInvocation(invocationId);
}

// Allow ASP.NET Core middleware to continue
_coordinator.CompleteFunctionInvocation(invocationId);
}

private static async Task<bool> TryHandleHttpResult(object? result, FunctionContext context, HttpContext httpContext, bool isInvocationResult = false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Description>ASP.NET Core extensions for .NET isolated functions</Description>

<!--Version information-->
<VersionPrefix>1.3.1</VersionPrefix>
<VersionPrefix>1.3.2</VersionPrefix>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -259,5 +260,21 @@ private Mock<IResult> GetMockIResult()

return mockResult;
}

[Fact]
public async Task CompleteFunctionInvocation_RunsWhen_FunctionThrowsException()
{
var test = SetupTest("httpTrigger");
var mockDelegate = new Mock<FunctionExecutionDelegate>();
mockDelegate.Setup(d => d.Invoke(It.IsAny<FunctionContext>()))
.Throws(new Exception("Custom exception message"));

var funcMiddleware = new FunctionsHttpProxyingMiddleware(test.MockCoordinator.Object);

await Assert.ThrowsAsync<Exception>(async () => await funcMiddleware.Invoke(test.FunctionContext, mockDelegate.Object));

mockDelegate.Verify(p => p.Invoke(test.FunctionContext), Times.Once());
test.MockCoordinator.Verify(p => p.CompleteFunctionInvocation(It.IsAny<string>()), Times.Once());
}
}
}

0 comments on commit efd0aef

Please sign in to comment.