Azure / azure-webjobs-sdk-extensions

Azure WebJobs SDK Extensions
MIT License
342 stars 206 forks source link

[3.0.0-beta8] HttpTrigger shows exceptions in output when returning HttpResponse #478

Open johlrich opened 6 years ago

johlrich commented 6 years ago

With the latest version I am seeing exceptions when returning an already written to HttpResponse from an HttpTrigger. It looks like the middleware is treating it as an ObjectResult and trying to interpret / serialize / write this object to the response.

Stack trace: ``` [8/30/2018 2:42:12 PM] Executing 'Ping-Http' (Reason='This function was programmatically called via the host APIs.', Id=f66ecd9b-c2f4-4142-8e5b-34075f365306) [8/30/2018 2:42:12 PM] Executed 'Ping-Http' (Succeeded, Id=f66ecd9b-c2f4-4142-8e5b-34075f365306) info: Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor[1] Executing ObjectResult, writing value of type 'Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse'. fail: Microsoft.AspNetCore.Server.Kestrel[13] Connection id "0HLGEGR2HT0K0", Request id "0HLGEGR2HT0K0:00000001": An unhandled exception was thrown by the application. System.InvalidOperationException: Headers are read-only, response has already started. at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException() at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value) at Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.set_ContentType(String value) at Microsoft.AspNetCore.Mvc.Formatters.OutputFormatter.WriteResponseHeaders(OutputFormatterWriteContext context) at Microsoft.AspNetCore.Mvc.Formatters.TextOutputFormatter.WriteAsync(OutputFormatterWriteContext context) at Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsync(ActionContext context, ObjectResult result) at Microsoft.AspNetCore.Mvc.ObjectResult.ExecuteResultAsync(ActionContext context) at Microsoft.Azure.WebJobs.Script.WebHost.Middleware.FunctionInvocationMiddleware.Invoke(HttpContext context) in C:\azure-webjobs-sdk-script\src\WebJobs.Script.WebHost\Middleware\FunctionInvocationMiddleware.cs:line 69 at Microsoft.Azure.WebJobs.Script.WebHost.Middleware.HomepageMiddleware.Invoke(HttpContext context) in C:\azure-webjobs-sdk-script\src\WebJobs.Script.WebHost\Middleware\HomepageMiddleware.cs:line 34 at Microsoft.AspNetCore.Buffering.ResponseBufferingMiddleware.Invoke(HttpContext httpContext) at Microsoft.Azure.WebJobs.Script.WebHost.Middleware.HttpExceptionMiddleware.Invoke(HttpContext context) in C:\azure-webjobs-sdk-script\src\WebJobs.Script.WebHost\Middleware\HttpExceptionMiddleware.cs:line 23 at Microsoft.Azure.WebJobs.Script.WebHost.Middleware.EnvironmentReadyCheckMiddleware.Invoke(HttpContext httpContext, IScriptWebHostEnvironment webHostEnvironment) in C:\azure-webjobs-sdk-script\src\WebJobs.Script.WebHost\Middleware\EnvironmentReadyCheckMiddleware.cs:line 29 at Microsoft.Azure.WebJobs.Script.WebHost.Middleware.AppServiceHeaderFixupMiddleware.Invoke(HttpContext httpContext) in C:\azure-webjobs-sdk-script\src\WebJobs.Script.WebHost\Middleware\AppServiceHeaderFixupMiddleware.cs:line 35 at Microsoft.Azure.WebJobs.Script.WebHost.Middleware.ScriptHostRequestServiceProviderMiddleware.Invoke(HttpContext httpContext, WebJobsScriptHostService manager) in C:\azure-webjobs-sdk-script\src\WebJobs.Script.WebHost\Middleware\ScriptHostRequestServiceProviderMiddleware.cs:line 36 at Microsoft.Azure.WebJobs.Script.WebHost.Middleware.HostAvailabilityCheckMiddleware.Invoke(HttpContext httpContext, IScriptHostManager scriptHostManager) in C:\azure-webjobs-sdk-script\src\WebJobs.Script.WebHost\Middleware\HostAvailabilityCheckMiddleware.cs:line 48 at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application) info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2] Request finished in 740.878ms 200 ```

Repro steps

Return a HttpResponse. I typically do this from Giraffe/Saturn, but was able to create minimal repro with just DefaultHttpResponse:

Repro module ``` module Http open Microsoft.AspNetCore.Http open Microsoft.Azure.WebJobs open Microsoft.Azure.WebJobs.Extensions.Http open Microsoft.AspNetCore.Http.Internal [] let ping ( [] req: HttpRequest ) = let resp = DefaultHttpResponse(req.HttpContext) let body = System.Text.Encoding.UTF8.GetBytes("pong") resp.Body.Write(body, 0, body.Length) resp ```

Expected behavior

HttpResponse is used

Actual behavior

HttpResponse interpreted as ObjectResult

Known workarounds

N/A

Related information

2.0.1-beta.36 of runtime 3.0.0-beta8 of nuget packages

fabiocav commented 6 years ago

This is consistent with the behavior you'd see in a standard ASP.NET Core application. The response would end up being treated as an ObjectResult and serialization would be attempted there. Is this a repro for something one of those frameworks are doing? Are they registering a different formatters/handling the response?

Not sure if you're just trying to repro framework behavior, I need to ask, any reason why you're not working with the context response?

johlrich commented 6 years ago

In V1 I often used the HttpRequestMessage -> HttpResponseMessage to do it and have been just doing HttpRequest -> HttpResponse in the V2 previews following that habit. The adapter being used to go from a Giraffe/Saturn handler -> azure function does result in a signature of HttpRequest -> Task< HttpResponse> to work and is where my actual usage ends up down this path.

Here's the snippet of the adapter that creates that fn: https://github.com/SaturnFramework/Saturn/blob/master/src/Saturn.AzureFunctions/AzureFunctions.fs#L59-L75

Oddly enough, I didn't have any issues with that approach until this latest release. I also noticed after posting this issue that the client was getting a 200 for both beta35/36 of v2 runtime, but only logged the exception messages above under the newest release for the same snippets. Could it be that this has always been an issue for me, but maybe wasn't being made visible before?

johlrich commented 6 years ago

@fabiocav after thinking about the reason I ran into this (trying to find a way to glue Giraffe -> Azure Functions) I wonder if it's better to instead ask: with all the new changes that just landed, is there a way to add additional services / middlewares to the IApplicationBuilder now?