ThreeMammals / Ocelot

.NET API Gateway
https://www.nuget.org/packages/Ocelot
MIT License
8.38k stars 1.64k forks source link

Why does Ocelot not support for SSE proto? #941

Closed dingfengwu closed 4 years ago

dingfengwu commented 5 years ago

Message from repo maintainer on November 30, 2023

Hello Devin Wu! My short answer: No Ocelot doesn't support SSE protocol.

Why did you close the issue on Jul 24, 2020? 🥲 Tired of notifications?...

trickreich commented 5 years ago

Maybe we could add here some more information: SSE (Server-Sent Events) isn't working for me

snb83 commented 5 years ago

I've faced with same problem. Our .NET Core backend sits behind API Gateway (Ocelot) and I've added SignalR hub recently. We only need one-way communication from backend to SPA (front-end app). Server-Sent Event (SSE) transport is one of three options SignalR provides and it looks like a perfect lightweight candiate. Client app works flawlesly when it is connected direcetly to the backend (which is IIS hosted), but when accessing via API Gateway the Ocelot stucks.

First request (the handshake) passes fine:

HTTP POST /myservice/hubs/notifications/negotiate HTTP/1.1

HTTP/1.1 200 OK
Content-Length: 125
Content-Type: application/json
Server: Kestrel
Date: Fri, 01 Nov 2019 12:49:48 GMT

{"connectionId":"IepBcjkYfdnXxg3Vn-z24Q","availableTransports":[{"transport":"ServerSentEvents","transferFormats":["Text"]}]}

However the second connection which should be a long-living text/event-stream connection fails after few seconds:

GET /myservice/hubs/notifications?id=IepBcjkYfdnXxg3Vn-z24Q HTTP/1.1

HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Length: 49
Content-Type: text/event-stream
Server: Kestrel
Date: Fri, 01 Nov 2019 12:50:03 GMT

:
data: {"error":"Handshake was canceled."}

The Ocelot outputs this into console log:

18:14:29:637 Debug: Ocelot.Requester.Middleware.HttpRequesterMiddleware requestId: 0HLQUQ3CANMQU:00000002, previousRequestId: no previous request id, message: setting http response message
18:14:29:642 Debug: Ocelot.Responder.Middleware.ResponderMiddleware requestId: 0HLQUQ3CANMQU:00000002, previousRequestId: no previous request id, message: no pipeline errors, setting and returning completed response
18:14:29:644 Debug: Ocelot.Errors.Middleware.ExceptionHandlerMiddleware requestId: 0HLQUQ3CANMQU:00000002, previousRequestId: no previous request id, message: ocelot pipeline finished

After short investigation it appears that pipeline was not designed to pass-though streamed responses:

HttpRequesterMiddleware simply reads the whole response:

var response = await _requester.GetResponse(context);
...
context.DownstreamResponse = new DownstreamResponse(response.Data);
await _next.Invoke(context);

The requested uses standard HttpClient to read the response.

However, I see that Ocelot has special (dedicated) pipeline to handle websocket connections:

            // If the request is for websockets upgrade we fork into a different pipeline
            builder.MapWhen(context => context.HttpContext.WebSockets.IsWebSocketRequest,
                app =>
                {
                    app.UseDownstreamRouteFinderMiddleware();
                    app.UseDownstreamRequestInitialiser();
                    app.UseLoadBalancingMiddleware();
                    app.UseDownstreamUrlCreatorMiddleware();
                    app.UseWebSocketsProxyMiddleware();
                });

The WebSocketsProxyMiddleware middleware contains an infinite loop that pumps the data in both directions:

var destinationUri = new Uri(serverEndpoint);
            await client.ConnectAsync(destinationUri, context.RequestAborted);
            using (var server = await context.WebSockets.AcceptWebSocketAsync(client.SubProtocol))
            {
                var bufferSize = DefaultWebSocketBufferSize;
                await Task.WhenAll(PumpWebSocket(client, server, bufferSize, context.RequestAborted), PumpWebSocket(server, client, bufferSize, context.RequestAborted));
            }

The HttpRequesterMiddleware does not make any attempts to pump streamable content (say if HTTP Content-Type is text/event-stream).

Can we please have either alternative pipeline for SSE or, may be, some adjustments in HttpRequesterMiddleware that will make pumping SSE traffic possible?

I would appreciate if @TomPallister comment on this :)

trickreich commented 4 years ago

@snb83 That means it's not working for you, right?

snb83 commented 4 years ago

@snb83 That means it's not working for you, right?

Yes, SSE transport does not work for me when notifications are routed via Ocelot API Gateway.

luheorga commented 4 years ago

Hi, ¿there are some update about that?

ganhj99 commented 2 years ago

Hi, any update on this?

coppercarpenter commented 1 year ago

Hello any update on this? I am also facing the same issue.

byjokese commented 11 months ago

Interested on looking how this goes too. 👀

krishnankuppaswamy commented 11 months ago

Watching this issue for the updates. I faced the same challenge today and had to spend several late night hours before concluding...

raman-m commented 11 months ago

@krishnankuppaswamy @byjokese @coppercarpenter @ganhj99 @luheorga @trickreich Hello guys!

@dingfengwu And, dear author,

Welcome to Ocelot to contribute! As a team, we are open to discuss, review PRs... If someone has an intention to contribute, then Welcome!

raman-m commented 11 months ago

@snb83 commented on Nov 1, 2019

Hi Sergei! Right, we have different pipelines for Http & Websoket traffic.

Can we please have either alternative pipeline for SSE or, may be, some adjustments in HttpRequesterMiddleware that will make pumping SSE traffic possible?

Yes, we can. But, will you contribute actively? Seems we have to design & develop 3rd pipeline for SSE protocol... I'm not sure we can reuse Http pipeline somehow... I can reopen this issue, if you're still with Ocelot... 😉

ggnaegi commented 9 months ago

keeping some notes for the feature: HttpContext.Features.Get<IHttpResponseBodyFeature>().DisableBuffering() https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.features.ihttpresponsebodyfeature?view=aspnetcore-8.0 @ggnaegi @raman-m

it's related to this discussion: https://github.com/ThreeMammals/Ocelot/discussions/1943

truongnguyengithub commented 3 months ago

add config in public void Configure(IApplicationBuilder app, IWebHostEnvironment env), not using middleware

app.Use(async (context, next) => {
    context.Request.EnableBuffering();
    await next();
});

Ocelot setting json:

{
  "DownstreamPathTemplate": "/notice/{eveything}",
  "DownstreamScheme": "https",
  "DownstreamHostAndPorts": [
    {
      "Host": "localhost",
      "Port": 9000
    }
  ],
  "UpstreamPathTemplate": "/notice/{eveything}",
  "UpstreamHttpMethod": [ "POST", "PUT", "GET", "DELETE", "PATCH" ],
  "DangerousAcceptAnyServerCertificateValidator": true
},
Screenshot 2024-07-21 103343
raman-m commented 3 months ago

Hello @truongnguyengithub! What were you trying to communicate or demonstrate? Were you suggesting that the Server-Side Events protocol functions effectively with context.Request.EnableBuffering() without the need for .DisableBuffering()?

I would mention that in certain applications and user environments, both methods might be viable, although we do not officially support the SSE protocol. There have been no professional pull requests for an Ocelot Core update. However, we acknowledge the widespread popularity of SSE, and as a team, we are prepared to give priority to the development of this protocol to finally enable it in routing schemes.

truongnguyengithub commented 3 months ago

Hi @raman-m

I am looking to solve the SSE issue when using Ocelot for my project. It would be great if Ocelot Core was updated with this protocol. I hope to have a release soon.

raman-m commented 3 months ago

@truongnguyengithub, are you prepared for a professional contribution? If so, I will reopen the issue once the PR is opened.