mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-16 00:00:07 +01:00
193 lines
6.5 KiB
Markdown
193 lines
6.5 KiB
Markdown
![]() |
# lws-meta protocol
|
||
|
|
||
|
lws-meta is a lightweight ws subprotocol that accepts other ws connections
|
||
|
to the same server inside it and multiplexes their access to the connection.
|
||
|
|
||
|
```
|
||
|
Client Server
|
||
|
|
||
|
conn1: \ / :conn1
|
||
|
conn2: = mux ------ lws-meta ws protocol ----- mux = :conn2
|
||
|
conn3: / \ :conn3
|
||
|
```
|
||
|
|
||
|
You may have n client ws connections back to the server, but you now
|
||
|
only have one tcp connection (and one SSL wrapper if using SSL) instead
|
||
|
of n of those.
|
||
|
|
||
|
If you currently make multiple ws connections back to the server, so you
|
||
|
can have different protocols active in one webpage, this if for you.
|
||
|
|
||
|
- The subprotocol code for the connections inside a lws-meta connection
|
||
|
need zero changes from being a normal ws connection. It is unaware
|
||
|
it is inside an lws-meta parent connection.
|
||
|
|
||
|
- The traffic on the lws-meta connection is indistinguishable from
|
||
|
standard ws traffic, so intermediaries won't object to it
|
||
|
|
||
|
- The multiplexing is done in the protocol, **not by an extension**. So
|
||
|
it's compatible with all browsers.
|
||
|
|
||
|
- Javascript helper code is provided to very simply use lws-meta
|
||
|
protocol instead of direct connections. The lws test server has
|
||
|
been converted to use this by default.
|
||
|
|
||
|
# Converting your server
|
||
|
|
||
|
1) include the provided lws-meta plugin (plugins/protocl_lws_meta.c) as an
|
||
|
active protocol for your server. You can do that using runtime plugins, or
|
||
|
include the plugin sources into your server at build-time. The lws test
|
||
|
server uses the latter approach.
|
||
|
|
||
|
That's all you need to do on the server side.
|
||
|
|
||
|
# Converting your browser JS
|
||
|
|
||
|
1) import lws-common.js
|
||
|
|
||
|
2) Instantiate a parent lws-meta connection object
|
||
|
|
||
|
```
|
||
|
var lws_meta = new lws_meta_ws();
|
||
|
```
|
||
|
|
||
|
3) Connect the lws-meta object to your server
|
||
|
|
||
|
```
|
||
|
lws_meta.new_parent(get_appropriate_ws_url("?mirror=" + mirror_name));
|
||
|
```
|
||
|
|
||
|
4) Convert your actual ws connections to go via the lws_meta object
|
||
|
|
||
|
```
|
||
|
var my_ws = lws_meta.new_ws("", "dumb-increment-protocol");
|
||
|
```
|
||
|
|
||
|
The first arg is the URL path, the second arg is the ws protocol you want.
|
||
|
|
||
|
That's it. my_ws will get `onopen()`, `onmessage()` etc calls as before.
|
||
|
|
||
|
# lws-meta wire protocol
|
||
|
|
||
|
lws-meta works by adding some bytes at the start of a message indicating
|
||
|
which channel the message applies to.
|
||
|
|
||
|
Channel messages are atomic on the wire. The reason is if we tried to
|
||
|
intersperse other channel fragments between one channels message fragments,
|
||
|
an intermediary would observe violations of the ws framing rule about
|
||
|
having to start a message with TEXT or BINARY, and use only CONTINUATION
|
||
|
for the subsequent fragments. Eg
|
||
|
|
||
|
```
|
||
|
[ ch1 TEXT NOFIN ] [ ch2 BINARY FIN ] [ ch1 CONTINUATION FIN ]
|
||
|
```
|
||
|
|
||
|
is illegal to an observer that doesn't understand lws-meta headers in the
|
||
|
packet payloads. So to avoid this situation, only complete messages may
|
||
|
be sent from one subchannel in each direction at a time.
|
||
|
|
||
|
Consequently, only the first fragment of each message is modified to
|
||
|
have the extra two bytes identifying the subchannel it is aimed at, since
|
||
|
the rest of the message from the same subchannel is defined to follow.
|
||
|
|
||
|
If it makes latencies, modify the protocol sending large messages to
|
||
|
send smaller messages, so the transmission of messages from other channels
|
||
|
can be sent inbetween the smaller messages.
|
||
|
|
||
|
## lws-meta commands
|
||
|
|
||
|
1) CSTRING indicates a string terminated by 0x00 byte
|
||
|
|
||
|
2) Channel IDs are sent with 0x20 added to them, to guarantee valid UTF-8
|
||
|
|
||
|
### 0x41: RX: LWS_META_CMD_OPEN_SUBCHANNEL
|
||
|
|
||
|
- CSTRING: protocol name
|
||
|
- CSTRING: url
|
||
|
- CSTRING: cookie (7 bytes max)
|
||
|
|
||
|
Client is requesting to open a new channel with the given protocol name,
|
||
|
at the given url. The cookie (eg, channel name) is only used in
|
||
|
LWS_META_CMD_OPEN_RESULT, when the channel id is assigned, so it is
|
||
|
applied to the right channel.
|
||
|
|
||
|
### 0x42: TX: LWS_META_CMD_OPEN_RESULT
|
||
|
|
||
|
- CSTRING cookie
|
||
|
- BYTE channel id (0 indicates failed)
|
||
|
- CSTRING: selected protocol name
|
||
|
|
||
|
The server is informing the client of the results of a previous
|
||
|
open request. The cookie the client sent to identify the request
|
||
|
is returned along with a channel id to be used subsequently. If
|
||
|
the channel ID is 0 (after subtracting the transport offset of
|
||
|
0x20) then the open request has failed.
|
||
|
|
||
|
### 0x43: TX: LWS_META_CMD_CLOSE_NOTIFY
|
||
|
|
||
|
- BYTE channel id
|
||
|
- BYTE: payload length + 0x20
|
||
|
- BYTE: close code MSB
|
||
|
- BYTE: close code LSB
|
||
|
- PAYLOAD: payload (< 123 bytes)
|
||
|
|
||
|
Server notifies the client that a child has closed, for whatever reason.
|
||
|
|
||
|
### 0x44: RX: LWS_META_CMD_CLOSE_RQ
|
||
|
- BYTE: channel id
|
||
|
- BYTE: payload length + 0x20
|
||
|
- BYTE: close code MSB
|
||
|
- BYTE: close code LSB
|
||
|
- PAYLOAD: payload (< 123 bytes)
|
||
|
|
||
|
The client requests to close a child connection
|
||
|
|
||
|
### 0x45: TX: LWS_META_CMD_WRITE
|
||
|
|
||
|
- BYTE: channel id
|
||
|
|
||
|
Normal write of payload n from lws-meta perspective is actually
|
||
|
LWS_META_CMD_WRITE, channel id, then (n - 2) bytes of payload
|
||
|
|
||
|
The command only appears at the start of a message, continuations do
|
||
|
not have the command.
|
||
|
|
||
|
## Protocol Notes
|
||
|
|
||
|
- Once the subchannel is up, overhead is only +2 bytes per message
|
||
|
|
||
|
- Close reasons are supported in both directions
|
||
|
|
||
|
- Ping and Pong are only supported at the lws-meta level, using normal ws ping and pong packets.
|
||
|
|
||
|
- Only the final close of the tcp lws-meta connection itself goes out as
|
||
|
a normal ws close frame. Subchannels close is done in a normal TEXT
|
||
|
message using LWS_META_CMD_CLOSE_RQ and then the close packet payload.
|
||
|
This is so intermediaries do not mistake subchannel closures for the
|
||
|
tcp / ws link going down.
|
||
|
|
||
|
Messages that start with LWS_META_CMD_OPEN_SUBCHANNEL only contain those
|
||
|
commands but may contain any number of them for the whole duration of the
|
||
|
message. The lws-meta js support collects child open requests made before
|
||
|
the parent lws-meta connection is open, and dumps them all in a single
|
||
|
message when it does open.
|
||
|
|
||
|
Messages that start with LWS_META_CMD_OPEN_RESULT or LWS_META_CMD_CLOSE_NOTIFY
|
||
|
only contain those two commands, but they may contain any number of them
|
||
|
for the whole duration of the message.
|
||
|
|
||
|
|
||
|
# Current Implemention Limitations
|
||
|
|
||
|
- only server side is supported in lws. The client side JS for
|
||
|
a browser is supported.
|
||
|
|
||
|
- max number of child connections per parent at the moment is 8
|
||
|
|
||
|
- child connection URL paramter when opening the connection is
|
||
|
ignored
|
||
|
|
||
|
- there is no ah attached when the child connections are
|
||
|
established inside the lws-meta parent. So header access
|
||
|
functions will fail.
|