|
| 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