Search code examples
asp.net-coreiis-8asp.net-core-signalr

WebSockets don't work with SignalR Core when deployed to Prod


ASP.NET Core 2.1 running behind IIS 8.5

SignalR doesn't seem to negotiate a Sub-Protocol when connecting and fails to complete the connection. Messages sent from the client to the server are never received.

I can't configure the site to serve from Kestrel directly as this is a shared server. Changing the transport to long-polling works. I have tried disabling HTTPS/HSTS redirection.

Attached are the logs from the client and the server:

Client logs:

Information: Normalizing 'hubs/screen' to 'https://<url>/hubs/screen'.
Utils.ts:173 Information: WebSocket connected to ws://<url>/hubs/screen?id=
...
Error: Connection disconnected with error 'Error: Server timeout elapsed without receiving a message from the server.'.<id>

Server logs:

dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionManager[1]
      New connection <id> created.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionDispatcher[10]
      Sending negotiation response.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.HttpConnectionDispatcher[4]
      Establishing new connection.
dbug: Microsoft.AspNetCore.SignalR.HubConnectionHandler[5]
      OnConnectedAsync started.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[1]
      Socket opened using Sub-Protocol: '(null)'.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[14]
      Socket connection closed prematurely.
System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake.
   at System.Net.WebSockets.ManagedWebSocket.ThrowIfEOFUnexpected(Boolean throwOnPrematureClosure)
   at System.Net.WebSockets.ManagedWebSocket.EnsureBufferContainsAsync(Int32 minimumRequiredBytes, Boolean throwOnPrematureClosure)
   at System.Net.WebSockets.ManagedWebSocket.ReceiveAsyncPrivate[TWebSocketReceiveResultGetter,TWebSocketReceiveResult](Memory`1 payloadBuffer, CancellationToken cancellationToken, TWebSocketReceiveResultGetter resultGetter)
   at System.Net.WebSockets.ManagedWebSocket.ReceiveAsyncPrivate[TWebSocketReceiveResultGetter,TWebSocketReceiveResult](Memory`1 payloadBuffer, CancellationToken cancellationToken, TWebSocketReceiveResultGetter resultGetter)
   at System.Net.WebSockets.WebSocket.ReceiveAsync(Memory`1 buffer, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport.StartReceiving(WebSocket socket)
fail: Microsoft.AspNetCore.SignalR.HubConnectionContext[5]
      Failed connection handshake.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[4]
      Waiting for the application to finish sending data.
dbug: Microsoft.AspNetCore.Http.Connections.Internal.Transports.WebSocketsTransport[2]
      Socket closed.

Solution

  • I found the problem!

    We had a custom IIS module that injected server name + request timing as an HTTP header and this was causing SignalR to fail. (The module was probably just blindly injecting its header, without respect to Websockets)