simple Asynchronous Bi-directional Communication Protocol
A highly-opinionated transport agnostic protocol to communicate asynchronously between client-server
It comprises of two basic mechanisms
- Connection mechanism
- Messaging mechanism
A client initiates a connection.
A server authenticates, accpets a connection and sends a unique session-id for that connection
Both the client and server communicates using the session-id.
- Each active session id should be used by exactly one connection handler
A message is just one or more message frames
Messages have restrictions imposed by server:
- Each message is limited by its size (command + header + body + null) crossing which the server should truncate and mark end of the message
In short, the null message frame portion (<delimiter>\x00
) or size limit which ever is met first should markt the end of message
A messsage frame is one frame sent on the network by using underlying protocol like TCP or web-sockets.
It consists of 4 sections
- COMMAND - An upper-cased command which identifies the type of the message frame
- Headers - List of key-value paires delimited by double colon (::) which carry crucial information of the connection and the frame
- Body - Message frame Body
- null - optional Null byte marking end of the message (not the message frame)
Each section needs to be delimited by any chosen section delimiter
For the sake of examples, we are going to use the delimiter newline + ascii-paragraph (\n¶
)
which translates to \x0A\xB6
Each header within the header section are delimited by \n
This is a valid frame
COMMAND
¶header-key1::headerValue1
header-key2::headerValue2
¶bodybodybodybody
bodybodybodybody
¶^@
Although useless, this is also a valid frame
COMMAND
¶header-key::headerValue
¶^@
This is also a valid frame just that the message is not complete yet
COMMAND
¶header-key::headerValue
¶bodybodybodybody
bodybodybodybody
But this is an invalid frame since headers are not present COMMAND ¶bodybodybodybody bodybodybodybody ¶^@
Valid commands are:
- CONNECT issued by client
- CONNECTED issued by server
- DISCONNECT issued by client
- DISCONNECTING issued by server
- MESSAGE issued by both server and clients
- ERROR to transmit any errors
Valid headers are listed in the below table. Rules and restrictions can be observed while examining the individual commands
Header | Command | Mandatory? |
---|---|---|
client-id | CONNECT | NO |
client-passcode | CONNECT | NO |
session-id | All except CONNECT | YES |
session-expiry | CONNECTED | NO |
msg-id / ref-msg-id | MESSAGE | YES |
send-only | MESSAGE | NO |
msg-more | MESSAGE | NO |
Connection initiation happens only from client
Client
CONNECT
¶client-id::23450-678-aedc
client-passcode::Password@123
¶^@
NOTES:
- The headers client-id, client-passcode should be used for authentication on the server
- Omit those optional headers if no authentication is needed
Server
CONNECTED
¶session-id::NaTPOgp1QUuB6Gm5tAdcSw
session-expiry::Tue, 01 Jun 2017 21:47:38 GMT
NOTES:
- session-id can be anything unqiue that an active server could generate to identify a connection session. This is a named alias for client connection handler
- session-id is a mandatory header on all commands except
CONNECT
- session-expirty is an optional header which identifies the expiry time of the session
- After the expiry time is reached, the server should disconnect the client
Connection can be terminated by either server or client
Client can request server to gracefully terminate the session
Client
DISCONNECT
¶session-id::35a4cf3a-0a75-414b-81e8-69b9b4075c4b
NOTES:
- session-id is passed to request Server to disconnect the client
- This is a graceful termination and server should process the earlier or in-flight messages for the client before disconnecting the client
- After this command is received, server should stop accepting any further messages from the client
Client / Server both can terminate the session informing the other party
Client / Server
DISCONNECTING
¶session-id::abcdef123456@#
NOTES:
- This could be an ugly termination of connection
- The disconnecting party sends the disconnecting command and terminates the Connection
- However, it is not guaranteed (and shouldn't be) that the other party sees this command
MESSAGE is a command to send a message between two peers.
Message can be sent from either client or server.
Since the messaging is asynchronous all the messages have to be tagged with msg-id.
And it is the responsibility of the message sending party to uniquely tag the message id.
There are 2 flavors of messaging
- Messages where responses / acknowledgements are expected
- Messagess which are send-only and delivery guarantee is not bothered
Messsags can span across multiple message frames
Maximum message-size should be implemented by server to limit the size of the message
Sending a response:
Peer
MESSAGE
¶session-id::NaTPOgp1QUuB6Gm5tAdcSw
msg-id::000001
¶Hola
¶^@
Other peer
MESSAGE
¶session-id::NaTPOgp1QUuB6Gm5tAdcSw
ref-msg-id::000001
¶Mundo!!!
¶^@
Sending an acknowledgement is no different than sending a response
Peer
MESSAGE
¶session-id::NaTPOgp1QUuB6Gm5tAdcSw
msg-id::123123
¶Hello
¶^@
Other peer
MESSAGE
¶session-id::NaTPOgp1QUuB6Gm5tAdcSw
ref-msg-id::123123
¶OK
¶^@
Send-only messages
Client/Server
MESSAGE
¶session-id::NaTPOgp1QUuB6Gm5tAdcSw
msg-id::888999
send-only::yes
¶OK
¶^@
NOTES:
- message-id or ref-message-id header should be present on every message
- message-id represents the originating message where as ref-message-id represents the response of the original message
- send-only (
yes
) represents a send only message. ref-messsage-id shouldn't be used in this case - If not a send-only message, server should respond back with a response or an acknowledgement based on how server is implemented
Message can be wired on multiple frames.
Either a null character in the message body marks the end of the message OR
reaching the message size limit marks the end of the message
Frame1 MESSAGE ¶session-id::NaTPOgp1QUuB6Gm5tAdcSw msg-id::123456 msg-more::yes ¶This is first part of the message. Second part
Frame2 MESSAGE ¶session-id::NaTPOgp1QUuB6Gm5tAdcSw msg-id::123456 msg-more::yes ¶is on the way. See I can have ^@ within the message but shouldn't precede with delimieter. Finally the third
Frame3 MESSAGE ¶session-id::NaTPOgp1QUuB6Gm5tAdcSw msg-id::123456 ¶part marks the end of the message ¶^@
The peeer that receives the frames should accumulate them to form a complete message.
The message body formed by the peer should read as follows:
This is first part of the message. Second
part is on the way. See I can have ^@ within the
message but shouldn't precede with delimiter.
Finally the thirdpart marks the end of the
message
NOTES:
- msg-id should be consistent across the frames of a single message
- msg-more should be set to
yes
if there is something more to be sent on next frame - msg-more has no significance on the end frame (where frame has ^@ section)
- If the frame has no ^@ section and no msg-more, then it is marked as Error frame and should be discarded
Let's assume a peer has message size limit of 300 bytes on it
The size includes all the command, headers, body and the null sections of every frame.
In this size limit case, the message that is formed by the peer from the above example looks like
MESSAGE
¶session-id::NaTPOgp1QUuB6Gm5tAdcSw
msg-id::123456
msg-more::yes
¶This is first part of the message. Second
part MESSAGE
¶session-id::NaTPOgp1QUuB6Gm5tAdcSw
msg-id::123456
msg-more::yes
¶is on the way. See I can have ^@ within the
message but shouldn't precede with two CR-LFs.
Finally t
So, the message body that is formed is as follows:
This is first part of the message. Second
part is on the way. See I can have ^@ within the
message but shouldn't precede with two CR-LFs.
Finally t
Error messages are special frames to communicate errors.
Unlike regular messages, the errror messages should be sent in a single frame
Example
ERROR
¶error-code::403
¶Access Forbidden
¶^@
NOTES:
- There are no specific rules for the headers or message body incase of error frames.
- One way is to send the error-code in header and the error description as part of body.
Command Rules
- Valid commands are
CONNECT
,CONNECTED
,DISCONNECT
,DISCONNECTING
,MESSAGE
,ERROR
- Only clients can issue
CONNECT
and only servers can issueCONNECTED
- Only clients can issue
DISCONNECT
but both client and server can issueDISCONNECTING
- Both client and server can send messages using
MESSAGE
command - Although uncommon for clients to send, both client and server can send Error messages using
ERROR
command
Header Rules
- Valid headers which are reserved (cannot be used otherwise) are
- client-id
- client-passcode
- session-id
- session-expiry
- msg-id
- ref-msg-id
- send-only
- msg-more
- error-code
client-id
is mandatory onCONNECT
command. Should represent the client identityclient-passcode
is optional onCONNECT
command. Should be used for authentication alogn withclient-id
session-id
is mandatory on all commands exceptCONNECT
. Should be first populated as part ofCONNETED
commandsession-expiry
is an optional header returned onCONNECTED
command. Represents the upto session validty timemsg-id
is mandatory header onMESSAGE
command unless there is aref-msg-id
header. It represents the unique id of the message in a session to communicate asynchronously. In case of multi-framed message scenario, this identifier is also used to concatenate the message frame bodies to form a single message body.ref-msg-id
is mandatory header onMESSAGE
command unless there is amsg-id
. It represents a response to the message sent with idmsg-id
In case of multi-framed message (response) scenario, this identifier is also used to concatenate the message frame bodies to form a single message body.send-only
is an optional header onMESSAGE
command which represents that the message is send-only and no response is expected. In case of multi-framed message with message-id, it is sufficient to havesend-only
populated on the first frame. It can have valueyes
to reprsent sendonly message. All other values represent nomsg-more
is an optinal header onMESSAGE
command which conveys the peer that there are more message frames being sent as part of single message It can havge a valueyes
. All other values representno
error-code
is an optional conventional header that should only be transmitted onERROR
commands. It represents the an error. If it is an error on a specific message, amessage-id
could be accompanied with. But it is purely upto the implementer.
Encoding rules
- All the messages should be encoded in UTF-8 encoding
- Headers cannot have
::
in their values