97 lines
3.1 KiB
Bash
Executable File
97 lines
3.1 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
test_description='apply-one-time-script CGI helper is safe under concurrent requests'
|
|
|
|
. ./test-lib.sh
|
|
|
|
HELPER="$TEST_DIRECTORY/lib-httpd/apply-one-time-script.sh"
|
|
|
|
test_expect_success PIPE 'concurrent requests: one rewritten, one passed through, neither empty' '
|
|
mkdir workdir fakebin &&
|
|
ENTERED="$PWD/entered" &&
|
|
GATE="$PWD/gate" &&
|
|
export ENTERED GATE &&
|
|
mkfifo "$ENTERED" "$GATE" &&
|
|
|
|
# Stand in for git-http-backend. The modify role returns a response
|
|
# containing "packfile", which the one-time script rewrites. The
|
|
# passthrough role returns a response that is left untouched, but first
|
|
# announces that it has entered the helper and then blocks, so that it
|
|
# is still in flight when the modify role claims and removes the marker.
|
|
write_script fakebin/git-http-backend <<-\EOF &&
|
|
printf "Status: 200 OK\r\n"
|
|
printf "Content-Type: application/x-git-result\r\n"
|
|
printf "\r\n"
|
|
if test "$ROLE" = modify
|
|
then
|
|
printf "packfile\n"
|
|
else
|
|
echo entered >"$ENTERED"
|
|
read -r released <"$GATE"
|
|
printf "refs\n"
|
|
fi
|
|
EOF
|
|
|
|
# The transform that replace_packfile would install as one-time-script:
|
|
# rewrite responses that contain "packfile", leave the rest alone.
|
|
write_script workdir/one-time-script <<-\EOF &&
|
|
if grep packfile "$1" >/dev/null
|
|
then
|
|
sed "/packfile/q" "$1" &&
|
|
printf "REPLACED\n"
|
|
else
|
|
cat "$1"
|
|
fi
|
|
EOF
|
|
|
|
GIT_EXEC_PATH="$PWD/fakebin" &&
|
|
export GIT_EXEC_PATH &&
|
|
|
|
# Hold GATE open read-write on fd 9 for the duration, so releasing the
|
|
# passthrough request below cannot block even if that request has
|
|
# already exited (it keeps a reader on the FIFO).
|
|
exec 9<>"$GATE" &&
|
|
|
|
# Launch the passthrough request in the background. It enters the
|
|
# helper, signals us through ENTERED, then blocks on GATE inside the
|
|
# fake backend. The braces keep the && chain intact while backgrounding
|
|
# only the subshell, so "wait" can reap it by pid; kill it on any exit
|
|
# so a stray blocked child cannot hold the test output open and stall a
|
|
# reader such as prove.
|
|
{ (
|
|
cd workdir &&
|
|
ROLE=passthrough sh "$HELPER" >../passthrough.out 2>../passthrough.err
|
|
) & } &&
|
|
passthrough_pid=$! &&
|
|
test_when_finished "kill $passthrough_pid 2>/dev/null || :" &&
|
|
|
|
# Wait until the passthrough request is past the marker check.
|
|
read -r entered <"$ENTERED" &&
|
|
|
|
# Run the modifying request to completion while the passthrough request
|
|
# is still blocked.
|
|
(
|
|
cd workdir &&
|
|
ROLE=modify sh "$HELPER" >../modify.out 2>../modify.err
|
|
) &&
|
|
|
|
# Release the passthrough request and let it finish. Ignore the helper
|
|
# exit status here so a broken helper is diagnosed by the assertions
|
|
# below rather than aborting the test.
|
|
echo released >&9 &&
|
|
{ wait "$passthrough_pid" || :; } &&
|
|
|
|
# Neither request may error out or produce an empty (HTTP 500) body,
|
|
# and each must have played its role: the modify request rewrote its
|
|
# response and the passthrough request came through untouched.
|
|
test_must_be_empty passthrough.err &&
|
|
test_must_be_empty modify.err &&
|
|
test_grep "Status: 200 OK" passthrough.out &&
|
|
test_grep "Status: 200 OK" modify.out &&
|
|
test_grep REPLACED modify.out &&
|
|
test_grep ! REPLACED passthrough.out &&
|
|
test_grep refs passthrough.out
|
|
'
|
|
|
|
test_done
|