Skip to content

Apply Net::HTTP cutoff to internal retries - #20

Merged
justinhoward merged 1 commit into
masterfrom
tighten-net-http-timeout-on-retry
May 12, 2026
Merged

justinhoward merged 1 commit into
masterfrom
tighten-net-http-timeout-on-retry

Conversation

@justinhoward

Copy link
Copy Markdown
Owner

Net::HTTP#transport_request silently retries idempotent requests (GET, HEAD, PUT, DELETE, OPTIONS, TRACE) up to max_retries (default 1) on transient errors like Net::ReadTimeout, OpenSSL::SSL::SSLError, and Errno::ECONNRESET. The retry path closes the socket and goes through re-apply: the retry inherited the read_timeout set on the original attempt. A deadline of N seconds silently became 2N seconds, with no log line or callback to indicate the retry had happened.

Factor the timeout-clamping and checkpoint into a private helper and also call it from begin_transport, which runs once per HTTP attempt including retries. On a retry after the cutoff is exhausted, the checkpoint raises CutoffExceededError; Net::HTTP catches it as a Timeout::Error and re-raises once max_retries is hit. With the default max_retries of 1, the second attempt short-circuits cleanly.

As a side effect, the keep-alive connection-reuse path now also applies the cutoff. Previously a request reusing a pooled socket skipped the start patch entirely, since #start is only called when opening a new connection.

Net::HTTP#transport_request silently retries idempotent requests (GET,
HEAD, PUT, DELETE, OPTIONS, TRACE) up to max_retries (default 1) on
transient errors like Net::ReadTimeout, OpenSSL::SSL::SSLError, and
Errno::ECONNRESET. The retry path closes the socket and goes through
re-apply: the retry inherited the read_timeout set on the original
attempt. A deadline of N seconds silently became 2N seconds, with no
log line or callback to indicate the retry had happened.

Factor the timeout-clamping and checkpoint into a private helper and
also call it from begin_transport, which runs once per HTTP attempt
including retries. On a retry after the cutoff is exhausted, the
checkpoint raises CutoffExceededError; Net::HTTP catches it as a
Timeout::Error and re-raises once max_retries is hit. With the default
max_retries of 1, the second attempt short-circuits cleanly.

As a side effect, the keep-alive connection-reuse path now also applies
the cutoff. Previously a request reusing a pooled socket skipped the
start patch entirely, since #start is only called when opening a new
connection.
@justinhoward
justinhoward force-pushed the tighten-net-http-timeout-on-retry branch from f90e5ae to b671cf8 Compare May 12, 2026 22:32
@justinhoward
justinhoward merged commit 45d68fd into master May 12, 2026
11 checks passed
@justinhoward
justinhoward deleted the tighten-net-http-timeout-on-retry branch May 12, 2026 22:35
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.

1 participant