Raw
1 #!/bin/sh
2
3 test_description='apply-one-time-script CGI helper is safe under concurrent requests'
4
5 . ./test-lib.sh
6
7 HELPER="$TEST_DIRECTORY/lib-httpd/apply-one-time-script.sh"
8
9 test_expect_success PIPE 'concurrent requests: one rewritten, one passed through, neither empty' '
10 mkdir workdir fakebin &&
11 ENTERED="$PWD/entered" &&
12 GATE="$PWD/gate" &&
13 export ENTERED GATE &&
14 mkfifo "$ENTERED" "$GATE" &&
15
16 # Stand in for git-http-backend. The modify role returns a response
17 # containing "packfile", which the one-time script rewrites. The
18 # passthrough role returns a response that is left untouched, but first
19 # announces that it has entered the helper and then blocks, so that it
20 # is still in flight when the modify role claims and removes the marker.
21 write_script fakebin/git-http-backend <<-\EOF &&
22 printf "Status: 200 OK\r\n"
23 printf "Content-Type: application/x-git-result\r\n"
24 printf "\r\n"
25 if test "$ROLE" = modify
26 then
27 printf "packfile\n"
28 else
29 echo entered >"$ENTERED"
30 read -r released <"$GATE"
31 printf "refs\n"
32 fi
33 EOF
34
35 # The transform that replace_packfile would install as one-time-script:
36 # rewrite responses that contain "packfile", leave the rest alone.
37 write_script workdir/one-time-script <<-\EOF &&
38 if grep packfile "$1" >/dev/null
39 then
40 sed "/packfile/q" "$1" &&
41 printf "REPLACED\n"
42 else
43 cat "$1"
44 fi
45 EOF
46
47 GIT_EXEC_PATH="$PWD/fakebin" &&
48 export GIT_EXEC_PATH &&
49
50 # Hold GATE open read-write on fd 9 for the duration, so releasing the
51 # passthrough request below cannot block even if that request has
52 # already exited (it keeps a reader on the FIFO).
53 exec 9<>"$GATE" &&
54
55 # Launch the passthrough request in the background. It enters the
56 # helper, signals us through ENTERED, then blocks on GATE inside the
57 # fake backend. The braces keep the && chain intact while backgrounding
58 # only the subshell, so "wait" can reap it by pid; kill it on any exit
59 # so a stray blocked child cannot hold the test output open and stall a
60 # reader such as prove.
61 { (
62 cd workdir &&
63 ROLE=passthrough sh "$HELPER" >../passthrough.out 2>../passthrough.err
64 ) & } &&
65 passthrough_pid=$! &&
66 test_when_finished "kill $passthrough_pid 2>/dev/null || :" &&
67
68 # Wait until the passthrough request is past the marker check.
69 read -r entered <"$ENTERED" &&
70
71 # Run the modifying request to completion while the passthrough request
72 # is still blocked.
73 (
74 cd workdir &&
75 ROLE=modify sh "$HELPER" >../modify.out 2>../modify.err
76 ) &&
77
78 # Release the passthrough request and let it finish. Ignore the helper
79 # exit status here so a broken helper is diagnosed by the assertions
80 # below rather than aborting the test.
81 echo released >&9 &&
82 { wait "$passthrough_pid" || :; } &&
83
84 # Neither request may error out or produce an empty (HTTP 500) body,
85 # and each must have played its role: the modify request rewrote its
86 # response and the passthrough request came through untouched.
87 test_must_be_empty passthrough.err &&
88 test_must_be_empty modify.err &&
89 test_grep "Status: 200 OK" passthrough.out &&
90 test_grep "Status: 200 OK" modify.out &&
91 test_grep REPLACED modify.out &&
92 test_grep ! REPLACED passthrough.out &&
93 test_grep refs passthrough.out
94 '
95
96 test_done