t/lib-httpd: refactor "one-time-perl" CGI script to not depend on Perl

Our Apache HTTPD setup exposes an "one_time_perl" endpoint to access repositories. If used, we execute the "apply-one-time-perl.sh" CGI script that checks whether we have a "one-time-perl" script. If so, that script gets executed so that it can munge what would be served. Once done, the script gets removed so that it doesn't execute a second time. As the name says, this functionality expects the user to pass a Perl script. This isn't really necessary though: we can just as easily implement the same thing with arbitrary scripts. Refactor the code so that we instead expect an arbitrary script to exist and rename the functionality to "one-time-script". Adapt callers to use shell utilities instead of Perl so that we can drop the PERL_TEST_HELPERS prerequisite. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Apr 3, 2025 at 07:06 UTC cee137b7e53403fe9170c9aeb758fa946b2f0c4c
8 files changed +86 -77
t/lib-httpd.sh
+1 -1
@@ -165,7 +165,7 @@ prepare_httpd() {
165 install_script broken-smart-http.sh
166 install_script error-smart-http.sh
167 install_script error.sh
168 - install_script apply-one-time-perl.sh
168 + install_script apply-one-time-script.sh
169 install_script nph-custom-auth.sh
170
171 ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
t/lib-httpd/apache.conf
+3 -3
@@ -135,7 +135,7 @@ SetEnv PERL_PATH ${PERL_PATH}
135 SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
136 SetEnv GIT_HTTP_EXPORT_ALL
137 </LocationMatch>
138 -<LocationMatch /one_time_perl/>
138 +<LocationMatch /one_time_script/>
139 SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
140 SetEnv GIT_HTTP_EXPORT_ALL
141 </LocationMatch>
@@ -159,7 +159,7 @@ ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
159 ScriptAlias /broken_smart/ broken-smart-http.sh/
160 ScriptAlias /error_smart/ error-smart-http.sh/
161 ScriptAlias /error/ error.sh/
162 -ScriptAliasMatch /one_time_perl/(.*) apply-one-time-perl.sh/$1
162 +ScriptAliasMatch /one_time_script/(.*) apply-one-time-script.sh/$1
163 ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
164 <Directory ${GIT_EXEC_PATH}>
165 Options FollowSymlinks
@@ -182,7 +182,7 @@ ScriptAliasMatch /custom_auth/(.*) nph-custom-auth.sh/$1
182 <Files error.sh>
183 Options ExecCGI
184 </Files>
185 -<Files apply-one-time-perl.sh>
185 +<Files apply-one-time-script.sh>
186 Options ExecCGI
187 </Files>
188 <Files ${GIT_EXEC_PATH}/git-http-backend>
t/lib-httpd/apply-one-time-perl.sh deleted
-27
@@ -1,27 +0,0 @@
1 -#!/bin/sh
2 -
3 -# If "one-time-perl" exists in $HTTPD_ROOT_PATH, run perl on the HTTP response,
4 -# using the contents of "one-time-perl" as the perl command to be run. If the
5 -# response was modified as a result, delete "one-time-perl" so that subsequent
6 -# HTTP responses are no longer modified.
7 -#
8 -# This can be used to simulate the effects of the repository changing in
9 -# between HTTP request-response pairs.
10 -if test -f one-time-perl
11 -then
12 - LC_ALL=C
13 - export LC_ALL
14 -
15 - "$GIT_EXEC_PATH/git-http-backend" >out
16 - "$PERL_PATH" -pe "$(cat one-time-perl)" out >out_modified
17 -
18 - if cmp -s out out_modified
19 - then
20 - cat out
21 - else
22 - cat out_modified
23 - rm one-time-perl
24 - fi
25 -else
26 - "$GIT_EXEC_PATH/git-http-backend"
27 -fi
t/lib-httpd/apply-one-time-script.sh new
+26
@@ -0,0 +1,26 @@
1 +#!/bin/sh
2 +
3 +# If "one-time-script" exists in $HTTPD_ROOT_PATH, run the script on the HTTP
4 +# response. If the response was modified as a result, delete "one-time-script"
5 +# so that subsequent HTTP responses are no longer modified.
6 +#
7 +# This can be used to simulate the effects of the repository changing in
8 +# between HTTP request-response pairs.
9 +if test -f one-time-script
10 +then
11 + LC_ALL=C
12 + export LC_ALL
13 +
14 + "$GIT_EXEC_PATH/git-http-backend" >out
15 + ./one-time-script out >out_modified
16 +
17 + if cmp -s out out_modified
18 + then
19 + cat out
20 + else
21 + cat out_modified
22 + rm one-time-script
23 + fi
24 +else
25 + "$GIT_EXEC_PATH/git-http-backend"
26 +fi
t/t5537-fetch-shallow.sh
+8 -9
@@ -256,7 +256,7 @@ start_httpd
256
257 REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo"
258
259 -test_expect_success PERL_TEST_HELPERS 'shallow fetches check connectivity before writing shallow file' '
259 +test_expect_success 'shallow fetches check connectivity before writing shallow file' '
260 rm -rf "$REPO" client &&
261
262 git init "$REPO" &&
@@ -271,22 +271,21 @@ test_expect_success PERL_TEST_HELPERS 'shallow fetches check connectivity before
271 git -C "$REPO" config protocol.version 2 &&
272 git -C client config protocol.version 2 &&
273
274 - git -C client fetch --depth=2 "$HTTPD_URL/one_time_perl/repo" main:a_branch &&
274 + git -C client fetch --depth=2 "$HTTPD_URL/one_time_script/repo" main:a_branch &&
275
276 # Craft a situation in which the server sends back an unshallow request
277 # with an empty packfile. This is done by refetching with a shorter
278 # depth (to ensure that the packfile is empty), and overwriting the
279 # shallow line in the response with the unshallow line we want.
280 - printf "$(test_oid perl)" \
281 - "$(git -C "$REPO" rev-parse HEAD)" \
282 - "$(git -C "$REPO" rev-parse HEAD^)" \
283 - >"$HTTPD_ROOT_PATH/one-time-perl" &&
280 + write_script "$HTTPD_ROOT_PATH/one-time-script" <<-EOF &&
281 + sed "$(printf "$(test_oid perl)" "$(git -C "$REPO" rev-parse HEAD)" "$(git -C "$REPO" rev-parse HEAD^)")" "\$1"
282 + EOF
283 test_must_fail env GIT_TEST_SIDEBAND_ALL=0 git -C client \
285 - fetch --depth=1 "$HTTPD_URL/one_time_perl/repo" \
284 + fetch --depth=1 "$HTTPD_URL/one_time_script/repo" \
285 main:a_branch &&
286
288 - # Ensure that the one-time-perl script was used.
289 - ! test -e "$HTTPD_ROOT_PATH/one-time-perl" &&
287 + # Ensure that the one-time-script script was used.
288 + ! test -e "$HTTPD_ROOT_PATH/one-time-script" &&
289
290 # Ensure that the resulting repo is consistent, despite our failure to
291 # fetch.
t/t5616-partial-clone.sh
+26 -22
@@ -737,21 +737,25 @@ intersperse () {
737 sed 's/\(..\)/'$1'\1/g'
738 }
739
740 -# Create a one-time-perl command to replace the existing packfile with $1.
740 +# Create a one-time-script command to replace the existing packfile with $1.
741 replace_packfile () {
742 - # The protocol requires that the packfile be sent in sideband 1, hence
743 - # the extra \x01 byte at the beginning.
744 - cp $1 "$HTTPD_ROOT_PATH/one-time-pack" &&
745 - echo 'if (/packfile/) {
746 - print;
747 - my $length = -s "one-time-pack";
748 - printf "%04x\x01", $length + 5;
749 - print `cat one-time-pack` . "0000";
750 - last
751 - }' >"$HTTPD_ROOT_PATH/one-time-perl"
742 + cp "$1" one-time-pack &&
743 + write_script "$HTTPD_ROOT_PATH/one-time-script" <<-EOF
744 + if grep packfile "\$1" >/dev/null
745 + then
746 + sed '/packfile/q' "\$1" &&
747 + # The protocol requires that the packfile be sent in sideband
748 + # 1, hence the extra \001 byte at the beginning.
749 + printf "%04x\001" \$((\$(wc -c <"$PWD/one-time-pack") + 5)) &&
750 + cat "$PWD/one-time-pack" &&
751 + printf "0000"
752 + else
753 + cat "\$1"
754 + fi
755 + EOF
756 }
757
754 -test_expect_success PERL_TEST_HELPERS 'upon cloning, check that all refs point to objects' '
758 +test_expect_success 'upon cloning, check that all refs point to objects' '
759 SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
760 rm -rf "$SERVER" repo &&
761 test_create_repo "$SERVER" &&
@@ -776,15 +780,15 @@ test_expect_success PERL_TEST_HELPERS 'upon cloning, check that all refs point t
780 # section header.
781 test_config -C "$SERVER" protocol.version 2 &&
782 test_must_fail git -c protocol.version=2 clone \
779 - --filter=blob:none $HTTPD_URL/one_time_perl/server repo 2>err &&
783 + --filter=blob:none $HTTPD_URL/one_time_script/server repo 2>err &&
784
785 test_grep "did not send all necessary objects" err &&
786
783 - # Ensure that the one-time-perl script was used.
784 - ! test -e "$HTTPD_ROOT_PATH/one-time-perl"
787 + # Ensure that the one-time-script script was used.
788 + ! test -e "$HTTPD_ROOT_PATH/one-time-script"
789 '
790
787 -test_expect_success PERL_TEST_HELPERS 'when partial cloning, tolerate server not sending target of tag' '
791 +test_expect_success 'when partial cloning, tolerate server not sending target of tag' '
792 SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
793 rm -rf "$SERVER" repo &&
794 test_create_repo "$SERVER" &&
@@ -818,11 +822,11 @@ test_expect_success PERL_TEST_HELPERS 'when partial cloning, tolerate server not
822
823 # Exercise to make sure it works.
824 git -c protocol.version=2 clone \
821 - --filter=blob:none $HTTPD_URL/one_time_perl/server repo 2> err &&
825 + --filter=blob:none $HTTPD_URL/one_time_script/server repo 2> err &&
826 ! grep "missing object referenced by" err &&
827
824 - # Ensure that the one-time-perl script was used.
825 - ! test -e "$HTTPD_ROOT_PATH/one-time-perl"
828 + # Ensure that the one-time-script script was used.
829 + ! test -e "$HTTPD_ROOT_PATH/one-time-script"
830 '
831
832 test_expect_success PERL_TEST_HELPERS 'tolerate server sending REF_DELTA against missing promisor objects' '
@@ -845,7 +849,7 @@ test_expect_success PERL_TEST_HELPERS 'tolerate server sending REF_DELTA against
849
850 # Clone. The client has deltabase_have but not deltabase_missing.
851 git -c protocol.version=2 clone --no-checkout \
848 - --filter=blob:none $HTTPD_URL/one_time_perl/server repo &&
852 + --filter=blob:none $HTTPD_URL/one_time_script/server repo &&
853 git -C repo hash-object -w -- "$SERVER/have.txt" &&
854
855 # Sanity check to ensure that the client does not have
@@ -899,8 +903,8 @@ test_expect_success PERL_TEST_HELPERS 'tolerate server sending REF_DELTA against
903 grep "want $(cat deltabase_missing)" trace &&
904 ! grep "want $(cat deltabase_have)" trace &&
905
902 - # Ensure that the one-time-perl script was used.
903 - ! test -e "$HTTPD_ROOT_PATH/one-time-perl"
906 + # Ensure that the one-time-script script was used.
907 + ! test -e "$HTTPD_ROOT_PATH/one-time-script"
908 '
909
910 # DO NOT add non-httpd-specific tests here, because the last part of this
t/t5702-protocol-v2.sh
+15 -12
@@ -1120,7 +1120,7 @@ test_expect_success 'push with http:// and a config of v2 does not request v2' '
1120 ! grep "git< version 2" log
1121 '
1122
1123 -test_expect_success PERL_TEST_HELPERS 'when server sends "ready", expect DELIM' '
1123 +test_expect_success 'when server sends "ready", expect DELIM' '
1124 rm -rf "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" http_child &&
1125
1126 git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
@@ -1132,15 +1132,16 @@ test_expect_success PERL_TEST_HELPERS 'when server sends "ready", expect DELIM'
1132
1133 # After "ready" in the acknowledgments section, pretend that a FLUSH
1134 # (0000) was sent instead of a DELIM (0001).
1135 - printf "\$ready = 1 if /ready/; \$ready && s/0001/0000/" \
1136 - >"$HTTPD_ROOT_PATH/one-time-perl" &&
1135 + write_script "$HTTPD_ROOT_PATH/one-time-script" <<-\EOF &&
1136 + sed "/ready/{n;s/0001/0000/;}" "$1"
1137 + EOF
1138
1139 test_must_fail git -C http_child -c protocol.version=2 \
1139 - fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
1140 + fetch "$HTTPD_URL/one_time_script/http_parent" 2> err &&
1141 test_grep "expected packfile to be sent after .ready." err
1142 '
1143
1143 -test_expect_success PERL_TEST_HELPERS 'when server does not send "ready", expect FLUSH' '
1144 +test_expect_success 'when server does not send "ready", expect FLUSH' '
1145 rm -rf "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" http_child log &&
1146
1147 git init "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" &&
@@ -1157,12 +1158,13 @@ test_expect_success PERL_TEST_HELPERS 'when server does not send "ready", expect
1158
1159 # After the acknowledgments section, pretend that a DELIM
1160 # (0001) was sent instead of a FLUSH (0000).
1160 - printf "\$ack = 1 if /acknowledgments/; \$ack && s/0000/0001/" \
1161 - >"$HTTPD_ROOT_PATH/one-time-perl" &&
1161 + write_script "$HTTPD_ROOT_PATH/one-time-script" <<-\EOF &&
1162 + sed "/acknowledgments/,//{s/0000/0001/;}" "$1"
1163 + EOF
1164
1165 test_must_fail env GIT_TRACE_PACKET="$(pwd)/log" git -C http_child \
1166 -c protocol.version=2 \
1165 - fetch "$HTTPD_URL/one_time_perl/http_parent" 2> err &&
1167 + fetch "$HTTPD_URL/one_time_script/http_parent" 2> err &&
1168 grep "fetch< .*acknowledgments" log &&
1169 ! grep "fetch< .*ready" log &&
1170 test_grep "expected no other sections to be sent after no .ready." err
@@ -1446,14 +1448,15 @@ test_expect_success 'http:// --negotiate-only' '
1448 grep "$COMMON" out
1449 '
1450
1449 -test_expect_success PERL_TEST_HELPERS 'http:// --negotiate-only without wait-for-done support' '
1451 +test_expect_success 'http:// --negotiate-only without wait-for-done support' '
1452 SERVER="server" &&
1451 - URI="$HTTPD_URL/one_time_perl/server" &&
1453 + URI="$HTTPD_URL/one_time_script/server" &&
1454
1455 setup_negotiate_only "$SERVER" "$URI" &&
1456
1455 - echo "s/ wait-for-done/ xxxx-xxx-xxxx/" \
1456 - >"$HTTPD_ROOT_PATH/one-time-perl" &&
1457 + write_script "$HTTPD_ROOT_PATH/one-time-script" <<-\EOF &&
1458 + sed "s/ wait-for-done/ xxxx-xxx-xxxx/" "$1"
1459 + EOF
1460
1461 test_must_fail git -c protocol.version=2 -C client fetch \
1462 --no-tags \
t/t5703-upload-pack-ref-in-want.sh
+7 -3
@@ -468,7 +468,7 @@ test_expect_success 'setup repos for change-while-negotiating test' '
468 test_commit m3 &&
469 git tag -d m2 m3
470 ) &&
471 - git -C "$LOCAL_PRISTINE" remote set-url origin "http://127.0.0.1:$LIB_HTTPD_PORT/one_time_perl/repo" &&
471 + git -C "$LOCAL_PRISTINE" remote set-url origin "http://127.0.0.1:$LIB_HTTPD_PORT/one_time_script/repo" &&
472 git -C "$LOCAL_PRISTINE" config protocol.version 2
473 '
474
@@ -481,7 +481,9 @@ inconsistency () {
481 # RPCs during a single negotiation.
482 oid1=$(git -C "$REPO" rev-parse $1) &&
483 oid2=$(git -C "$REPO" rev-parse $2) &&
484 - echo "s/$oid1/$oid2/" >"$HTTPD_ROOT_PATH/one-time-perl"
484 + write_script "$HTTPD_ROOT_PATH/one-time-script" <<-EOF
485 + sed "s/$oid1/$oid2/" "\$1"
486 + EOF
487 }
488
489 test_expect_success 'server is initially ahead - no ref in want' '
@@ -533,7 +535,9 @@ test_expect_success 'server loses a ref - ref in want' '
535 git -C "$REPO" config uploadpack.allowRefInWant true &&
536 rm -rf local &&
537 cp -r "$LOCAL_PRISTINE" local &&
536 - echo "s/main/rain/" >"$HTTPD_ROOT_PATH/one-time-perl" &&
538 + write_script "$HTTPD_ROOT_PATH/one-time-script" <<-\EOF &&
539 + sed "s/main/rain/" "$1"
540 + EOF
541 test_must_fail git -C local fetch 2>err &&
542
543 test_grep "fatal: remote error: unknown ref refs/heads/rain" err