I come with a confusing issue when using SignalR SSE ( ServerSentEvents ) : the HttpContext in SignalR Hub is always the same instance, regardless the Get Request to send data to Client, or the Post Request to receive data from Client, which leads to issue when retrieving from Context.
Down the rabbit hole
To understand what happened, let’s take a look at the source code.
First let’s look at ServerSentEventsServerTransport, found HttpContext is passed as parameter,
Then we need to know where the Transport is called, found the only reference in HttpConnectionDispatcher,
private async Task ExecuteAsync(HttpContext context, ConnectionDelegate connectionDelegate, HttpConnectionDispatcherOptions options, ConnectionLogScope logScope)
It would check if Content-Type is
text/event-stream, which correspond with what we know about SSE, indicating this is where Get long-connection is handled.
The sse object is passed to DoPersistentConnection, let’s continue,
private async Task DoPersistentConnection(ConnectionDelegate connectionDelegate,
The second parameter is passed to method of connection, then
internal bool TryActivatePersistentConnection(
Got it. The HttpContext is passed from ExecuteAsync to ServerSentEventsServerTransport, so it’d always be the Context from Get Request…
Is it true?
Nope, we only prove the HttpContext from GetRequest would be consumed by Transport, but not say the HttpContext from Post Request would not. Let’s go back to HttpConnectionDispatcher to take a look at ExecuteAsync again,
public async Task ExecuteAsync(HttpContext context, HttpConnectionDispatcherOptions options, ConnectionDelegate connectionDelegate)
Hmm, it’s confusing to use overload somethings: the ExecuteAsync metioned above accepts 4 parameters, which is called in the ExecuteAsync accepting 3 parameters, when the Request is Get or Connect, which matches the theory.
And for Post Request, it’s handled in ProcessSend of Dispatch,
private async Task ProcessSend(HttpContext context)
Here is a simplified version, the HttpContext is used here only, except sending error code when something goes wrong, to copy the data fro Post Body.
The other parts acts with the SignalR long connection, so the HttpContext must be the one from Get Request.
So, why not Websockets? Because it’s not supported by the Reserved Proxy. It would be better if we adopt WS, right? In Websockets, for a SignalR Connection, there is only one HttpContext, which would not be confusing like this.
How about Long Polling? Same to SSE, the Contexts of Post Request are dropped, and we should get difference HttpContext from Get Requests, which is even more terrible.
Post link: https://blog.fallenwood.net/en/2022/09/02/signalr_httpcontext/