elsa-workflows / elsa-core

A .NET workflows library
https://v3.elsaworkflows.io/
MIT License
6.27k stars 1.15k forks source link

[BUG] Elsa.HttpEndpoint: System.InvalidOperationException: Synchronous operations are disallowed. #5867

Open volker-knoess opened 1 month ago

volker-knoess commented 1 month ago

Description

The HTTP Endpoint run into an error when an XML is send with the Content-Type application/xml

Steps to Reproduce

Build a simple workflow with Elsa Studio with HTTP Endpoint and HTTP Response. Set the Parsed Content to variable type object or string (storage workflow or memory should not matter). Use Postman or something similar to send an XML (e.g. with the Content-Type application/xml.

image

  1. Reproduction Rate: every time

  2. Video/Screenshots: Error in Elsa Studio image

  3. Additional Configuration: Nothing specific, Elsa v3.1.3 from Nuget

Expected Behavior

No System.InvalidOperationException, XML content should be in the variable as string or a better in a more specific object (e.g. XDocument)

Actual Behavior

See under 5.

Screenshots

The problem seems for me quite easy to reproduce but let me know if further information is needed.

Environment

Log Output

Copied from the console.

warn: Elsa.Workflows.Middleware.Activities.ExceptionHandlingMiddleware[0]
      An exception was caught from a downstream middleware component
      System.InvalidOperationException: Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead.
         at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpRequestStream.Read(Byte[] buffer, Int32 offset, Int32 count)
         at System.IO.StreamReader.ReadBuffer(Span`1 userBuffer, Boolean& readToUserBuffer)
         at System.IO.StreamReader.ReadSpan(Span`1 buffer)
         at System.IO.StreamReader.Read(Char[] buffer, Int32 index, Int32 count)
         at System.Xml.XmlTextReaderImpl.ReadData()
         at System.Xml.XmlTextReaderImpl.InitTextReaderInput(String baseUriStr, Uri baseUri, TextReader input)
         at System.Xml.XmlTextReader..ctor(TextReader input)
         at System.Xml.Serialization.XmlSerializer.Deserialize(TextReader textReader)
         at Elsa.Http.Parsers.XmlHttpContentParser.ReadAsync(Stream content, Type returnType, CancellationToken cancellationToken)
         at Elsa.Extensions.HttpActivityExecutionContextExtensions.ParseContentAsync(ActivityExecutionContext context, Stream content, String contentType, Type returnType, CancellationToken cancellationToken)
         at Elsa.Http.HttpEndpoint.ParseContentAsync(ActivityExecutionContext context, HttpRequest httpRequest)
         at Elsa.Http.HttpEndpoint.HandleRequestAsync(ActivityExecutionContext context, HttpContext httpContext)
         at Elsa.Http.HttpEndpoint.ExecuteAsync(ActivityExecutionContext context)
         at Elsa.Workflows.Activity.Elsa.Workflows.Contracts.IActivity.ExecuteAsync(ActivityExecutionContext context)
         at Elsa.Workflows.Middleware.Activities.DefaultActivityInvokerMiddleware.ExecuteActivityAsync(ActivityExecutionContext context)
         at Elsa.Workflows.Runtime.Middleware.Activities.BackgroundActivityInvokerMiddleware.ExecuteActivityAsync(ActivityExecutionContext context)
         at Elsa.Workflows.Middleware.Activities.DefaultActivityInvokerMiddleware.InvokeAsync(ActivityExecutionContext context)
         at Elsa.Workflows.Middleware.Activities.NotificationPublishingMiddleware.InvokeAsync(ActivityExecutionContext context)
         at Elsa.Workflows.Middleware.Activities.ExecutionLogMiddleware.InvokeAsync(ActivityExecutionContext context)
         at Elsa.Workflows.Middleware.Activities.ExceptionHandlingMiddleware.InvokeAsync(ActivityExecutionContext context)

Troubleshooting Attempts

Tried to copy the HttpEndpoint over to my code, it's compiling and the activity is coming in Elsa studio but in this case the activity is not activated and a 404 is coming. It seems it takes only the Elsa HttpEndpoint and not my copy. Is it possible to create also your own HttpEnpoints?

I tried also to read the body with a custom Activity, but also without success, it results in an empty string.

public class DataReader : CodeActivity<object>
{
    //public Input<string> ParsedContent { get; set; } = default!;
    protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
    {
        var last = context.GetLastResult();
        Console.WriteLine($"######## GetLastResult:\n{last}\n########\n\n");
        if (last is HttpRequest http)
        {
            http.EnableBuffering();
            var stream = http.Body;
            stream.Position = 0;
            var bodyStream = new StreamReader(stream);
            var bodyText = await bodyStream.ReadToEndAsync();
            Console.WriteLine($"######## Data:\n{bodyText}\n########\n\n");
        }
    }
}

Additional Context

The error comes only with Content-Type application/xml other types seems to work same content with text/plain works fine.

Related Issues

I don't thinks so the other seems to address different things.

V0idSt4r commented 6 days ago

I encountered the same problem. You can read more about it here https://github.com/dotnet/runtime/issues/12711

You need to allow AllowSynchronousIO as a workaround:

builder.Services.Configure<KestrelServerOptions>(options => { options.AllowSynchronousIO = true; });