Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reverseproxy: Cleaner close of websocket connections (and SSE) #4762

Closed
mholt opened this issue May 5, 2022 · 7 comments · Fixed by #4895
Closed

reverseproxy: Cleaner close of websocket connections (and SSE) #4762

mholt opened this issue May 5, 2022 · 7 comments · Fixed by #4895
Assignees
Labels
discussion 💬 The right solution needs to be found feature ⚙️ New feature or request
Milestone

Comments

@mholt
Copy link
Member

mholt commented May 5, 2022

Based on info from gorilla/websocket#316 (comment), we can probably be closing web sockets more cleanly.

@mholt mholt added the help wanted 🆘 Extra attention is needed label May 5, 2022
@mholt mholt added this to the v2.5.2 milestone May 5, 2022
@WeidiDeng
Copy link
Member

I read reverse_proxy code and findout if response is 101, reverse_proxy will do a hijack and take it from there.. Per http.Server documentation, reverse_proxy should register a shutdown function. I know how to close websocket connection, but have no experience with sse or h2c.

Maybe reverse_proxy needs to keep track of hijacked connections and send a goaway frame to websocket at least. I don't know how other protocol does graceful shutdown. Protocol upgrade can be found in the upgrade header.

@francislavoie
Copy link
Member

Yeah exactly, that's my same reading of it, we need to keep track of websocket connections in its own list and send close frames. I'm not sure how best to write the close frame logic though so we could use some help to get that right.

We can do this via Cleanup() on the reverse proxy module, which is called when the config is being shut down or reloaded.

I also think we should consider adding a grace timeout config in the reverse proxy module itself to close off other hijacked connections that are of unknown type.

Maybe for SSE we can just always close it right away because it should rarely be harmful to do so, but it could hurt if we close other kinds of streams where the frontend/backend of the app doesn't have logic to recover gracefully.

@deepto98
Copy link

Can I pick this up?

@francislavoie
Copy link
Member

Sure, PRs always welcome!

@mholt
Copy link
Member Author

mholt commented Jun 20, 2022

Yeah, we'd love the assistance!

@francislavoie francislavoie modified the milestones: v2.5.2, v2.6.0 Jun 29, 2022
@mholt mholt self-assigned this Jul 14, 2022
@mholt
Copy link
Member Author

mholt commented Jul 14, 2022

I've made progress on this to the point where I think I have it working. I was not experiencing any hanging config reloads before my changes, so if anyone is having that with WebSockets, I'd like to know. What I did experience, however, was that WebSocket connections were not closed on config reloads, which I think they should be.

So I have implemented that Caddy will send its own Close control messages when a config reload or shutdown is taking place. In my testing, this cleanly closes the connections with the client and the backend, whereas before the connections would have an abnormal close on exit. (It is a best-effort attempt though, I don't wait for a returned Close frame...)

As an aside, grace_period seemed irrelevant in my testing. Probably because http.Server.Shutdown() doesn't do anything with hijacked connections.

@mholt mholt added discussion 💬 The right solution needs to be found feature ⚙️ New feature or request and removed help wanted 🆘 Extra attention is needed labels Jul 14, 2022
@mholt
Copy link
Member Author

mholt commented Jul 17, 2022

I've pushed my current efforts (which is the result of rewriting it about 3 times) to #4895. @WeidiDeng if you're interested, please feel free to try it out!

In my testing, the change successfully closes the connection that was left open before. It is closed between client and backend. For WebSocket connections specifically, the Close frame is now sent by Caddy to give the backend and the client and opportunity to close gracefully (code 1001 instead of 1006).

If interested parties could please try out my changes in staging and production, that would be great!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion 💬 The right solution needs to be found feature ⚙️ New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants