1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright 2019 Joyent, Inc.
//! Fast: A simple RPC protcol used by Joyent products
//!
//! Fast is a simple RPC protocol used in
//! Joyent's[Triton](http://github.com/joyent/triton) and
//! [Manta](https://github.com/joyent/manta) systems, particularly in the
//! [Moray](https://github.com/joyent/moray) and
//! [Boray](https://github.com/joyent/boray) components.
//!
//! Protocol overview
//!
//! The Fast protocol is intended for use with TCP. Typically, a Fast server
//! listens for TCP connections on a well-known port, and Fast clients connect
//! to the server to make RPC requests. Clients can make multiple connections
//! to the server, but each connection represents a logically separate
//! client. Communication between client and server consist of discrete
//! _messages_ sent over the TCP connection.
//!
//! Fast protocol messages have the following structure:
//!
//! <img src="../../../docs/fastpacket.svg" width="100%" height="100%">
//!
//! * VERSION 1-byte integer. The only supported value is "1".
//!
//! * TYPE 1-byte integer. The only supported value is TYPE_JSON (0x1),
//! indicating that the data payload is an encoded JSON object.
//!
//! * STATUS 1-byte integer. The only supported values are:
//!
//! * STATUS_DATA 0x1 indicates a "data" message
//!
//! * STATUS_END 0x2 indicates an "end" message
//!
//! * STATUS_ERROR 0x3 indicates an "error" message
//!
//! * MSGID0...MSGID3 4-byte big-endian unsigned integer, a unique identifier
//! for this message.
//!
//! * CRC0...CRC3 4-byte big-endian unsigned integer representing the CRC16
//! value of the data payload
//!
//! * DLEN0...DLEN4 4-byte big-endian unsigned integer representing the number
//! of bytes of data payload that follow
//!
//! * DATA0...DATAN Data payload. This is a JSON-encoded object (for TYPE =
//! TYPE_JSON). The encoding length in bytes is given by the
//! DLEN0...DLEN4 bytes.
//!
//! ### Status
//!
//! There are three allowed values for `status`:
//!
//! |Status value | Status name | Description |
//! |------------ | ----------- | ----------- |
//! | `0x1` | `DATA` | From clients, indicates an RPC request. From servers, indicates one of many values emitted by an RPC call.|
//! | `0x2` | `END` | Indicates the successful completion of an RPC call. Only sent by servers. |
//! | `0x3` | `ERROR` | Indicates the failed completion of an RPC call. Only sent by servers. |
//!
//! ### Message IDs
//!
//! Each Fast message has a message id, which is scoped to the Fast
//! connection. These are allocated sequentially from a circular 31-bit space.
//!
//! ### Data payload
//!
//! For all messages, the `data` field contains properties:
//!
//! | Field | Type | Purpose |
//! | -------- | ----------------- | ------- |
//! | `m` | object | describes the RPC method being invoked |
//! | `m.name` | string | name of the RPC method being invoked |
//! | `m.uts` | number (optional) | timestamp of message creation, in microseconds since the Unix epoch |
//! | `d` | object or array | varies by message status |
//!
//! ### Messaging Scenarios
//!
//! Essentially, there are only four messaging scenarios with Fast:
//!
//! **Client initiates an RPC request.** The client allocates a new message
//! identifier and sends a `DATA` message with `data.m.name` set to the name of
//! the RPC method it wants to invoke. Arguments are specified by the array
//! `data.d`. Clients may issue concurrent requests over a single TCP
//! connection, provided they do not re-use a message identifier for separate
//! requests.
//!
//! **Server sends data from an RPC call.** RPC calls may emit an arbitrary
//! number of values back to the client. To emit these values, the server sends
//! `DATA` messages with `data.d` set to an array of non-null values to be
//! emitted. All `DATA` messages for the same RPC request have the same message
//! identifier that the client included in its original `DATA` message that
//! initiated the RPC call.
//!
//! **Server completes an RPC call successfully.** When an RPC call completes
//! successfully, the server sends an `END` event having the same message
//! identifier as the one in the client's original `DATA` message that initiated
//! the RPC call. This message can contain data as well, in which case it should
//! be processed the same way as for a DATA message.
//!
//! **Server reports a failed RPC call.** Any time before an `END` message is
//! generated for an RPC call, the server may send an `ERROR` message having the
//! same message identifier as the one in the client's original `DATA` message
//! that initiated the RPC call.
//!
//! By convention, the `m` fields (`m.name` and `m.uts`) are populated for all
//! server messages, even though `m.name` is redundant.
//!
//! The RPC request begins when the client sends the initial `DATA` message.
//! The RPC request is finished when the server sends either an `ERROR` or `END`
//! message for that request. In summary, the client only ever sends one
//! message for each request. The server may send any number of `DATA` messages
//! and exactly one `END` or `ERROR` message.