Skip to content

Conversation

@robmueller
Copy link

There's a well known race condition with http keep alive connections. If the server closes the connection after the client has checked the socket is still connected, but before it has prepared the request to send it, then the client will receive an EPIPE error and fail the entire request when it tries to write the request to the socket.

This means clients always have to check for this case and decide what to do, which could be resend the request depending on when exactly the EPIPE occured and whether the request was idempotent or not.

Another work around for this is to make sure the client has a keepalive reuse timeout that's less than the server keepalive time. So for instance if the client has a 5 second timeout and the server has a 10 second timeout, then if it's been > 5 seconds since the connection was last re-used, the client will close it and open a new one, avoiding the case where the server would close the connection on it.

This setup can be very useful when using HTTP::Tiny to send requests to internal service endpoints and wanting as much as possible to avoid this edge case failure more.

Copy link
Member

@haarg haarg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This overall seems good to me.

One concern I have is that it only supports whole seconds. The existing timeout method will under the hood be implemented using select, so it will support finer granularity. Possibly this should be using Time::HiRes::time.

There's a well known race condition with http keep alive connections.
If the server closes the connection after the client has checked
the socket is still connected, but before it has prepared the request
to send it, then the client will receive an EPIPE error and fail
the entire request when it tries to write the request to the socket.

This means clients always have to check for this case and decide
what to do, which could be resend the request depending on when
exactly the EPIPE occured and whether the request was idempotent
or not.

Another work around for this is to make sure the client has a keepalive
reuse timeout that's less than the server keepalive time. So for instance
if the client has a 5 second timeout and the server has a 10 second
timeout, then if it's been > 5 seconds since the connection was last
re-used, the client will close it and open a new one, avoiding the case
where the server would close the connection on it.

This setup can be very useful when using HTTP::Tiny to send requests
to internal service endpoints and wanting as much as possible to
avoid this edge case failure more.
@robmueller robmueller force-pushed the implement-keep-alive-timeout branch from 6f1fa86 to 985adf8 Compare December 12, 2025 04:07
@robmueller
Copy link
Author

I've updated the PR to use Time::HiRes iff keep_alive_timeout > 0, minimise classes loaded unless needed.

@haarg haarg merged commit 1e3f796 into Perl-Toolchain-Gang:master Dec 13, 2025
18 of 19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants