Skip to content

Commit ff0dd0a

Browse files
draft-refactor-test-retry-state-handling
1 parent 380be46 commit ff0dd0a

File tree

4 files changed

+150
-185
lines changed

4 files changed

+150
-185
lines changed

t/lib-httpd.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ prepare_httpd() {
167167
install_script error.sh
168168
install_script apply-one-time-script.sh
169169
install_script nph-custom-auth.sh
170+
install_script http-429.sh
170171

171172
ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
172173

t/lib-httpd/apache.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ SetEnv PERL_PATH ${PERL_PATH}
139139
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
140140
SetEnv GIT_HTTP_EXPORT_ALL
141141
</LocationMatch>
142+
<LocationMatch /http_429/>
143+
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
144+
SetEnv GIT_HTTP_EXPORT_ALL
145+
</LocationMatch>
142146
<LocationMatch /smart_v0/>
143147
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
144148
SetEnv GIT_HTTP_EXPORT_ALL
@@ -160,6 +164,7 @@ ScriptAlias /broken_smart/ broken-smart-http.sh/
160164
ScriptAlias /error_smart/ error-smart-http.sh/
161165
ScriptAlias /error/ error.sh/
162166
ScriptAliasMatch /one_time_script/(.*) apply-one-time-script.sh/$1
167+
ScriptAliasMatch /http_429/(.*) http-429.sh/$1
163168
ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
164169
<Directory ${GIT_EXEC_PATH}>
165170
Options FollowSymlinks
@@ -185,6 +190,9 @@ ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
185190
<Files apply-one-time-script.sh>
186191
Options ExecCGI
187192
</Files>
193+
<Files http-429.sh>
194+
Options ExecCGI
195+
</Files>
188196
<Files ${GIT_EXEC_PATH}/git-http-backend>
189197
Options ExecCGI
190198
</Files>

t/lib-httpd/http-429.sh

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/bin/sh
2+
3+
# Script to return HTTP 429 Too Many Requests responses for testing retry logic.
4+
# Usage: /http_429/<test-context>/<retry-after-value>/<repo-path>
5+
#
6+
# The test-context is a unique identifier for each test to isolate state files.
7+
# The retry-after-value can be:
8+
# - A number (e.g., "1", "2", "100") - sets Retry-After header to that many seconds
9+
# - "none" - no Retry-After header
10+
# - "invalid" - invalid Retry-After format
11+
# - "permanent" - always return 429 (never succeed)
12+
# - An HTTP-date string (RFC 2822 format) - sets Retry-After to that date
13+
#
14+
# On first call, returns 429. On subsequent calls (after retry), forwards to git-http-backend
15+
# unless retry-after-value is "permanent".
16+
17+
# Extract test context, retry-after value and repo path from PATH_INFO
18+
# PATH_INFO format: /<test-context>/<retry-after-value>/<repo-path>
19+
path_info="${PATH_INFO#/}" # Remove leading slash
20+
test_context="${path_info%%/*}" # Get first component (test context)
21+
remaining="${path_info#*/}" # Get rest
22+
retry_after="${remaining%%/*}" # Get second component (retry-after value)
23+
repo_path="${remaining#*/}" # Get rest (repo path)
24+
25+
# Extract repository name from repo_path (e.g., "repo.git" from "repo.git/info/refs")
26+
# The repo name is the first component before any "/"
27+
repo_name="${repo_path%%/*}"
28+
29+
# Use current directory (HTTPD_ROOT_PATH) for state file
30+
# Create a safe filename from test_context, retry_after and repo_name
31+
# This ensures all requests for the same test context share the same state file
32+
safe_name=$(echo "${test_context}-${retry_after}-${repo_name}" | tr '/' '_' | tr -cd 'a-zA-Z0-9_-')
33+
state_file="http-429-state-${safe_name}"
34+
35+
# Check if this is the first call (no state file exists)
36+
if test -f "$state_file"
37+
then
38+
# Already returned 429 once, forward to git-http-backend
39+
# Set PATH_INFO to just the repo path (without retry-after value)
40+
# Set GIT_PROJECT_ROOT so git-http-backend can find the repository
41+
# Use exec to replace this process so git-http-backend gets the updated environment
42+
PATH_INFO="/$repo_path"
43+
export PATH_INFO
44+
# GIT_PROJECT_ROOT points to the document root where repositories are stored
45+
# The script runs from HTTPD_ROOT_PATH, and www/ is the document root
46+
if test -z "$GIT_PROJECT_ROOT"
47+
then
48+
# Construct path: current directory (HTTPD_ROOT_PATH) + /www
49+
GIT_PROJECT_ROOT="$(pwd)/www"
50+
export GIT_PROJECT_ROOT
51+
fi
52+
exec "$GIT_EXEC_PATH/git-http-backend"
53+
fi
54+
55+
# Mark that we've returned 429
56+
touch "$state_file"
57+
58+
# Output HTTP 429 response
59+
printf "Status: 429 Too Many Requests\r\n"
60+
61+
# Set Retry-After header based on retry_after value
62+
case "$retry_after" in
63+
none)
64+
# No Retry-After header
65+
;;
66+
invalid)
67+
printf "Retry-After: invalid-format-123abc\r\n"
68+
;;
69+
permanent)
70+
# Always return 429, don't set state file for success
71+
rm -f "$state_file"
72+
printf "Retry-After: 1\r\n"
73+
printf "Content-Type: text/plain\r\n"
74+
printf "\r\n"
75+
printf "Permanently rate limited\n"
76+
exit 0
77+
;;
78+
*)
79+
# Check if it's a number
80+
case "$retry_after" in
81+
[0-9]*)
82+
# Numeric value
83+
printf "Retry-After: %s\r\n" "$retry_after"
84+
;;
85+
*)
86+
# Assume it's an HTTP-date format (passed as-is, URL decoded)
87+
# Apache may URL-encode the path, so decode common URL-encoded characters
88+
# %20 = space, %2C = comma, %3A = colon
89+
retry_value=$(echo "$retry_after" | sed -e 's/%20/ /g' -e 's/%2C/,/g' -e 's/%3A/:/g')
90+
printf "Retry-After: %s\r\n" "$retry_value"
91+
;;
92+
esac
93+
;;
94+
esac
95+
96+
printf "Content-Type: text/plain\r\n"
97+
printf "\r\n"
98+
printf "Rate limited\n"
99+

0 commit comments

Comments
 (0)