elixir-mint / mint

Functional HTTP client for Elixir with support for HTTP/1 and HTTP/2 🌱
Apache License 2.0
1.36k stars 112 forks source link

Strategy for keeping connection open for as long as possible #298

Closed sashaafm closed 3 years ago

sashaafm commented 3 years ago

Hello again,

I'm using Mint to connect to a third-party Web server (via HTTP2). From what I understand, HTTP2 keeps connections until either the client or server trigger their idle timeout. Also, from Mint's settings, it looks like the server_settings do not show what the server idle timeout is (maybe this isn't part of the negotiated settings between client and server?).

Therefore, as to keep a connection open as long as possible my top of the head strategy is to use Mint.HTTP2.ping/2 to ping the server at a given interval which I know should be lower than the idle timeout (e.g., 5 seconds). Is this a valid strategy for keeping a long-lived connection? I've been testing and it looks like I'm able to keep it open indefinitely using this strategy. However, it's hard to be sure since I have no control over the server to check what is truly happening.

If this is bad behaviour or not possible to force a connection to be kept open (of course the server can always close the connection at will) then is it possible to receive a "connection closed" message to then connect again?

Thank you for your time

ericmj commented 3 years ago

You may be able to keep the connection open longer by using Mint.HTTP2.ping/2 or other actions that do activity on the connection, but ultimately the server can close the connection at any time for any reason so you need to be able to handle that. You may also use the :keepalive transport option, but it depends on the server's implementation if they will still idle timeout, some servers may even ignore PINGs when checking if the connection is idle.

I am not sure if it's bad behavior to do this, if you control the server then I guess it's fine, if you do not then I would suggest talking to the operators of the server. It's a question of weighing the resource usage of keeping a connection open against the additional latency of reconnecting so there is no answer that works for everyone.

Mint will return {:error, :closed} when the connection has been closed.

sashaafm commented 3 years ago

@ericmj that was a thorough explanation! I experimented with the ping strategy and for this particular server I'm working against it's enough to keep the connection alive "forever".

However, I want to make my connection resilient to general disconnects (either from the server or unreliable network). I'm wondering if there's a particular message that my GenServer can receive if the connection is closed from the other party or due to network issues? From my experiments and looking at the source code these messages should be {:tcp_closed, <port>} for HTTP and {:ssl_closed, {:sslsocket, {:gen_tcp, <port>, :tls_connection, :undefined}, [<pid1>, <pid2>]}} for HTTPS. Is this correct?

The goal would be to react to that message and connect again to the same host.

Thank you for your work and time

ericmj commented 3 years ago

Mint will return {:error, :closed} when it receives the :tcp_closed or :ssl_closed messages, you should not try to intercept them yourself.

sashaafm commented 3 years ago

Thank you for your help and clearing up this issue 🙏