Harden fetch: client timeout, retry with backoff, url.JoinPath #63
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Three related defects in
internal/cli/fetch.go, all on the same code path:httpGet()(internal/cli/fetch.go:73-83) buildsthe request with
http.NewRequestWithContextbut then issues it withhttp.DefaultClient.Do(req).http.DefaultClienthas noTimeout, so aserver that accepts the connection and then stalls hangs the fetch
forever. Note
internal/cli/manifest_loader.go:31already does thiscorrectly with
&http.Client{Timeout: manifestFetchTimeout}—fetchisthe outlier.
fetch.go. Asingle transient 5xx or dropped connection aborts a whole multi-file
download that may already be most of the way done. For the stated use
case — mirroring a large tree over flaky HTTP — this is the difference
between the tool being usable and not.
internal/cli/fetch.go:143:fileURL := baseURL.String() + encodeFilePath(f.GetPath())and
resolveManifestURL(internal/cli/fetch.go:278-288):parsed.Path += "index.mf". Both are wrong for base URLs with or withouta trailing slash, with query strings, or with escaped characters.
Definition of done
fetchgo through anhttp.Clientwith an explicittimeout. The timeout is a named constant and is overridable by the user
via a CLI flag.
attempt count. "Transient" is defined explicitly and narrowly: connection
errors, timeouts, and 5xx / 429 responses. 4xx other than 429 must not
be retried.
Retry-Afterwhen the server sendsit.
ctxcancellation during a backoff sleep returnspromptly rather than sleeping out the interval.
url.JoinPath(orURL.JoinPath) throughout. Nostring concatenation onto a URL or a
URL.Pathanywhere in the file.httptest.Server: a stalled server hits the timeout anderrors rather than hanging; a server that fails twice then succeeds is
retried to success; a 404 is not retried; a base URL with and without a
trailing slash both resolve to the same file URLs; a path needing escaping
round-trips correctly. Retry tests must use short, injected intervals so
the suite stays fast.
make checkpasses.TODO.mdupdated in the same commit.Implementation requirements
for real. A test suite that takes seconds to prove backoff works is a
failed implementation of this issue.
response body without resetting state — check what
downloadFilehasalready written before re-attempting, and make sure a retry cannot produce
a truncated or double-written file.
retried download must still be verified.
(closes #63).