bevy icon indicating copy to clipboard operation
bevy copied to clipboard

Add WebSocket support for RemoteHttpPlugin

Open villor opened this issue 1 year ago • 4 comments
trafficstars

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 RemoteHttpPlugin using tungstenite. This way the connection uses the same endpoint (host/port) as the HTTP requests.
  • Requests are sent as Text messages 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 id field 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 http feature optional and only included when the feature is enabled.

Testing

  • Ran the server example and used a web based tool to test both complete and watched requests, as well as batched requests.
  • Ran the server/client examples 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-tungstenite with custom implementation. It is a tiny library, but adds a (non-optional) tokio dependency which should not be needed.
  • See if the uses of futures-util can be replaced with smol alternatives.

villor avatar Nov 16 '24 20:11 villor

You added a new feature but didn't add a description for it. Please update the root Cargo.toml file.

github-actions[bot] avatar Nov 16 '24 20:11 github-actions[bot]

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.

github-actions[bot] avatar Nov 17 '24 15:11 github-actions[bot]

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

mockersf avatar Dec 01 '24 18:12 mockersf

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!

villor avatar Dec 03 '24 16:12 villor

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!

jbuehler23 avatar Jun 18 '25 16:06 jbuehler23

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.

villor avatar Jun 21 '25 08:06 villor

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 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.

jbuehler23 avatar Jun 21 '25 09:06 jbuehler23