The Yar Protocol
Yar does not rely on a schema or IDL file: everything is exchanged on the wire as plain bytes. Any language that can read and write bytes can speak to a Yar service, without installing any framework at all — build one fixed-size binary header and a serialized request body, send them to the service URI, and parse the reply.
Yar does not rely on a schema or IDL file: everything is exchanged on the wire as plain bytes. Any language that can read and write bytes can speak to a Yar service, without installing any framework at all — build one fixed-size binary header and a serialized request body, send them to the service URI, and parse the reply.
A message consists of a fixed-size header of 82 bytes followed by a body. The header is laid out exactly like the following C structure, packed with no padding, and is written to the wire field after field in declaration order:
typedef struct _yar_header {
uint32_t id; /* transaction id */
uint16_t version; /* protocol version, currently always 0 */
uint32_t magic_num; /* must be 0x80DFEC60 */
uint32_t reserved;
unsigned char provider[32]; /* request from whom (authentication) */
unsigned char token[32]; /* request token (authentication) */
uint32_t body_len; /* length of the whole body, including
the packager identifier */
} __attribute__ ((packed)) yar_header_t;The id, magic_num, reserved and body_len fields are stored in network byte order (big-endian); the remaining fields are raw bytes.
The body starts with an 8-byte packager identifier — PHP, JSON or MSGPACK, zero-padded — telling the receiver how the remainder was encoded, followed by the serialized content itself.
- The request body decodes to an array with the keys
i(the transaction id),m(the method being called) andp(the list of parameters). - The response body decodes to an array with the keys
i(the transaction id),s(the status, one of theYAR_ERR_*codes),r(the return value),o(any output the service method produced) ande(the error or exception, when the call failed).
Over HTTP the message is sent as the body of a POST request, with the response arriving as the body of the reply; over TCP or Unix sockets it is written directly on the stream.
Calling a Yar service without the extension
The following self-contained script builds a valid Yar request for the php packager with nothing but standard sockets, sends it to a service URI, and prints the decoded response. Running it against the Operator service from the examples prints int(3).
<?php
$uri = "http://api.example.com/operator.php";
/* 1. the body: packager identifier + serialized request */
$serialized = serialize(array("i" => 1, "m" => "add", "p" => array(1, 2)));
$body = str_pad("PHP", 8, "\0") . $serialized;
/* 2. the header: 82 bytes, multi-byte integers in network byte order */
$header = pack("N", 1) /* id */
. pack("v", 0) /* version */
. pack("N", 0x80DFEC60) /* magic number */
. pack("N", 0) /* reserved */
. str_pad("", 32, "\0") /* provider */
. str_pad("", 32, "\0") /* token */
. pack("N", strlen($body)); /* body length */
/* 3. send it as the body of a POST request */
$stream = stream_context_create(array("http" => array(
"method" => "POST",
"header" => "Content-Type: application/octet-stream\r\n",
"content" => $header . $body,
)));
$reply = file_get_contents($uri, false, $stream);
/* 4. parse the reply: 82-byte header, then the response body */
$response = unserialize(substr($reply, 82 + 8));
var_dump($response["r"]);
?>A more complete client implementation in plain PHP, which also decodes the response header and supports concurrent calls, lives in the tools/ directory of the Yar source repository.