Skip to content

Commit 3b654b2

Browse files
http: add support for HTTP 429 rate limit retries
Add retry logic for HTTP 429 (Too Many Requests) responses to handle server-side rate limiting gracefully. When Git's HTTP client receives a 429 response, it can now automatically retry the request after an appropriate delay, respecting the server's rate limits. The implementation supports the RFC-compliant Retry-After header in both delay-seconds (integer) and HTTP-date (RFC 2822) formats. If a past date is provided, Git retries immediately without waiting. Retry behavior is controlled by three new configuration options (http.maxRetries, http.retryAfter, and http.maxRetryTime) which are documented in git-config(1). The retry logic implements a fail-fast approach: if any delay (whether from server header or configuration) exceeds maxRetryTime, Git fails immediately with a clear error message rather than capping the delay. This provides better visibility into rate limiting issues. The implementation includes extensive test coverage for basic retry behavior, Retry-After header formats (integer and HTTP-date), configuration combinations, maxRetryTime limits, invalid header handling, environment variable overrides, and edge cases. Signed-off-by: Vaidas Pilkauskas <vaidas.pilkauskas@shopify.com>
1 parent c4a0c88 commit 3b654b2

File tree

12 files changed

+729
-56
lines changed

12 files changed

+729
-56
lines changed

Documentation/config/http.adoc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,30 @@ http.keepAliveCount::
315315
unset, curl's default value is used. Can be overridden by the
316316
`GIT_HTTP_KEEPALIVE_COUNT` environment variable.
317317

318+
http.retryAfter::
319+
Default wait time in seconds before retrying when a server returns
320+
HTTP 429 (Too Many Requests) without a Retry-After header. If set
321+
to -1 (the default), Git will fail immediately when encountering
322+
a 429 response without a Retry-After header. When a Retry-After
323+
header is present, its value takes precedence over this setting.
324+
Can be overridden by the `GIT_HTTP_RETRY_AFTER` environment variable.
325+
See also `http.maxRetries` and `http.maxRetryTime`.
326+
327+
http.maxRetries::
328+
Maximum number of times to retry after receiving HTTP 429 (Too Many
329+
Requests) responses. Set to 0 (the default) to disable retries.
330+
Can be overridden by the `GIT_HTTP_MAX_RETRIES` environment variable.
331+
See also `http.retryAfter` and `http.maxRetryTime`.
332+
333+
http.maxRetryTime::
334+
Maximum time in seconds to wait for a single retry attempt when
335+
handling HTTP 429 (Too Many Requests) responses. If the server
336+
requests a delay (via Retry-After header) or if `http.retryAfter`
337+
is configured with a value that exceeds this maximum, Git will fail
338+
immediately rather than waiting. Default is 300 seconds (5 minutes).
339+
Can be overridden by the `GIT_HTTP_MAX_RETRY_TIME` environment
340+
variable. See also `http.retryAfter` and `http.maxRetries`.
341+
318342
http.noEPSV::
319343
A boolean which disables using of EPSV ftp command by curl.
320344
This can be helpful with some "poor" ftp servers which don't

http-push.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,10 @@ static int fetch_indices(void)
716716
case HTTP_MISSING_TARGET:
717717
ret = 0;
718718
break;
719+
case HTTP_RATE_LIMITED:
720+
error(_("rate limited by '%s', please try again later"), repo->url);
721+
ret = -1;
722+
break;
719723
default:
720724
ret = -1;
721725
}
@@ -1548,6 +1552,10 @@ static int remote_exists(const char *path)
15481552
case HTTP_MISSING_TARGET:
15491553
ret = 0;
15501554
break;
1555+
case HTTP_RATE_LIMITED:
1556+
error(_("rate limited by '%s', please try again later"), url);
1557+
ret = -1;
1558+
break;
15511559
case HTTP_ERROR:
15521560
error("unable to access '%s': %s", url, curl_errorstr);
15531561
/* fallthrough */

http-walker.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,11 @@ static int fetch_indices(struct walker *walker, struct alt_base *repo)
414414
repo->got_indices = 1;
415415
ret = 0;
416416
break;
417+
case HTTP_RATE_LIMITED:
418+
error("rate limited by '%s', please try again later", repo->base);
419+
repo->got_indices = 0;
420+
ret = -1;
421+
break;
417422
default:
418423
repo->got_indices = 0;
419424
ret = -1;

0 commit comments

Comments
 (0)