-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Implement HTTP::WebSocket#on_{ping,pong} handler #3680
Conversation
Wow, that was fast! |
I do think that it would be a good idea to automatically respond with a pong for every ping received. Just about every web socket implementation I've seen does this by default, and I think its part of the web socket spec -- users probably shouldn't have to worry about responding to pings in their application code. |
@andrewhamon Sending |
@Sija yes, but the same WebSocket class is used for both clients and servers if I'm not mistaken. Furthermore, RFC 6455 clearly states that a ping must trigger a pong in response:
The MDN resource you linked also states:
This is also how its done in the Gorilla WebSocket library for Go:
|
The implementation could be as simple as: def initialize(@ws : Protocol)
@buffer = Slice(UInt8).new(4096)
@current_message = IO::Memory.new
on_ping do |msg|
pong(msg)
end
end which would allow the user to easily override the ping handler themselves if they wanted. |
@andrewhamon Sure, it totally makes sense, I'd just want an opinion on that from @asterite or someone from @crystal-lang team :) |
Yes, this is a required protocol detail that can be automated. Please send a PONG immediately when receiving a PING, unless a CLOSE frame was already received. As per the spec. |
@ysbaddaden ATM |
I agree with @ysbaddaden, I'm not sure we need |
@asterite
Is some pseudocode for disconnecting if a peer is unresponsive for 5 seconds. |
@andrewhamon Ah, right |
An alternative would be to make ws.ping do
# Execute block when the next pong comes back
end Which I actually think I like a bit better, since a pong shouldn't be showing up anyways unless there is a ping that triggered it. |
@asterite In Ruby world em-websocket for instance exposes |
Maybe we can expose them, but according to the protocol an incoming PING must always send a PONG, so the ping handler should be able to do something in addition to the default behaviour (that is, not overwrite it). Sounds good? |
I'd go with overwrite, and provide a very straightforward way to |
Keeping it SimpleI think that the faye-websocket-ruby interface does a good job providing a minimal interface that supports most use cases. Theres no This is a very safe path and would make it really hard for applications to do anything in violation of the protocol, but is still flexible enough for most things. Allowing more controlOTOH, this is the standard library and the WebSocket class should perhaps be thought of more as a building block, in which case we should allow more. For example:
Which would necessitate an exposed We also don't expose binary messages. Even if we do expose everything, though, the RFC is very clear:
Which makes me unsure whether applications should be able to override the default ping handler and potentially break compliance or provide a ping handler in addition to the default. |
I monkey-patched these changes into an app and can confirm that they are working as expected for my use case (heroku router keepalive). Thanks @Sija! |
I'd go with current design + simple overridable I'm not sure though how to proceed with tracking closed state since neither class does it ( |
So I'm reading the RFC and I assume you are talking about this:
Given this
I think its O.K. to not worry about it, as long as the close frame is sent in a timely manner. |
tl;dr the RFC doesn't compel implementations to not send a pong after receiving a close, so I don't think the close state needs to be tracked. In theory this could result in a surprising IO error if a peer:
Which I think would be out of spec for the peer. Furthermore, the run loop executes the callbacks in the same Fiber it seems, so unless the callbacks themselves spawn a Fiber, the only way a pong will go out after a close would be if the peer sent a ping after a close. |
@Sija This PR is almost perfect. The only thing missing is always sending a PONG in reply to a PING. This must be hardcoded and always done, because it's a protocol requirement. A user can additionally specify a PING handler to do some extra stuff, in addition to the default PONG reply (though I don't know what's a use case for that, but I'm OK with having that option) |
And if the protocol requires sending a CLOSE frame in reply to CLOSE, then I'd do that too (only for the first CLOSE frame, so we'd need to track that with a bool) |
I'm not sure that I like silently returning false if closed. Especially since it's not being concistently applied for all message types. Also, as I said earlier, I don't think it's necessary to track closed at all. Either the underlying IO is still open, and messages are permitted by the RFC (even after receiving a close frame), or the IO is closed and an IO error will be raised. If you insist on tracking closed, sending a message of any type should raise an exception. Otherwise applications will have a mix of checking for an error return (false) AND exception handeling (since they should be prepared for an IO error anyways) |
@andrewhamon uhm, I had that funny feeling about just returning |
Can you add some docs to the new methods while you're at it? 😊 |
ec18350
to
1418ac0
Compare
@andrewhamon I've pushed the updates. |
@ws.stream(binary: binary, frame_size: frame_size) do |io| | ||
yield io | ||
end | ||
end | ||
|
||
def close(message = nil) | ||
check_open |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Double closing a websocket shouldn't error. This is how every IO works right now and we prefer this behaviour as no bug can result from closing a closed socket. So this should return if @closed
as the first thing.
@Sija I just made a final tiny comment about double closing. After that, I'll merge this so it's available in the next version |
@asterite done! |
Great, thank you! ❤️ |
Fixes #3677