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
@@ -1,68 +1,78 @@
 #!/bin/bash
 #
 # ts (laptop side) - reach and drive UCSC "ticket sandbox" browsers parked on
 # hgwdev.  refs #37867
 #
 # The real tool lives on hgwdev at ~/ticketSandboxes/bin/ts and does the freezing
 # and httpd management there.  This thin wrapper, meant for your Mac, runs those
 # subcommands over ssh and opens the local ssh -L tunnel so you can point a
 # browser at a parked instance.
 #
 #   ts NNNNN                 open a tunnel to parked ticket NNNNN, print its URL
 #   ts tunnel NNNNN          same
 #   ts list                  list parked tickets on hgwdev
 #   ts create NNNNN [note]   run remotely on hgwdev (freeze + start)
 #   ts sync|conf|start|stop|remove NNNNN
 #
 # Set HGWDEV to override the host (default hgwdev.gi.ucsc.edu); e.g. if you have
 # a "Host hgwdev" block in ~/.ssh/config, run:  HGWDEV=hgwdev ts list
 # Set TS_REMOTE to override the path of the real ts on hgwdev (default
 # ticketSandboxes/bin/ts, relative to your hgwdev home).
 #
 set -eu
 
 HGWDEV="${HGWDEV:-hgwdev.gi.ucsc.edu}"
 REMOTE="${TS_REMOTE:-ticketSandboxes/bin/ts}"   # path under your hgwdev home
 
 # tunnel makes two ssh connections (port lookup, then the tunnel).  With key
 # auth set up neither prompts; do NOT use ControlMaster/ControlPersist here, its
 # backgrounded master swallows the Ctrl-C that should close the foreground tunnel.
 
 usage() {
     sed -n '11,20p' "$0" | sed 's/^#\{0,1\} \{0,1\}//'
     exit 1
 }
 
 [ $# -ge 1 ] || usage
 sub="$1"
 
 case "$sub" in
     -h|--help|help) usage;;
 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