Detect 101 Switching Protocols in relay_response() and switch to raw
bidirectional TCP relay instead of re-entering the HTTP parsing loop.
Previously, is_bodiless_response() treated 101 as a generic 1xx
informational response, forwarding only the headers and returning to
the HTTP parsing loop. After a 101, subsequent bytes are upgraded
protocol frames (e.g. WebSocket), not HTTP — causing the relay to
block or silently drop all post-upgrade traffic.
Changes:
- Add RelayOutcome enum (Reusable/Consumed/Upgraded) replacing bool
return type across L7Provider::relay trait and all relay functions
- Detect 101 before generic 1xx handler in relay_response(), capture
overflow bytes, return RelayOutcome::Upgraded
- Validate client sent Upgrade + Connection: Upgrade headers before
accepting 101 (rejects unsolicited upgrades from non-compliant
upstream servers)
- Extract shared handle_upgrade() helper used by both relay_rest()
and relay_passthrough_with_credentials()
- Add l7_decision=allow_upgrade audit log annotation for upgrades
- Add unit tests for 101 overflow capture, unsolicited 101 rejection,
and client_requested_upgrade header validation
- Add integration test: WebSocket echo through L7Provider::relay
Fixes: #652
Co-authored-by: John Myers <johntmyers@users.noreply.github.com>