|
2 | 2 |
|
3 | 3 | Extreme portability is a key design goal of libchan.
|
4 | 4 |
|
5 |
| -This document specifies the libchan protocol to allow multiple implementations to co-exist with |
6 |
| -full interoperability. |
| 5 | +This document specifies the libchan protocol to allow multiple implementations |
| 6 | +to co-exist with full interoperability. |
7 | 7 |
|
8 | 8 | ## Version
|
9 | 9 |
|
10 |
| -No version yet. |
| 10 | +0.2.0 |
11 | 11 |
|
12 |
| -## Author |
| 12 | +## Authors |
13 | 13 |
|
14 | 14 |
|
| 15 | + |
| 16 | +Matteo Collina < [email protected]> |
15 | 17 |
|
16 | 18 | ## Status
|
17 | 19 |
|
18 |
| -This specification is still work in progress. Things will change, probably in reverse-incompatible ways. |
19 |
| -We hope to reach full API stability soon. |
| 20 | +This specification is nearing a stable release. The protocol still may change in |
| 21 | +reverse-incompatible ways. |
20 | 22 |
|
21 | 23 | ## Terminology
|
22 | 24 |
|
23 | 25 | ### Channel
|
24 | 26 |
|
25 |
| -A `channel` is an object which allows 2 concurrent programs to communicate with each other. The semantics |
26 |
| -of a libchan channel are very similar (but not identical) to those of Go's native channels. |
| 27 | +A `channel` is an object which allows 2 concurrent programs to communicate with |
| 28 | +each other. The semantics of a libchan channel are very similar (but not |
| 29 | +identical) to those of Go's native channels. A channel may be used |
| 30 | +synchronously, but do not support synchronization primitives such as Go's |
| 31 | +channel select semantics. |
27 | 32 |
|
28 |
| -A channel has 2 ends: a `Sender` end and a `Receiver` end. The Sender can send messages and close the channel. |
29 |
| -The Receiver can receive messages. Messages arrive in the same order they were sent. |
| 33 | +A channel has 2 ends: a `Sender` end and a `Receiver` end. The Sender can send |
| 34 | +messages and close the channel. The Receiver can receive messages. Messages |
| 35 | +arrive in the same order they were sent. |
30 | 36 |
|
31 |
| -A channel is uni-directional: messages can only flow in one direction. So channels are more similar to pipes |
32 |
| -than to sockets. |
| 37 | +A channel is uni-directional: messages can only flow in one direction. So |
| 38 | +channels are more similar to pipes than to sockets. |
33 | 39 |
|
34 | 40 | ### Message
|
35 | 41 |
|
36 |
| -A message is a discrete packet of data which can be sent on a channel. Messages are structured into multiple |
37 |
| -fields. The protocol defines which data types can be carried by a message, and how transports should encode and |
38 |
| -decode them. |
| 42 | +A message is a discrete packet of data which can be sent on a channel. The |
| 43 | +protocol defines which data types can be carried sent as a message, and how |
| 44 | +transports should encode and decode them. A message is similar to a JSON object |
| 45 | +containing [custom types](#custom-types) to represent channels and byte streams. |
39 | 46 |
|
40 | 47 | ### Byte stream
|
41 | 48 |
|
42 |
| -A byte stream is an object which implements raw IO with `Read`, `Write` and `Close` methods. |
43 |
| -Typical byte streams are text files, network sockets, memory buffers, pipes, and so on. |
| 49 | +A byte stream is an object which implements raw IO with `Read`, `Write` and |
| 50 | +`Close` methods. Typical byte streams are text files, network sockets, memory |
| 51 | +buffers, pipes, and so on. A byte stream may either be read only, write only, or |
| 52 | +full duplex. |
44 | 53 |
|
45 |
| -One distinct characteristic of libchan is that it can encode byte streams as first class fields |
46 |
| -in a message, alongside more basic types like integers or strings. |
| 54 | +One distinct characteristic of libchan is that it can encode byte streams as |
| 55 | +first class fields in a message, alongside more basic types like integers or |
| 56 | +strings. |
47 | 57 |
|
48 | 58 | ### Nesting
|
49 | 59 |
|
50 |
| -Libchan supports nesting. This means that a libchan message can include a channel, which itself |
51 |
| -can be used to send and receive messages, and so on all the way down. |
| 60 | +Libchan supports nesting. This means that a libchan message can include a |
| 61 | +channel, which itself can be used to send and receive messages, and so on all |
| 62 | +the way down. |
52 | 63 |
|
53 | 64 | Nesting is a fundamental property of libchan.
|
54 | 65 |
|
55 | 66 | ## Underlying transport
|
56 | 67 |
|
57 |
| -The libchan protocol requires a reliable, 2-way byte stream as a transport. |
58 |
| -The most popular options are TCP connections, unix stream sockets and TLS sessions. |
| 68 | +The libchan protocol requires a reliable, 2-way byte stream with support for |
| 69 | +multiplexing as a transport. The underlying byte stream protocol is abstracted |
| 70 | +to the libchan protocol through a simple multiplexed stream interface which may |
| 71 | +use SPDY/3.1, HTTP/2, or SSH over over TCP connections, unix stream sockets and |
| 72 | +TLS sessions. |
59 | 73 |
|
60 |
| -It is also possible to use websocket as an underlying transport, which allows exposing |
61 |
| -a libchan endpoint at an HTTP1 url. |
| 74 | +When a reliable stream transport is not available but a non-multiplexed |
| 75 | +connection is available, a multiplexing protocol (such as SPDY or another |
| 76 | +simple multiplexing protocol) may be done on top of the existing connection. |
| 77 | +This also makes using websockets as an underlying byte stream transport |
| 78 | +possible, which allows exposing a libchan endpoint at an HTTP/1 url. |
62 | 79 |
|
63 | 80 | ## Authentication and encryption
|
64 | 81 |
|
65 |
| -Libchan can optionally use TLS to authenticate and encrypt communications. After the initial |
66 |
| -handshake and protocol negotiation, the TLS session is simply used as the transport for |
67 |
| -the libchan wire protocol. |
68 |
| - |
69 |
| -## Wire protocol |
70 |
| - |
71 |
| -Libchan uses SPDY (protocol draft 3) as its wire protocol, with no modification. |
| 82 | +Libchan can optionally use TLS to authenticate and encrypt communications. After |
| 83 | +the initial handshake and protocol negotiation, the TLS session is simply used |
| 84 | +as the transport for the libchan multiplexed stream provider. |
| 85 | + |
| 86 | +## Stream Provider |
| 87 | + |
| 88 | +Libchan uses a stream provider to establish new channels and byte streams over |
| 89 | +an underlying byte stream. The stream provider must be able to send |
| 90 | +headers when creating new streams and retrieve headers for remotely created |
| 91 | +streams. |
| 92 | + |
| 93 | +### Headers |
| 94 | +The stream provider must support sending key-value headers on stream creation. |
| 95 | + |
| 96 | +- *"libchan-ref"* - String representation of a unique 64 bit integer identifier |
| 97 | +for the established stream. |
| 98 | +- *"libchan-parent-ref"* String representation of a unique 64 bit integer |
| 99 | +identifier for parent of the established stream. (see *"Sending nested |
| 100 | +channels"* and *"Sending byte streams"*) |
| 101 | + |
| 102 | +### Streams |
| 103 | +The stream provider provides the functionality for creating new streams as |
| 104 | +well as accepting streams created remotely. A stream is create with |
| 105 | +a set of headers and an accepted stream has a method for returning the |
| 106 | +headers. Closing a stream must put the stream in a half-closed state and |
| 107 | +not allow anymore data to be written. If the remote side has already |
| 108 | +closed, the stream is fully closed. Reseting a stream forces the stream |
| 109 | +into a fully closed state and should only be used in error cases. |
| 110 | +Resetting does not give the remote a chance to finish sending data and |
| 111 | +cleanly close. |
| 112 | + |
| 113 | +## Stream identifiers |
| 114 | +Libchan creates a unique identifier for every stream created by the stream |
| 115 | +provider. The identifiers are integer values and should never be reused. |
| 116 | +The identifier is only unique to a given endpoint, meaning both sides of a |
| 117 | +connection may have the same identifier for two different streams. The |
| 118 | +identifiers received from the remote endpoint should only be used to reference |
| 119 | +streams from that endpoint, and never streams created locally. A remote |
| 120 | +endpoint's stream identifier should never be sent in a libchan message. |
| 121 | +To send a stream created remotely, a new stream should be created |
| 122 | +locally, copied from the remote stream, and the identifier to the local copy |
| 123 | +should be used. |
72 | 124 |
|
73 | 125 | ## Control protocol
|
74 | 126 |
|
75 |
| -Once 2 libchan endpoints have established a SPDY session, they communicate with the following |
76 |
| -control protocol. |
| 127 | +Once 2 libchan endpoints have established a multiplexed stream session, they |
| 128 | +communicate with the following control protocol. |
77 | 129 |
|
78 | 130 | ### Top-level channels
|
79 | 131 |
|
80 |
| -Each SPDY session may carry multiple concurrent channels, in both directions, using standard |
81 |
| -SPDY framing and stream multiplexing. Each libchan channel is implemented by an underlying |
82 |
| -SPDY stream. |
| 132 | +Each libchan session may carry multiple concurrent channels, in both directions, |
| 133 | +using stream multiplexing. Each libchan channel is implemented by an underlying |
| 134 | +stream. |
83 | 135 |
|
84 |
| -To use a SPDY session, either endpoint may initiate new channels, wait for its peer to |
85 |
| -initiate new channels, or a combination of both. Channels initiated in this way are called |
86 |
| -*top-level channels*. |
| 136 | +To use a libchan session, either endpoint may initiate new channels, wait for |
| 137 | +its peer to initiate new channels, or a combination of both. Channels initiated |
| 138 | +in this way are called *top-level channels*. |
87 | 139 |
|
88 |
| -* To initiate a new top-level channel, either endpoint may initiate a new SPDY stream, then |
89 |
| -start sending messages to it (see *"sending messages"*). |
| 140 | +* To initiate a new top-level channel, either endpoint may initiate a new |
| 141 | +stream, then start sending messages to it (see *"sending messages"*). |
90 | 142 |
|
91 |
| -* The endpoint initiating a top-level channel MAY NOT allow the application to receive messages |
92 |
| -from it and MUST ignore inbound messages received on that stream. |
| 143 | +* The endpoint initiating a top-level channel MAY NOT allow the application to |
| 144 | +receive messages from it and MUST interpret inbound messages received on that |
| 145 | +stream as an ack or error message. |
93 | 146 |
|
94 |
| -* When an endpoint receives a new inbound SPDY stream, and the initial headers DO NOT include |
95 |
| -the key `libchan-ref`, it MUST queue a new `Receiver` channel to pass to the application. |
| 147 | +* The endpoint initiating the channel must create a unique identifier for the |
| 148 | +channel and include the value in the *"libchan-ref"* header when creating |
| 149 | +the new stream. |
96 | 150 |
|
97 |
| -* The endpoint receiving a top-level channel MAY NOT allow the application to send messages to |
98 |
| -it. |
| 151 | +* When an endpoint receives a new stream without the header |
| 152 | +*"libchan-parent-ref"*, it MUST interpret the stream as an inbound top-level |
| 153 | +channel and queue a new `Receiver` channel to pass to the application. |
99 | 154 |
|
| 155 | +* The endpoint receiving a top-level channel MAY NOT allow the application to |
| 156 | +send messages to it. |
100 | 157 |
|
101 |
| -### Sending messages on a channel |
102 | 158 |
|
103 |
| -Once a SPDY stream is initiated, it can be used as a channel, with the initiating endpoint holding |
104 |
| -the `Sender` end of the channel, and the recipient endpoint holding the `Receiver` end. |
| 159 | +### Sending messages on a channel |
105 | 160 |
|
106 |
| -* To send a message, the sender MUST encode it using the [msgpack](https://msgpack.org) encoding format, and |
107 |
| -send a single data frame on the corresponding SPDY stream, with the encoded message as the exact content of |
108 |
| -the frame. |
| 161 | +Once a stream is initiated, it can be used as a channel, with the initiating |
| 162 | +endpoint holding the `Sender` end of the channel, and the recipient endpoint |
| 163 | +holding the `Receiver` end. |
109 | 164 |
|
110 |
| -* When receiving a data frame on any active SPDY stream, the receiver MUST decode it using msgpack. If |
111 |
| -the decoding fails, the receiver MUST close the underlying stream, and future calls to `Receive` on that |
112 |
| -channel MUST return an error. |
| 165 | +* To send a message, the sender MUST encode it using the message encoding format |
| 166 | +(see *"message encoding"*), and send the encoded message on the corresponding |
| 167 | +stream. |
113 | 168 |
|
114 |
| -* A valid msgpack decode operation with leftover trailing or leading data is considered an *invalid* msgpack |
115 |
| -decode operation, and MUST yield the corresponding error. |
| 169 | +* When receiving a data on any active stream, the receiver MUST decode it using |
| 170 | +the same message encoding format. If the decoding fails, the receiver MUST close |
| 171 | +the underlying stream, and future calls to `Receive` on that channel MUST return |
| 172 | +an error. |
116 | 173 |
|
117 |
| -### Closing a channel |
| 174 | +* Every send message should have a corresponding receive of an ack message from |
| 175 | +the peer. The ack message is a map with at least one field named `code`. The |
| 176 | +`code` field should have an integer value, with an a value of zero considered |
| 177 | +a successful ack and non-zero as an error. An error should be accompanied with |
| 178 | +an additional `message` field of type string, describing the error. If an error |
| 179 | +is received, the sender should close and pass an error to the application. |
118 | 180 |
|
119 |
| -The endpoint which initiated a channel MAY close it by closing the underlying SPDY stream. |
| 181 | +### Sending nested channels |
120 | 182 |
|
121 |
| -*FIXME: provide more details* |
| 183 | +* When sending a nested channel, in addition to the *"libchan-ref"* header, the |
| 184 | +*"libchan-parent-ref"* header must be sent identifying the channel used to |
| 185 | +create the nested channel. |
122 | 186 |
|
123 |
| -### Sending byte streams |
| 187 | +### Closing a channel |
124 | 188 |
|
125 |
| -Libchan messages support a special type called *byte streams*. Unlike regular types like integers or strings, |
126 |
| -byte streams are not fully encoded in the message. Instead, the message encodes a *reference* which allows |
127 |
| -the receiving endpoint to reconstitute the byte stream after receiving the message, and pass it to the |
128 |
| -application. |
| 189 | +The endpoint which holds the send side of a channel MAY close it which will |
| 190 | +half-close the stream. The receive side should respond by closing the stream, |
| 191 | +putting the stream in a fully closed state. Any send or receive call from the |
| 192 | +application after close should return an error. |
129 | 193 |
|
130 |
| -*FIXME: specify use of msgpack extended types to encode byte streams* |
| 194 | +When an error is received on a channel, the underlying stream should be |
| 195 | +closed by both ends. |
131 | 196 |
|
132 |
| -Libchan supports 2 methods for sending byte streams: a default method which is supported on all transports, |
133 |
| -and an optional method which requires unix stream sockets. All implementations MUST support both methods. |
| 197 | +### Sending byte streams |
134 | 198 |
|
135 |
| -#### Default method: SPDY streams |
| 199 | +Libchan messages support a special type called *byte streams*. Unlike regular |
| 200 | +types like integers or strings, byte streams are not fully encoded in the |
| 201 | +message. Instead, the message encodes a *reference* which allows the receiving |
| 202 | +endpoint to reconstitute the byte stream after receiving the message, and pass |
| 203 | +it to the application. |
136 | 204 |
|
137 |
| -The primary method for sending a byte stream is to send it over a SPDY stream, with the following protocol: |
| 205 | +Byte streams use the raw stream returned by the stream provider. |
138 | 206 |
|
139 |
| -* When encoding a message including 1 or more byte stream values, the sender MUST assign to each value |
140 |
| -an identifier unique to the session, and store these identifiers for future use. |
| 207 | +* When encoding a message including 1 or more byte stream values, the sender |
| 208 | +MUST assign to each value an identifier unique to the session, and store these |
| 209 | +identifiers for future use. |
141 | 210 |
|
142 |
| -* After sending the encoded message, the sender MUST initiate 1 new SPDY stream for each byte stream value |
143 |
| -in the message. |
| 211 | +* After sending the encoded message, the sender MUST create 1 new stream for |
| 212 | +each byte stream value in the message. |
144 | 213 |
|
145 |
| -* Each of those SPDY stream MUST include an initial header with as a key the string "*libchan-ref*", and |
146 |
| -as a value the identifier of the corresponding byte stream. |
| 214 | +* Each of new stream MUST include a header with the key *"libchan-ref"* and |
| 215 | +a value of the identifier of the corresponding byte stream. It must also |
| 216 | +include a header with the key *"libchan-parent-ref"* and a value of the |
| 217 | +stream identifier for the message channel which created the byte stream. |
147 | 218 |
|
148 | 219 | Conversely, the receiver must follow this protocol:
|
149 | 220 |
|
150 |
| -* When decoding a message including 1 or more byte stream values, the receiver MUST store the unique identifier |
151 |
| -of each value in a session-wide table of pending byte streams. It MAY then immediately pass the decoded message to the application. |
152 |
| - |
153 |
| -* The sender SHOULD cap the size of its pending byte streams table to a reasonable value. It MAY make that value |
154 |
| -configurable by the application. If it receives a message with 1 or more byte stream references, and the table |
155 |
| -is full, the sender MAY suspend processing of the message until there is room in the table. |
156 |
| - |
157 |
| -* When receiving new SPDY streams which include the header key "*libchan-ref*", the receiver MUST lookup that |
158 |
| -header value in the table of pending byte streams. If the value is registered in the table, that SPDY stream |
159 |
| -MUST be passed to the application. |
160 |
| - |
161 |
| -On either end, once the SPDY stream for a byte-stream value is established, it MUST be exposed to the application |
162 |
| -as follows: |
163 |
| - |
164 |
| -* After sending each of those SPDY streams, each write operation by the application to a byte-stream field MUST |
165 |
| -trigger the sending of a single data frame on the corresponding SPDY stream. |
166 |
| - |
167 |
| -* Each read operation by the application from a byte-stream field MUST yield the content of the next |
168 |
| -data frame received on the corresponding SPDY stream. If the reading end of the SPDY stream is closed, |
169 |
| -the read operation MUST yield EOF. |
170 |
| - |
171 |
| -* A close operation by the application on the a byte-stream field MUST trigger the closing of the writing end |
172 |
| -of the corresponding SPDY stream. |
173 |
| - |
174 |
| -#### Optional method: file descriptor passing |
| 221 | +* When decoding a message including 1 or more byte stream values, the receiver |
| 222 | +MUST store the unique identifier of each value in a session-wide table of |
| 223 | +pending byte streams. It MAY then immediately pass the decoded message to the |
| 224 | +application. |
175 | 225 |
|
176 |
| -*FIXME* |
| 226 | +* The sender SHOULD cap the size of its pending byte streams table to a |
| 227 | +reasonable value. It MAY make that value configurable by the application. If it |
| 228 | +receives a message with 1 or more byte stream references, and the table |
| 229 | +is full, the sender MAY suspend processing of the message until there is room in |
| 230 | +the table. |
177 | 231 |
|
178 |
| -### Sending nested channels |
| 232 | +* When receiving new streams which include the header key "*libchan-ref*", the |
| 233 | +receiver MUST lookup that header value in the table of pending byte streams. If |
| 234 | +the value is registered in the table, that stream MUST be passed to the |
| 235 | +application. |
179 | 236 |
|
180 |
| -*FIXME* |
| 237 | +On either end, once the stream for a byte-stream value is established, it MUST |
| 238 | +be exposed to the application as follows: |
| 239 | + |
| 240 | +* After sending each of those streams, each write operation by the application |
| 241 | +to a byte-stream field MUST trigger the sending of a single data frame on the |
| 242 | +corresponding stream. |
| 243 | + |
| 244 | +* Each read operation by the application from a byte-stream field MUST yield |
| 245 | +the content of the next data frame received on the corresponding stream. If the |
| 246 | +reading end of the stream is closed, the read operation MUST yield EOF. |
| 247 | + |
| 248 | +* A close operation by the application on the a byte-stream field MUST trigger |
| 249 | +the closing of the writing end of the corresponding SPDY stream. |
| 250 | + |
| 251 | +## Message encoding |
| 252 | +A message may be any type which supported by the libchan encoder. A libchan |
| 253 | +message encoder must support encoding raw byte stream types as well as channels. |
| 254 | +In addition to the libchan data types, time must also be encoded as a custom |
| 255 | +type to increase portability of the protocol. |
| 256 | + |
| 257 | +Currently supported message encoders are msgpack5 and soon CBOR. |
| 258 | + |
| 259 | +### Custom Types |
| 260 | +Each custom type defines a type code and the byte layout to represent that type. |
| 261 | +Directions of descriptions are from the point of view of the endpoint encoding. |
| 262 | +All multi-byte integers are encoded big endian. The length of bytes of the |
| 263 | +encoded value will be provided by the encoding format, allowing integer values |
| 264 | +to be variable length. |
| 265 | + |
| 266 | +| Type | Code | Byte Layout| |
| 267 | +|---|---|---| |
| 268 | +| Duplex Byte Stream | 1 | 4 or 8 byte integer identifier | |
| 269 | +| Inbound Byte Stream | 2 | 4 or 8 byte integer identifier | |
| 270 | +| Outbound Byte Stream | 3 | 4 or 8 byte integer identifier | |
| 271 | +| Inbound channel | 4 | 4 or 8 byte integer identifier | |
| 272 | +| Outbound channel | 5 | 4 or 8 byte integer identifier | |
| 273 | +| time | 6 | 8 byte integer seconds + 4 byte integer nanoseconds | |
| 274 | + |
| 275 | +## Version History |
| 276 | + |
| 277 | +0.2.0 |
| 278 | + - Define stream provider to support multiple multiplexing protocols |
| 279 | + - Support CBOR in addition to Msgpack for channel message encoding |
| 280 | + - Add extended type codes definition |
| 281 | + - Require byte-streams to send *"libchan-parent-ref"* |
| 282 | + - Allow byte-streams as duplex or half-duplex |
| 283 | + - Add channel synchronization through ack definition |
| 284 | + - Add channel errors |
| 285 | + - Update description of relationship to Go channels |
| 286 | + |
| 287 | +0.1.0 |
| 288 | + - Initial specification |
| 289 | + - Message channels |
| 290 | + - Nested message channels |
| 291 | + - Duplex byte streams |
| 292 | + - Msgpack channel message encoding |
| 293 | + - SPDY stream multiplexing |
0 commit comments