Skip to content

Commit 56503c0

Browse files
committed
t/lib-httpd: add cgi-lib.sh for concurrency-safe CGI helpers
The CGI helpers under t/lib-httpd keep state between requests in the shared HTTPD_ROOT_PATH, on the assumption that the web server hands them one request at a time. It does not: the server serves requests concurrently, and a single Git operation can open more than one request to the same endpoint at once. A partial fetch that receives a REF_DELTA against a missing promisor object lazily fetches that base while the first response is still being served, and protocol v2 and HTTP/2 can multiplex further. A helper that reads shared state with a "test -f" and then acts on it, or that writes to a fixed-name scratch file, therefore races with a concurrent request; the loser emits a malformed response and the server answers HTTP 500. Rather than fix each helper in isolation, add a small library, cgi-lib.sh, that captures the safe idioms once: cgi_claim atomically claims a one-shot marker, and cgi_first_request is an atomic first-wins test-and-set. Scratch files use a documented $$ suffix rather than a helper, since that is all they need. Install the library into HTTPD_ROOT_PATH alongside the helpers so they can source it; the following commits build the stateful helpers on top of it. Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
1 parent e9019fc commit 56503c0

2 files changed

Lines changed: 32 additions & 0 deletions

File tree

t/lib-httpd.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ prepare_httpd() {
159159
mkdir -p "$HTTPD_DOCUMENT_ROOT_PATH"
160160
cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
161161
cp "$TEST_PATH"/proxy-passwd "$HTTPD_ROOT_PATH"
162+
cp "$TEST_PATH"/cgi-lib.sh "$HTTPD_ROOT_PATH"
162163
install_script incomplete-length-upload-pack-v2-http.sh
163164
install_script incomplete-body-upload-pack-v2-http.sh
164165
install_script error-no-report.sh

t/lib-httpd/cgi-lib.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Helpers for writing concurrency-safe CGI test scripts.
2+
#
3+
# The web server runs requests concurrently, and a single Git operation can
4+
# open more than one request to the same endpoint at once: a partial fetch
5+
# that receives a REF_DELTA against a missing promisor object lazily fetches
6+
# that base while the first response is still being served, and protocol v2
7+
# and HTTP/2 can multiplex further. A CGI helper that keeps state between
8+
# requests must therefore make each decision atomically and must not share
9+
# fixed-name scratch files between requests.
10+
#
11+
# For a scratch file, suffix its name with $$ (unique among live processes).
12+
# For a decision that only one of several concurrent requests may take, use one
13+
# of the primitives below rather than a "test -f" followed by a separate remove
14+
# or create, which races.
15+
16+
# cgi_claim NAME
17+
# Atomically claim NAME by renaming it aside. Succeeds for exactly one of
18+
# any number of concurrent callers; the rest, and callers that arrive
19+
# after NAME is gone, fail. Use to consume a one-shot marker that a test
20+
# planted.
21+
cgi_claim () {
22+
mv "$1" "$1.$$" 2>/dev/null
23+
}
24+
25+
# cgi_first_request NAME
26+
# Succeed for exactly one of any number of concurrent callers and fail
27+
# for the rest, by creating NAME. mkdir(2) is atomic and fails if NAME
28+
# exists, which makes the test-and-set a single step.
29+
cgi_first_request () {
30+
mkdir "$1" 2>/dev/null
31+
}

0 commit comments

Comments
 (0)