31f2090cd11876f61a92260838078077f68a94ee
chmalee
  Thu Sep 24 15:32:25 2026 -0700
Add a test program and wrapper for debugging hung https connections, refs #38366

diff --git src/lib/tests/httpsFerryLeakTest.sh src/lib/tests/httpsFerryLeakTest.sh
new file mode 100755
index 00000000000..b6650baa398
--- /dev/null
+++ src/lib/tests/httpsFerryLeakTest.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+# Run httpsFerryLeakTest in the background and report its thread count and
+# open socket count every few seconds until it exits.
+#
+# usage: httpsFerryLeakTest.sh [URL] [interval] [httpsFerryLeakTest options]
+#   httpsFerryLeakTest.sh
+#   httpsFerryLeakTest.sh 10 -close -sleep=120
+#   httpsFerryLeakTest.sh https://host/big.bb 10 -close
+#
+# URL defaults to a 1.4 GB vcf.gz on hgwdev. interval is seconds between
+# reports (default 15).
+#
+# httpsFerryLeakTest options:
+#   -readSize=N  bytes to read before the errAbort (default 4096)
+#   -sleep=N     seconds to sleep after the errCatch (default 180)
+#   -close       call udcFileClose before the errAbort. Without it the udc file
+#                is left open, and the https thread and its socket to the remote
+#                host stay alive for the whole sleep. With it they should go away
+#                right after the close.
+
+set -u
+
+if [ "$(hostname -s)" != "hgwdev" ]; then
+    echo "$0: only runs on hgwdev" >&2
+    exit 1
+fi
+
+url=https://hgwdev.gi.ucsc.edu/~chmalee/vcfExampleCTs/evaSnps.ucscChroms.vcf.gz
+if [ $# -gt 0 ] && [[ $1 == https://* ]]; then
+    url=$1
+    shift
+fi
+interval=15
+if [ $# -gt 0 ] && [[ $1 =~ ^[0-9]+$ ]]; then
+    interval=$1
+    shift
+fi
+
+prog=$(dirname "$0")/bin/$(uname -m)/httpsFerryLeakTest
+if [ ! -x "$prog" ]; then
+    echo "$prog not found, run: make httpsFerryLeakTest" >&2
+    exit 1
+fi
+
+"$prog" "$@" "$url" &
+pid=$!
+trap 'kill "$pid" 2>/dev/null; wait "$pid" 2>/dev/null; echo "killed pid $pid"; exit 130' INT TERM
+start=$(date +%s)
+echo "started pid $pid"
+
+while kill -0 "$pid" 2>/dev/null; do
+    now=$(( $(date +%s) - start ))
+    threads=$(ls /proc/$pid/task 2>/dev/null | wc -l)
+    sockets=$(ls -l /proc/$pid/fd 2>/dev/null | grep -c socket)
+    echo "=== t=${now}s threads=$threads sockets=$sockets"
+    sleep "$interval"
+done
+
+wait "$pid"
+echo "pid $pid exited with status $?"