#
Protocol v2.0
#
Client
Minimal client protocol version: 2.0
#
Statement
{
// Version of API (not protocol)
"api": 0,
// Client local time in ms
"client_time": 16500123000,
// Which format to use for object encoding: JSON | YAML | TOML
"scheme_format": "JSON",
// Compression algorithms, supported on client side: gzip | zlib
"compressors": [
"gzip",
"zlib"
],
// Optional. compressors[0] used by default
"default_compression": "zlib",
}
api: 0
client_time: 16500123000
scheme_format: JSON
compressors:
- gzip
- zlib
default_compression: zlib
api = 0.0
client_time = 16500123000.0
scheme_format = "JSON"
compressors = [
"gzip",
"zlib"
]
default_compression = "zlib"
{
// Server local time in ms [UTC]
"server_time": 165000123000,
}
server_time: 165000123000
server_time = 165000123000.0
#
Actions
#
0x00
Action
Common request/response action.
#
Structure
Action
consists of:
HEAD
: 18 bytesHEADERS + SEPARATOR + PAYLOAD
:HEAD.data_len
bytes
Where SEPARATOR is 00 00
(two empty bytes)
#
Head
- Handler ID:
uInt2
-Endpoint ID - treat as URL alternative - Message ID:
uInt2
-Message ID - Send Time:
uInt8
- Client send time in ms - Data Type:
uInt1
- Whattype PAYLOAD is - Compression:
uInt1
- What compression was used on PAYLOAD - Data Len:
uInt4
- Length of HEADERS + HEADER_SEPARATOR + PAYLOAD
#
Headers
Message header works like in HTTP: It contains META information that can be used at protocol level to change up server behavior w/o changing business logic.
Message header is a simple UTF-8
encoded JSON (YAML or TOML) dictionary followed by two empty bytes 00 00
Request only Headers
"Offset": int
[1] - tells the server to skip N amount of first bytes of<Data>
section
Response only Headers
There are no response-only header currently supported
Common Headers
"Files": [{"key": str, "name": str, "size": int, "type": str?}]
[1] - This header is being used when<Data Type>
in packet header is set toFILES - 0x02
"Status": int
- HTTP Status code analog. Usually only used by a server to show client if there was any error or not.
[1] Using "Offset" header for the handler that returns
FILES
will also decrease "size" fields in "Files" response header. If "size" will drop to zero, then file won't appear in "Files" header.
#
Payload
Whatever Handler
accepts/returns
#
0x01
StreamAction
Common request/response streaming action. Basically same as Action
#
Structure
StreamAction
consists of:
HEAD
: 14 bytesHeaders size
: 4 bytesHEADERS
: Headers sizePAYLOAD
: multiple parts (optional)Payload part size
: 4 bytesPayload part
END
: 4 empty bytes
Client should read payload until payload part size
is 00 00 00 00
(4 empty bytes), then stop.
Compression of StreamAction
applies on each part separately, where Codec applies on the whole decompressed payload.
#
Head
- Handler ID:
uInt2
-Endpoint ID - treat as URL alternative - Message ID:
uInt2
-Message ID - Send Time:
uInt8
- Client send time in ms - Data Type:
uInt1
- Whattype PAYLOAD and HEADERS are - Compression:
uInt1
- What compression was used on PAYLOAD and HEADERS
#
0x02
InputAction
Small version of Action
, that is initially sent by server to
Client must send InputAction
back to server with requested information (or empty) to continue.
InputAction
may come to client multiple times during single request
#
Structure
InputAction
consists of:
HEAD
: 8 bytesHEADERS + SEPARATOR + PAYLOAD
:HEAD.data_len
bytes
Where SEPARATOR is 00 00
(two empty bytes)
#
Head
- Message ID:
uInt2
-Message ID - Data Type:
uInt1
- Whattype PAYLOAD - Compression:
uInt1
- What compression was used on PAYLOAD - Data Len:
uInt4
- Length of HEADERS + HEADER_SEPARATOR + PAYLOAD
#
0x05
DownloadSpeed
Separate action, that tells server to set send
rate to specific bps
limit.
Server won't reply to this action, but will acknowledge nonetheless
#
Structure
DownloadSpeedAction
consists of:
HEAD
: 4 bytes
#
Head
- Download speed:
uInt4
- Amount of bytes server is allowed to send per second
#
0x06
CancelInput
Separate action, that tells server to stop waiting for answer to InputAction
. Used instead of Input
response.
Server will reply to this action, with basic Action
or StreamAction
#
Structure
CancelInputAction
consists of:
HEAD
: 2 bytes
#
Head
- Message ID:
uInt2
-Message ID - message_id of request that is being processed by server
#
0xFF
PingPong
Separate action, that tells server that client is still alive. Server will respond with same action, but updated send time.
#
Structure
PingAction
consists of:
HEAD
: 8 bytes
#
Head
- Send Time:
uInt8
- Client send time in ms
#
Endpoints
In HTTP servers, endpoints are functions, that are located with method: GET|POST|etc.
and URL: https://...
.
In CATS we use uInt2
ID to locate endpoints.
Endpoints are triggered with Action
and StreamAction
messages, and they can ask issuer for additional info
using
#
Inputs
Inputs - is a mechanism that lets the server ask client for additional information. They also may be used to send partial data on client on demand
Instead of client sending the request first, inputs are sent firstly by server.
Inputs will convey same Message ID as client's request.
#
Broadcast
If you want to send a client an action, but don't want to wait for request, you can send it as usual reply, but with a different Message ID.
Broadcasts are one-sided, so client don't need to reply on it. Client should subscribe (locally) on broadcast in order to handle them, otherwise they will be silently dropped.
#
Message ID
Message ID - is uInt2
random number, generated by sending party. It is used to track request-response
and inputs
- Message ID must be unique for active requests
- Message ID must be
0x0000
to0x7FFF
forrequest-reply
scenario - Message ID must be
0x8000
to0xFFFF
forbroadcast/publish
scenario - You may use increment counter or RNG to generator Message ID
- Response will contain the same
Message ID
as request
#
Data Types
#
0x00
Binary
Plain byte buffer, how to use/parse it, should be described in
#
0x01
Scheme
JSON (YAML or TOML) encoded object. Which format is used declared at initialisation stage
#
0x02
Files
One or multiple files, concatenated together in a single byte buffer. Information about their respective sizes, names, formats, etc. is located in HEADERS
{
// List of files (order is same as byte buffers in payload)
"Files": [
{
// Hashmap key, may equal to name
"key": "avatar",
// Selected file name
"name": "Anakin.jpg",
// Size of byte buffer, that went into payload
"size": 51231,
// MIME (optional)
"type": "image/jpeg",
},
// ... more files
]
}
Files:
- key: avatar
name: Anakin.jpg
size: 51231
type: image/jpeg
[[Files]]
key = "avatar"
name = "Anakin.jpg"
size = 51231.0
type = "image/jpeg"
If payload was cut with Offset
header, Files[n].size
will be reduced as well.
#
0x03
Byte Scheme
Custom data scheme, encoded in plain bytes (like
#
Handshake
Handshake allows server to verify that client is some-what official.
- Client send
N bytes
to server - Server verifies it
- If handshake is OK, server send
01
to client - If handshake is NO, server send
00
to client and abort connection
#
Handshake: SHA256
SHA256 handshake is, as name suggests, just a sha256 hash comparison. It is time-bound, so old hashes won't work, but still have time window of ±10 seconds for connection latency check.
SHA256 is calculated from combined bytes of:
SECRET_KEY
- secret symmetric key that is set by server and must be present at clientTIME
- decimal string representation of time encoded in bytes (UTF-8
as always)
TIME - is a string of decimal integer, with last digit set to 0. Time used on client must take difference between local and server one into account.
Hence, server_time
sent by server in
SHA256 should be sent in pure 32 bytes, no need to convert to HEX string, and then encode to UTF-8 byte buffer, etc.