shotover / shotover-proxy

L7 data-layer proxy
https://docs.shotover.io
Apache License 2.0
87 stars 18 forks source link

Support websocket based connections for edge/browser compatibility #994

Open benbromhead opened 1 year ago

benbromhead commented 1 year ago

Is your feature request related to a problem? Please describe.

Code running in a WASM / Browser context generally doesn't have access to tcp sockets and the only networking available to them is via web sockets

Describe the solution you'd like

Supporting C*, Redis and Kafka protocols over web socket

Issues

This will need some support from the drivers in the js / ts / wasm community. We might need to show examples of how to use those drivers over websockets / or submit PRs

conorbros commented 1 year ago

WebSocket frames are as follows. In the handshake the client can specify a sub-protocol to use (eg. "cql", "redis") and if the server supports it will include it in it's response header

      0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
     |                     Payload Data continued ...                |
     +---------------------------------------------------------------+

Data frames can be sent with just binary in the payload, the client can send the inner protocol frames encoded this way.

Rust websocket support:

libraries:

tokio-tungstenite::WebSocketStream does not implement tokio::io::AsyncRead/tokio::io::AsyncWrite so it cannot be dropped in place of tokio::net::TcpStream:

let ws_stream = accept_ws_connection(stream).await;
let (rx, tx) = tokio::io::split(ws_stream);
spawn_read_write_tasks(
    self.codec.clone(),
    rx,
    tx,
    in_tx,
    out_rx,
    out_tx.clone(),
    terminate_rx,
);

Will probably need to modify parts of tungstenite-rs/tokio-tungstenite to implement AsyncRead/AsyncWrite. There is also the note that we only want the binary part of the message from the WebSocket frame payload. Ideally we would have a wrapper around parts of the tungstenite code that pulls out the inner protocol binary so we can feed it straight into the Shotover codecs.

Client support

Scylla developed a small MVP in Typescript for using CQL over WebSockets: https://github.com/dfilimonow/CQL-Driver

Shotover configuration

This is a bit premature given the work that needs to be done to tungstenite to get WebSocket support into Shotover but the simplest way to do this would be to add a field to the source config to enable WebSockets and disable standard TCP connections, rather than listen on different ports for standard TCP and WebSocket connections.