Raw
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.
30 # Create a safe name from test_context, retry_after and repo_name so that all
31 # requests for the same test context share the same state.
32 safe_name=$(echo "${test_context}-${retry_after}-${repo_name}" | tr '/' '_' | tr -cd 'a-zA-Z0-9_-')
33 state="http-429-state-${safe_name}"
34
35 # This endpoint returns 429 to the first request and forwards later ones to
36 # git-http-backend, so the retry succeeds. Apache can run this CGI for several
37 # requests at once, so a single atomic "mkdir" elects that first request: the
38 # one whose mkdir succeeds returns 429 and leaves the directory behind as the
39 # "already rate-limited" marker; every later request finds the directory (mkdir
40 # fails) and is forwarded.
41 #
42 # "permanent" is the exception: it must return 429 to every request and never
43 # succeed, so it skips the mkdir and records no state. A leftover directory
44 # would make its own later requests find the marker and be forwarded, which is
45 # exactly what "permanent" must not do.
46 if test "$retry_after" != permanent && ! mkdir "$state" 2>/dev/null
47 then
48 # Already returned 429 once, forward to git-http-backend
49 # Set PATH_INFO to just the repo path (without retry-after value)
50 # Set GIT_PROJECT_ROOT so git-http-backend can find the repository
51 # Use exec to replace this process so git-http-backend gets the updated environment
52 PATH_INFO="/$repo_path"
53 export PATH_INFO
54 # GIT_PROJECT_ROOT points to the document root where repositories are stored
55 # The script runs from HTTPD_ROOT_PATH, and www/ is the document root
56 if test -z "$GIT_PROJECT_ROOT"
57 then
58 # Construct path: current directory (HTTPD_ROOT_PATH) + /www
59 GIT_PROJECT_ROOT="$(pwd)/www"
60 export GIT_PROJECT_ROOT
61 fi
62 exec "$GIT_EXEC_PATH/git-http-backend"
63 fi
64
65 # Output HTTP 429 response
66 printf "Status: 429 Too Many Requests\r\n"
67
68 # Set Retry-After header based on retry_after value
69 case "$retry_after" in
70 none)
71 # No Retry-After header
72 ;;
73 invalid)
74 printf "Retry-After: invalid-format-123abc\r\n"
75 ;;
76 permanent)
77 # Always return 429
78 printf "Retry-After: 1\r\n"
79 printf "Content-Type: text/plain\r\n"
80 printf "\r\n"
81 printf "Permanently rate limited\n"
82 exit 0
83 ;;
84 *)
85 # Check if it's a number
86 case "$retry_after" in
87 [0-9]*)
88 # Numeric value
89 printf "Retry-After: %s\r\n" "$retry_after"
90 ;;
91 *)
92 # Assume it's an HTTP-date format (passed as-is, URL decoded)
93 # Apache may URL-encode the path, so decode common URL-encoded characters
94 # %20 = space, %2C = comma, %3A = colon
95 retry_value=$(echo "$retry_after" | sed -e 's/%20/ /g' -e 's/%2C/,/g' -e 's/%3A/:/g')
96 printf "Retry-After: %s\r\n" "$retry_value"
97 ;;
98 esac
99 ;;
100 esac
101
102 printf "Content-Type: text/plain\r\n"
103 printf "\r\n"
104 printf "Rate limited\n"