Fred Hors
Fred Hors

Reputation: 4156

Golang server default timeout with (long polling) server-sent event calls. The call is closed, how to maintain it open?

I'm using this amazing SSE Server in Golang (https://github.com/r3labs/sse) on Heroku.

There is a timeout limit there: https://devcenter.heroku.com/articles/request-timeout#long-polling-and-streaming-responses:

If you’re sending a streaming response, such as with server-sent events, you’ll need to detect when the client has hung up, and make sure your app server closes the connection promptly. If the server keeps the connection open for 55 seconds without sending any data, you’ll see a request timeout.

I know in the WebSocket world there is the concept of Keep-Alive ping.

I'm trying with this code:

go func() {
  for range time.Tick(time.Second * 1) {
    fmt.Println("a second")
    sseServer.Publish("test", &sse.Event{Data: []byte("ping")})
  }
}()

using a simple server like this:

httpServer := &http.Server{
  //...
  ReadTimeout:  "10s",
  WriteTimeout: "10s",
}

but it doesn't work. The call is closed after 10 seconds (and 10 pings).

I think it will fail on Heroku too.

Where am I wrong?

Can we change the timeout for these SSE calls only?

Is there another way?

UPDATE

I don't need accurate detection of disconnected client, I don't care at all. I'm using it to refresh a dashboard when something happens on server.

I prefer not to use WebSocket for something so easy.

I think I have not explained myself well, because I would like to use ping not so much for detecting disconnected clients but because I would like the connection not to be interrupted (as is happening on Heroku).

THE SITUATION RIGHT NOW

LOCALLY

If I remove the ReadTimeout field totally on Windows but also on Docker linux container the connection does not stop and everything works fine.

ON HEROKU

Since on Heroku the connection drops every 55 seconds for that timeout I told you in the first post I tried that loop with that very simple code and it works: the SSE calls are not closed anymore!

THE REMAINING ISSUE

  1. How to have in any case a default ReadTimeout for all other calls (not SSE); I think it's best practice to set a default ReadTimeout.

How to do?

Upvotes: 0

Views: 1969

Answers (1)

fatal_error
fatal_error

Reputation: 5973

You do not need the ReadTimeout; the server will never read anything from the client after the initial EventSource/Server Sent Events (SSE) connection.

Thus, it is not a best practice to set a default read timeout with an SSE connection, because that read timeout will always get hit. You can't ever send more data back up through the initial SSE GET request.

You should think about SSE as basically a GET request that simply never closes, because that's almost literally what it is. That means that it works great through most proxy servers, and where it doesn't (where the proxy server applies its own timeouts), the client side will automatically reconnect, which is actually a very nice feature that is not found in websockets (although most websocket client libraries do implement it).

You might want to read through this article to learn some more of the great (and not-so-great) things about SSE: https://www.smashingmagazine.com/2018/02/sse-websockets-data-flow-http2/

With regards to your other question, you're probably looking for your HTTP routing library that will allow you to apply timeouts to some GET requests, and not others, but the question is why; if you are trying to protect from a resource drain, you should apply that protection evenly across all endpoints.

Upvotes: 1

Related Questions