Skip to content

Fix websocket "can't switch protocols" error#16

Open
steffenbusch wants to merge 6 commits into
sebdroid:mainfrom
steffenbusch:hijack
Open

Fix websocket "can't switch protocols" error#16
steffenbusch wants to merge 6 commits into
sebdroid:mainfrom
steffenbusch:hijack

Conversation

@steffenbusch
Copy link
Copy Markdown

@steffenbusch steffenbusch commented May 19, 2026

Summary

Fix websocket support when cookiecrypt is enabled for a route by updating the response wrapper so Caddy can traverse wrapper layers and preserve protocol upgrade behavior.

What changed

  • Added proper http.Hijacker delegation to cookieInterceptResponseWriter.
  • Added Unwrap() support so http.NewResponseController can traverse nested Caddy wrappers.
  • Preserved the existing http.Flusher and io.ReaderFrom behavior by forwarding those interfaces from the wrapped writer.
  • Previously, cookieInterceptResponseWriter wrapped the underlying ResponseWriter without exposing the underlying wrapper chain, which broke websocket protocol upgrades.
  • This produced the reverse proxy error:
ERROR   http.handlers.reverse_proxy     can't switch protocols using non-Hijacker ResponseWriter        {"type": "*cookiecrypt.cookieInterceptResponseWriter"}

Result

  • Websocket upgrade requests now succeed again when cookiecrypt is enabled.
  • Optional ResponseWriter interfaces needed for upgrades are now preserved transparently through wrapper layers.

AI-Disclosure

The implementation approach was identified with the help of ChatGPT during troubleshooting. GitHub Copilot in VS Code was used to assist with implementing the required interface methods.

The initial implementation was based on plain Caddy configuration and appeared to work, but today’s troubleshooting session with GitHub Copilot in VS Code uncovered a deeper issue with more complex wrapper chains. GitHub Copilot (Raptor mini Preview) assisted in diagnosing the response writer chain, adding Unwrap() / Hijack() delegation, and cleaning up the middleware changes so the fix now works correctly for more complex Caddy setups.

@steffenbusch steffenbusch marked this pull request as draft May 20, 2026 11:33
steffenbusch and others added 4 commits May 20, 2026 17:09
…tailed logging and response writer chain tracking
### Summary
Fix websocket support when `cookiecrypt` is enabled for a route by updating the response wrapper so Caddy can traverse wrapper layers and preserve protocol upgrade behavior.

### What changed
- Added proper `http.Hijacker` delegation to `cookieInterceptResponseWriter`.
- Added `Unwrap()` support so `http.NewResponseController` can traverse nested Caddy wrappers.
- Preserved the existing `http.Flusher` and `io.ReaderFrom` behavior by forwarding those interfaces from the wrapped writer.
- Previously, `cookieInterceptResponseWriter` wrapped the underlying `ResponseWriter` without exposing the underlying wrapper chain, which broke websocket protocol upgrades.
- This produced the reverse proxy error:

```
ERROR   http.handlers.reverse_proxy     can't switch protocols using non-Hijacker ResponseWriter        {"type": "*cookiecrypt.cookieInterceptResponseWriter"}
```

### Result
- Websocket upgrade requests now succeed again when `cookiecrypt` is enabled.
- Optional `ResponseWriter` interfaces needed for upgrades are now preserved transparently through wrapper layers.

### AI-Disclosure
The implementation approach was identified with the help of ChatGPT during troubleshooting. GitHub Copilot in VS Code was used to assist with implementing the required interface methods.

The initial implementation was based on plain Caddy configuration and appeared to work, but today’s troubleshooting session with GitHub Copilot in VS Code uncovered a deeper issue with more complex wrapper chains. GitHub Copilot (Raptor mini Preview) assisted in diagnosing the response writer chain, adding `Unwrap()` / `Hijack()` delegation, and cleaning up the middleware changes so the fix now works correctly for more complex Caddy setups.
@steffenbusch steffenbusch marked this pull request as ready for review May 20, 2026 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant