Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/windows-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,11 @@ jobs:
# tested nothing: pin the skips, and floor the passes in case the glob empties.
# footer-overflow and purge-longpath skip on Windows (need a path past MAX_PATH);
# crange pending #581;
# webdav-mime needs a reapable background listener, which MSYS cannot give it;
# webdav-default and webdav-mime need a reapable background listener, which MSYS cannot give them;
# badmtime needs a filesystem that stores an mtime past gmtime's range;
# single-file ends on a GUI half needing htsserver, which this job does not build;
# update-304-leak needs a LeakSanitizer build, which MSVC has no equivalent of.
expected_skips=" 01_engine-footer-overflow.test 100_local-purge-longpath.test 114_local-update-304-leak.test 48_local-crange-memresume.test 71_local-crange-repaircache.test 79_local-proxytrack-webdav-mime.test 88_local-proxytrack-badmtime.test 94_local-single-file.test"
expected_skips=" 01_engine-footer-overflow.test 100_local-purge-longpath.test 114_local-update-304-leak.test 120_local-proxytrack-webdav-default.test 48_local-crange-memresume.test 71_local-crange-repaircache.test 79_local-proxytrack-webdav-mime.test 88_local-proxytrack-badmtime.test 94_local-single-file.test"
# First, or the deadline reads as an unexplained shortfall in the gates below.
[ "$deadline" -eq 0 ] || { echo "::error::suite did not finish within ${suite_deadline}s"; exit 1; }
[ "$pass" -ge 90 ] || { echo "::error::only $pass tests passed ($skip skipped)"; exit 1; }
Expand Down
6 changes: 3 additions & 3 deletions src/proxy/proxytrack.c
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,8 @@ static PT_Element proxytrack_process_DAV_Request(PT_Indexes indexes,
const char *thisUrl = list[i];
const char *mimeType = "application/octet-stream";
unsigned int thisUrlLen = (unsigned int) strlen(thisUrl);
int thisIsDir = (thisUrl[thisUrlLen - 1] == '/') ? 1 : 0;
/* the folder's default document is enumerated as an empty name */
int thisIsDir = (hts_lastchar(thisUrl) == '/') ? 1 : 0;

/* Item URL */
StringRoom(itemUrl,
Expand Down Expand Up @@ -853,8 +854,7 @@ static PT_Element proxytrack_process_HTTP_List(PT_Indexes indexes,
for(isDir = 1; isDir >= 0; isDir--) {
for(i = 0; list[i] != NULL; i++) {
char *thisUrl = list[i];
unsigned int thisUrlLen = (unsigned int) strlen(thisUrl);
int thisIsDir = (thisUrl[thisUrlLen - 1] == '/') ? 1 : 0;
int thisIsDir = (hts_lastchar(thisUrl) == '/') ? 1 : 0;

if (thisIsDir == isDir) {
if (isDir)
Expand Down
121 changes: 121 additions & 0 deletions tests/120_local-proxytrack-webdav-default.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/bin/bash
#
# A PROPFIND whose path is an exact cache entry enumerates that entry's default
# document under an empty name; proxytrack used to index one byte before it and
# die on the whole listener.

set -euo pipefail

: "${top_srcdir:=..}"
# shellcheck source=tests/testlib.sh
. "$top_srcdir/tests/testlib.sh"

python=$(find_python) || {
echo "python3 missing, skipping"
exit 77
}
command -v curl >/dev/null 2>&1 || {
echo "curl missing, skipping"
exit 77
}
# MSYS cannot reap a native listener, and the orphan wedges the suite (#595).
if is_windows; then
echo "windows: cannot reap a backgrounded proxytrack, skipping"
exit 77
fi

dir=$(mktemp -d)
ptpid=
cleanup() {
stop_server "$ptpid"
rm -rf "$dir"
}
trap 'set +e; cleanup' EXIT
trap 'set +e; cleanup; exit 1' INT TERM HUP

printf 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 5\r\n\r\n' >"$dir/hdr"
printf 'hello' >"$dir/body"
alen=$(($(wc -c <"$dir/hdr") + $(wc -c <"$dir/body")))
{
printf 'filedesc://t.arc 0.0.0.0 20250101000000 text/plain 200 - - 0 t.arc 9\n'
printf '2 0 test\n'
printf '\n\n'
printf 'http://example.com/page.html 0.0.0.0 20250101000000 text/html 200 - - 0 t.arc %d\n' "$alen"
cat "$dir/hdr" "$dir/body"
} >"$dir/in.arc"

freeport() {
"$python" -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1",0)); print(s.getsockname()[1]); s.close()'
}
proxyport=$(freeport)
icpport=$(freeport)

proxytrack "127.0.0.1:$proxyport" "127.0.0.1:$icpport" "$dir/in.arc" >"$dir/pt.log" 2>&1 &
ptpid=$!
waited=0
until grep -qE "HTTP Proxy installed on|Unable to (initialize a temporary server|create the server)" "$dir/pt.log"; do
kill -0 "$ptpid" 2>/dev/null || {
echo "FAIL: proxytrack exited before listening"
cat "$dir/pt.log"
exit 1
}
test "$waited" -lt 50 || {
echo "FAIL: proxytrack never announced its listen port"
exit 1
}
sleep 0.1
waited=$((waited + 1))
done
grep -q "HTTP Proxy installed on" "$dir/pt.log" || {
echo "FAIL: proxytrack failed to bind"
cat "$dir/pt.log"
exit 1
}

propfind() {
curl -s -i --max-time 30 -X PROPFIND -H "Depth: 1" \
"http://127.0.0.1:$proxyport/webdav/example.com/$1"
}

resp=$(propfind page.html) || {
echo "FAIL: PROPFIND on an exact cache entry did not answer"
cat "$dir/pt.log"
exit 1
}
grep -q '^HTTP/1\.[01] 207 ' <<<"$resp" || {
echo "FAIL: expected 207 Multi-Status"
printf '%s\n' "$resp"
exit 1
}
grep -q '<displayname>Default Document for the Folder</displayname>' <<<"$resp" || {
echo "FAIL: the default document of the entry was not listed"
printf '%s\n' "$resp"
exit 1
}
# The name alone would still pass on a truncated or garbage URL, so pin the
# href: the default document is the collection path plus an empty name.
grep -q '<href>/webdav/example\.com/page\.html/</href>' <<<"$resp" || {
echo "FAIL: the default document was listed under the wrong href"
printf '%s\n' "$resp"
exit 1
}
responses=$(grep -c '<response ' <<<"$resp" || true)
test "$responses" -eq 2 || {
echo "FAIL: expected the root and its default document, got $responses responses"
printf '%s\n' "$resp"
exit 1
}

# The crash killed the process, so a second request is the liveness proof.
resp=$(propfind "") || {
echo "FAIL: proxytrack died serving the previous PROPFIND"
cat "$dir/pt.log"
exit 1
}
grep -q '^HTTP/1\.[01] 207 ' <<<"$resp" || {
echo "FAIL: expected 207 Multi-Status on the follow-up listing"
printf '%s\n' "$resp"
exit 1
}

echo "OK: PROPFIND on an exact cache entry lists its default document and survives"
1 change: 1 addition & 0 deletions tests/tests-list.mk
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ TESTS += 96_local-refetch-keep.test
TESTS += 97_local-warc-update-keep.test
TESTS += 98_local-warc-segments.test
TESTS += 99_local-robots-error.test
TESTS += 120_local-proxytrack-webdav-default.test
TESTS += 107_local-bak-collision.test
TESTS += 108_engine-refetch-backup.test
TESTS += 122_local-warc-revisit-headers.test
Expand Down
Loading