- connEntry.isIdle(now, threshold): caller supplies now instead of time.Since
- connRegistry.closeIdle(now, threshold): passes now through to isIdle
- trackingConn gains a clock func() time.Time field used in Read/Write
- handleConn takes clock func() time.Time; uses it to init lastRead/lastWrite
and passes it to trackingConn
- Call sites in main pass time.Now or time.Now() explicitly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
On SIGINT/SIGTERM:
- Stop accepting new connections
- Close connections idle longer than --idle-timeout (default 5s),
determined by LastRead/LastWrite timestamps tracked per connection pair
- Wait for active connections to drain up to --shutdown-timeout (default 30s)
- Force-close any remaining connections if the timeout is exceeded
Also switches isClosedConn to use errors.Is(err, net.ErrClosed) and
exits the accept loop cleanly when a listener is closed during shutdown.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Accept any number of local-port:remote-host:remote-port forwards as
positional arguments. Backward-compatible with existing --port/--target
flags. Adds --version/-V/version and --help/help handling matching the
auth-proxy pattern, including printVersion printed to stderr at startup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>