Files
git/t/lib-httpd/apply-one-time-script.sh
Michael Montalbo 5945464690 t/lib-httpd: fix apply-one-time-script race under concurrent requests
apply-one-time-script.sh is a test helper that executes a
"one-time-script" responsible for modifying the response normally
returned by git-http-backend. apply-one-time-script.sh should run
"one-time-script" once and return a modified response once. However,
sometimes a race between multiple concurrent requests causes
apply-one-time-script.sh to misbehave and return multiple modified
responses or an empty response that results in:

  fatal: ... The requested URL returned error: 500
  fatal: could not fetch <oid> from promisor remote

This can be seen in the flaky failure of t5616.47 on the macOS CI
runners.

Fix the logic that checks if "one-time-script" has returned its modified
response by chaining "rm one-time-script" with its execution. This
ensures a racing script does not also have the opportunity to execute
"one-time-script".

Add t/t5567-one-time-script.sh to verify the race is fixed. Implement a
stub "git-http-backend" that intentionally invokes a concurrent request,
and check that only one modified response is returned without error.

Signed-off-by: Michael Montalbo <mmontalbo@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-09-01 09:02:53 -07:00

37 lines
1.2 KiB
Bash

#!/bin/sh
# If "one-time-script" exists in $HTTPD_ROOT_PATH, run the script on the HTTP
# response. If the response was modified as a result, delete "one-time-script"
# so that subsequent HTTP responses are no longer modified.
#
# This can be used to simulate the effects of the repository changing in
# between HTTP request-response pairs.
test -f one-time-script || exec "$GIT_EXEC_PATH/git-http-backend"
LC_ALL=C
export LC_ALL
out=out.$$
modified=out-modified.$$
"$GIT_EXEC_PATH/git-http-backend" >"$out"
# Since Apache can execute this script for multiple requests
# concurrently, we chain "rm one-time-script" with the logic
# for generating a modified response. If the "rm" ran separately,
# a concurrent request could pass the "test -f" above and
# erroneously result in multiple modified responses or an empty
# body depending on the race state.
#
# We discard stderr for ./one-time-script since it is possible
# ./one-time-script has been removed already, which is expected
# sometimes. In this case, the unmodified response will be returned.
if ./one-time-script "$out" 2>/dev/null >"$modified" &&
! cmp -s "$out" "$modified" &&
rm one-time-script 2>/dev/null
then
cat "$modified"
else
cat "$out"
fi
rm -f "$out" "$modified"