This documentation addresses the transmission protocol underneath the microse RPC channel, by implement the server and/or client according to the protocol, any program in any programming language can serve and/or connect to microse services.
Microse uses the standard WebSocket protocol under the hood for data transmission, and based on that, it introduces a top-level standard to encode and decode messages.
These events are used for transmission, some for the server only, some for the client only, and some for both.
enum ChannelEvents {
CONNECT = 1,
INVOKE = 2,
RETURN = 3,
THROW = 4,
YIELD = 5,
PUBLISH = 6,
PING = 7,
PONG = 8,
}To serve and connect a microse server, use the following url syntax:
<ws|wss>://<hostname>:<port>[/pathname]?id=<id>[&secret=<key>][&codec=<codec>]Or Unix socket path:
[ws+unix:]<filename>?id=<id>[&secret=<key>][&codec=<codec>]NOTE: the codec option is only used on the client side.
The WebSocket server must check the request on upgrade stage, to see if the
WebSocket connection has an id in its request url, and respond
401 Unauthorized error if not presented. And if the server configures the
secret option, it shall, as well, check the secret provided by the client,
if not match, a 401 error shall be responded as well.
All messages transmitted between the server and client are arrays in JSON format, which its first element is the channel event that being used for the peer to know which event is emitted, the remaining elements maybe different for each event, and they will be addressed in the following sections.
Once the connection is established, the server shall send a message of
CONNECT event positively in the following signature to perform a handshake:
[CONNECT, serverId, codec?] // for example [1, "test-server", "JSON"]
The client must accept this event in order to update its server map according
to the serverId received.
Even though we can set the codec option in the URL, it only means that we
prefer the one we set, but it's still possible that the server doesn't support
it, and respond with another codec (or without codec), we should update the
setting to the responded one or to JSON if not present (for old version
servers).
After the connection is established and the handshake is finished, the client and the server are safe to send and receive messages.
To call a remote function, the client shall send a message in the following signature as a request:
[INVOKE, requestId, module, method, [...args]] // for example [2, 1, "app.services.user", "setName", [10000, "John Smith"]]
The client must implement some kind of technique to provide an incremental
requestId, which is used for marking the request, after the server called the
target module and its method, the server will send a RETURN event or
THROW event for errors (or INVOKE event for generator calls) to the client
with the same requestId, so the client knows which request the response
belongs to.
After the server finished a request, it shall send a message in the following signature as response:
[RETURN, requestId, result?] // for example [3, 1, true]
Use JavaScript for example, when you call the return(data) method of a
generator, the data will be sent by the client via the RETURN event in the
following signature as a sub-request:
[RETURN, requestId, module, method, [data]] // for example [3, 2, "app.services.user", "someGeneratorMethod", ["foo"]]
Which the requestId is the same id sent by the parent request when calling the
generator function via the INVOKE event. The server shall send the same
data back to the client and close the generator, which is the default behavior
of a generator function.
Similar to regular response, except the data will always be an object with
done and an optional value properties.
[RETURN, requestId, { done: true, value?, key? }] // for example [3, 2, { done: true, value: "foo" }]
key is used for PHP implementation to support yield $key => $value, but
since this is the RETURN event, the key will alway be null ot not set.
If any error was thrown by the remote function, the server shall send a message in the following signature as an error/exception response.
[THROW, requestId, error] // for example [4, 1, "The name has been taken"]]
The error can be a string or an object that contains properties that can be
used for the client to regenerate the error instance:
nameThe error instance or its constructor's namemessageThe error message passed to the constructor.stack(optional) The stack trace of the error instance.- any other properties may be sent as well.
For example:
Use JavaScript for example, when you call the throw(error) method of a
generator, the error will be sent by the client via the THROW event in the
following signature as a sub-request:
[THROW, requestId, module, method, [error]] // for example [4, 2, "app.services.user", "someGeneratorMethod", ["something went wrong"]]
Which the requestId is the same id sent by the parent request when calling the
generator function via the INVOKE event. The server shall send the same
error back to the client and close the generator, which is the default
behavior of a generator function.
Use JavaScript for example, when you call the next(value) method of a
generator, the value will be sent by the client via YIELD event in the
following signature as a sub-request:
[YIELD, requestId, module, method, [data]] // for example [5, 2, "app.services.user", "someGeneratorMethod", ["bar"]]
Which the requestId is the same id sent by the parent request when calling the
generator function via the INVOKE event.
After the next() function is called and yield value inside the generator
function, the value will be sent by the server via the YIELD event in the
following signature as response:
[YIELD, requestId, { done: false, value, key? }] // for example [5, 2, { done: false, value: "baz" }]
key is used for PHP implementation to support yield $key => $value, other
languages that doesn't support this syntax should just ignore the key.
If the server wants to publish data to the client, it shall send a message in the following signature:
[PUBLISH, topic, data] // for example [6, "foo", "a message to everyone"]
The client shall receive this event and trigger any handler bound to the topic.
After connection, the client can positively and constantly send a message in the following signature, in order to detect availability and keep the connection alive:
[PING, timestamp] // for example [7, 1602752717476]
The server shall accept this event and send back the timestamp in the
following signature:
[PONG, timestamp] // for example [8, 1602752717476]
The client shall implement a method to detected network delay and close the connection in order to re-establish a new connection if there is too much delay.
NOTE: if the WebSocket implementation supports native ping/pong frame, it is
recommended to use the built-in function instead, which means, the server should
implement both the PING event and the ping frame if possible, and send back
PONG event or pong frame accordingly.
{ "name": "Error", "message": "something went wrong", "stack": "Error: something went wrong\n..." }