Summary
If every listener fails to bind (e.g. bad --listen-tcp address plus missing parent directory for --listen-socket), the process ends up serving nothing, but neither implementation signals failure usefully:
- Rust: both listener tasks log an error and return;
main exits with code 0. Under systemd Restart=on-failure, nothing restarts a proxy that serves nothing.
- Go:
startListener logs and returns for each failed listener, but main then blocks on <-ctx.Done() forever (go/main.go:62) — the process looks alive to the supervisor while serving nothing.
Per-listener independent failure is intentional (one listener failing shouldn't kill the other — matches both implementations). The bug is only the all-listeners-failed terminal state.
Affected implementation(s)
Expected behavior
If zero listeners bound successfully, exit promptly with a non-zero status so a supervisor (Restart=on-failure) restarts it and operators see a failed unit instead of a healthy-looking zombie.
Suggested fix
Have each listener task report whether it bound successfully; after startup (or after all tasks finish), if none bound, log an error and exit(1). Same pattern in both Go and Rust.
Context
Split out from the review follow-ups on PR #27. Related: #25.
Summary
If every listener fails to bind (e.g. bad
--listen-tcpaddress plus missing parent directory for--listen-socket), the process ends up serving nothing, but neither implementation signals failure usefully:mainexits with code 0. Under systemdRestart=on-failure, nothing restarts a proxy that serves nothing.startListenerlogs and returns for each failed listener, butmainthen blocks on<-ctx.Done()forever (go/main.go:62) — the process looks alive to the supervisor while serving nothing.Per-listener independent failure is intentional (one listener failing shouldn't kill the other — matches both implementations). The bug is only the all-listeners-failed terminal state.
Affected implementation(s)
Expected behavior
If zero listeners bound successfully, exit promptly with a non-zero status so a supervisor (
Restart=on-failure) restarts it and operators see a failed unit instead of a healthy-looking zombie.Suggested fix
Have each listener task report whether it bound successfully; after startup (or after all tasks finish), if none bound, log an error and
exit(1). Same pattern in both Go and Rust.Context
Split out from the review follow-ups on PR #27. Related: #25.