be830879c66213de693050b5e8697f5826f51ad1
braney
  Wed Sep 2 10:56:33 2026 -0700
ts: serve each ticket sandbox over https as well as http, refs #37867

A parked instance answered only over plain http, so anything a CGI decides from
the request scheme could not be exercised in one at all.  Apache sets HTTPS=on
for a TLS request and cgiServerHttpsIsOn() reads it, so a CGI that branches on
it, such as one deciding whether to mark a cookie Secure, always took the same
branch in a park no matter what was being tested.

Each instance now listens twice: plain http on its registered port, as before,
and https on that port plus 1000, from a self-signed certificate generated once
and shared by every park on the account.  Both listeners serve the same frozen
code, so hitting the pair is the comparison.

Only the http port is in the registry and the https port is derived from it, so
nothing about the existing layout changes and "ts conf NNNNN" adds https to an
instance frozen before this.  http ports are now kept below the start of the
https range so the two cannot overlap.  "ts list" prints both, "ts tunnel"
forwards both, and "ts port NNNNN ssl" gives the https one on its own, which is
how the laptop wrapper asks, rather than repeating the offset in a second file.

diff --git src/utils/ts/ts.mac src/utils/ts/ts.mac
index d6cbcd84bb2..7fc188b5fb4 100755
--- src/utils/ts/ts.mac
+++ src/utils/ts/ts.mac
@@ -41,28 +41,38 @@
 esac
 
 # a bare ticket number is shorthand for "tunnel"
 if printf '%s' "$sub" | grep -qE '^[0-9]+$'; then
     set -- tunnel "$sub"
     sub=tunnel
 fi
 
 case "$sub" in
     tunnel)
         [ $# -eq 2 ] || usage
         tkt="$2"
         printf '%s' "$tkt" | grep -qE '^[0-9]+$' || { echo "ts: ticket must be numeric" >&2; exit 1; }
         port="$(ssh "$HGWDEV" "$REMOTE port $tkt")" || exit 1
         [ -n "$port" ] || { echo "ts: no port for ticket $tkt" >&2; exit 1; }
+        # Ask hgwdev for the https port rather than computing it here, so the
+        # offset lives in one place.  An hgwdev ts from before https says
+        # nothing, and the tunnel then carries http alone, as it always did.
+        sport="$(ssh "$HGWDEV" "$REMOTE port $tkt ssl" 2>/dev/null)" || sport=""
         echo "Tunnel: localhost:$port -> $HGWDEV  (ticket $tkt)"
         echo "Open:   http://localhost:$port/cgi-bin/hgTracks"
+        if [ -n "$sport" ]; then
+            echo "        https://localhost:$sport/cgi-bin/hgTracks   (self-signed, warns once)"
+        fi
         echo "Ctrl-C closes the tunnel."
+        if [ -n "$sport" ]; then
+            exec ssh -N -L "$port:localhost:$port" -L "$sport:localhost:$sport" "$HGWDEV"
+        fi
         exec ssh -N -L "$port:localhost:$port" "$HGWDEV"
         ;;
     list|create|sync|conf|start|stop|remove)
         # run the real ts on hgwdev; use a login shell so PATH has ss/httpd
         cmd="$REMOTE"
         for a in "$@"; do cmd="$cmd $(printf '%q' "$a")"; done
         exec ssh -t "$HGWDEV" "bash -lc $(printf '%q' "$cmd")"
         ;;
     *) usage;;
 esac