Skip to content

[ASP.NET Core] System.InvalidOperationException on incoming web socket requests. #342

Closed
@armingerten

Description

@armingerten

When using ASP.NET API versioning with web sockets, a System.InvalidOperationException is thrown when trying to add API version response headers.

Steps to reproduce:

  1. Configure API versionen
public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services..AddVersionedApiExplorer();
    services.AddApiVersioning(o =>
    {
        o.AssumeDefaultVersionWhenUnspecified = true;
        o.DefaultApiVersion = new ApiVersion(1, 0);
        o.ReportApiVersions = true;
    });
    return services;
}
  1. Start application locally using the following controller / action:
[ApiVersion("1.0")]
public class  MyController : Controller
{
    [HttpGet]
    public async Task OpenWebSocketConnectionAsync()
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            while (webSocket.IsOpen())
            {
                WebSocketReceiveResult result = await webSocket.ReceiveAsync(segment, CancellationToken.None);
                // Some business logic ...
            }
        }
    }
}
  1. Try establishing a websocket request (and close it prematurely).
curl --include --header "Connection:Upgrade" --header "Upgrade: websocket" http://localhost:8080

=> This will cause the following exception:

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.System.Collections.Generic.IDictionary<System.String,Microsoft.Extensions.Primitives.StringValues>.Add(String key, StringValues value)
at Microsoft.AspNetCore.Mvc.Versioning.DefaultApiVersionReporter.Report(IHeaderDictionary headers, ApiVersionModel apiVersionModel)
at Microsoft.AspNetCore.Mvc.Filters.ActionFilterAttribute.OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ExceptionContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)

One possible solution would be to skip the reporting when dealing with HttpContext.WebSockets.IsWebSocketRequest or HttpContext.Response.HasStarted ?

I am using the following software versions.

  • .NET core version: 2.1
  • Microsoft.AspNetCore.Mvc.Versioning: 2.1

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions