bevy
bevy copied to clipboard
Add WebSocket support for RemoteHttpPlugin
Objective
BRP currently has a HTTP transport implementation, including watching requests using SSE (server-sent events).
While SSE can be great, especially when combined with HTTP/2, it has a significant limitation when used with HTTP/1.1. The browser will limit the amount of concurrent requests to a very low number.
There are other nice options today like WebTransport, but all of them (AFAIK) suffer from being limited to secure (HTTPS) connections only. Same thing applies to SSE over HTTP/2.
To keep the developer-friendly experience of not needing a certificate, while also allowing more concurrent watches, WebSocket seems like the only viable option.
Solution
- Implemented WebSocket upgrades for
RemoteHttpPluginusingtungstenite. This way the connection uses the same endpoint (host/port) as the HTTP requests. - Requests are sent as
Textmessages serialized to JSON, following the JSON RPC 2.0 format of BRP. - Responses are sent back in undefined order. The client is responsible for keeping track by using the
idfield in JSON RPC 2. - Batched requests work the same as with HTTP. An array can be sent in, and an array with all the responses will be sent back once they are all processed.
- Watchers will keep sending responses with a matching ID.
- Limitation: There is no way to cancel a watching request. With SSE this was not a problem since closing the connection worked. For a continous streaming transport like WebSocket, we need a way to unwatch requests. This is handled in #16407
- Feature flagged it behind
remote_websocket(non-default) - Extra: Made the dependencies used by the
httpfeature optional and only included when the feature is enabled.
Testing
- Ran the
serverexample and used a web based tool to test both complete and watched requests, as well as batched requests. - Ran the
server/clientexamples to ensure HTTP still works.
To test it
Run the server example with WebSocket enabled:
cargo run --example server --features remote_websocket
Follow-up
Reduce dependency footprint, potentially by:
- Replacing
hyper-tungstenitewith custom implementation. It is a tiny library, but adds a (non-optional)tokiodependency which should not be needed. - See if the uses of
futures-utilcan be replaced withsmolalternatives.
You added a new feature but didn't add a description for it. Please update the root Cargo.toml file.
You added a new feature but didn't update the readme. Please run cargo run -p build-templated-pages -- update features to update it, and commit the file change.
this is a very cool feature, but the more I think about it the more I would prefer it to be a third party crate that would add different protocols: websocket, mqtt, reversed connection, ...
Bevy can't support them all builtin, it makes more sense to have a basic implementation builtin, and ensure other protocols are easy to add
this is a very cool feature, but the more I think about it the more I would prefer it to be a third party crate that would add different protocols: websocket, mqtt, reversed connection, ...
Bevy can't support them all builtin, it makes more sense to have a basic implementation builtin, and ensure other protocols are easy to add
While I do agree that most transports should live in third party crates, I think there should be at least one well supported transport built in. For web browser-based tooling there are only two non-HTTPS options (as far as I can tell): HTTP and WebSocket. We have HTTP, but the connection limitation for watching requests makes it really clunky...
I really like the idea of being able to just "turn on BRP" to debug any Bevy app, and not care about whether a third party crate is compatible with the Bevy version I'm working with (could be stable, could be main, could be work on a new PR), or what the web based tool I'm using requires. The point of a built-in remote inspection/debug protocol is to not have to pull in extra crates etc IMO.
I guess one alternative could be to re-think the SSE +watch-requests a bit. Instead of one connection per request, there could be one connection to receive any +watch responses. And regular HTTP methods to subscribe/unsubscribe to them.
Another one is to simply not do web based tools. Desktop apps shouldn't have the same limitation... But it's really nice to be able to just open a web page to start inspecting 🔥 especially on mobile/tablet on the same network.
The ideal (for me) would be to be able to run any Bevy example (without installing any third-party crates that might work with the version I'm on) with BRP enabled:
cargo run --example ui --features=remote
And then being able to just open a tool I didn't have to install to debug/inspect it!
I personally think HTTP and websocket requests should be supported out of the box IMO, as streaming realtime updates to the editor via BRP over websocket would be incredibly useful!
I personally think HTTP and websocket requests should be supported out of the box IMO, as streaming realtime updates to the editor via BRP over websocket would be incredibly useful!
Just to clarify, (uni-directional) streaming is already possible with current BRP. The +watch requests will respond with an SSE (server-sent events) stream that will continuously push messages to the client.
I believe the main motivations for a WebSocket transport would be:
- Bi-directional streaming, which I’m not completely sure is needed as HTTP is pretty good at reusing connections these days so regular requests might suffice for client-> server messages
- Getting around the “number of connections limit” that web browsers have for HTTP/1.1, making web based BRP tools a bit limited. However, this could also be mitigated by some protocol re-design, for example by having a single endpoint to listen to all current watchers.
I personally think HTTP and websocket requests should be supported out of the box IMO, as streaming realtime updates to the editor via BRP over websocket would be incredibly useful!
Just to clarify, (uni-directional) streaming is already possible with current BRP. The
+watchrequests will respond with an SSE (server-sent events) stream that will continuously push messages to the client.I believe the main motivations for a WebSocket transport would be:
- Bi-directional streaming, which I’m not completely sure is needed as HTTP is pretty good at reusing connections these days so regular requests might suffice for client-> server messages
- Getting around the “number of connections limit” that web browsers have for HTTP/1.1, making web based BRP tools a bit limited. However, this could also be mitigated by some protocol re-design, for example by having a single endpoint to listen to all current watchers.
I do agree with this about perhaps not needing websockets, however I’m thinking from the editor standpoint there will be frequent updates and edits to the running application. But then, I suppose you could make the argument whether or not the right thing to do is modify via web interface in the first place.