Skip to content

Protocol

Eugene Lazutkin edited this page Nov 14, 2017 · 5 revisions

Main

The protocol is very simple. A bundler is attached to an endpoint /bundle by default. It receives an array of options by PUT method as JSON. options can be objects described below, URLs as strings, or a mix of both.

It verifies a bundle (all URLs should be whitelisted, empty payload, and big payloads are rejected, and so on). If everything is alright, it issues I/O requests (usually in parallel), collects responses and sends back a JSON object of the following format:

{
  "bundle": "bundle",
  "results": [
    // a list of result items
  ]
}

Every result item has a following structure:

{
  "options": {"url": "/abc"},  // options object
  "response": {
    "status":       200,       // an HTTP status code
    "statusText":   "OK",      // an HTTP status text corresponding to status code
    "responseType": "",        // taken from options.responseType, or an empty string
    "responseText": "[1,2,3]", // a payload as a string
    "headers": "Content-Type: application/json" // raw headers
  }
}

Options

If options is a string, it is assumed to be a URL for a GET request. Otherwise, following properties are recognized:

  • url is the only required property. It is a URL of an endpoint we deal with.

The rest of properties are all optional with reasonable defaults:

  • method is an HTTP method (including PATCH) as a string. Default: 'GET'.
  • query is a query dictionary (a key/value hash), which is used to form a query part of URL after '?'. Values of such dictionary can be strings, or arrays of strings to form multiple values with the same key. If URL already contains a query part, it is added as is without checking for duplicates. Default: none.
  • data is a data object to send. For GET method it is assumed to be a query object, if query is not specified. For all other requests, it is assumed to be a payload. data is assumed to be a JSON object. Default: none.
  • headers is a dictionary (a key/value hash), which is used to set request headers. Values of such dictionary can be strings, or arrays of strings to form multiple values with the same key. Default: none, but if there is no Accept header, it is set to application/json.

The next batch of properties is directly related to an underlying XHR request:

  • user is a user name as a string to be sent with the request. Default: not sent.
  • password is a password as a string. It is used only if user is specified. Default: ''.
  • timeout is a wait time for a request in milliseconds as a number. Default: not set.
  • responseType is a requested response type as a string. It can be: 'json', 'arraybuffer', 'blob', 'document', 'text', or ''. Essentially it defines an automatic conversion of a received response in response property of XHR, which is used by io() to return a value. Default: not set.
  • mime is a string used to override a returned MIME type in Content-Type response header. Default: not set.

Extensions

Raw JSON objects

Starting with version 1.0.5 of heya/io, it understands to recreate responseText from JSON representation. For that to work following conditions should be met:

  • responseType is "json".
  • responseText is ignored. In order to save some bandwidth it is advisable not to include it at all.
  • response is a valid JSON object to be transferred.
  • headers are to include Content-Type as application/json for consistency.

It allows to reformulate the previous example like that:

{
  "options": {"url": "/abc"},  // options object
  "response": {
    "status":       200,       // an HTTP status code
    "statusText":   "OK",      // an HTTP status text corresponding to status code
    "responseType": "json",    // should be "json"
    "response":     [1,2,3],   // a payload as an object
    "headers": "Content-Type: application/json" // raw headers
  }
}

This extension is used mainly for debugging, because usually a debugger makes it easier to inspect an object, than its textual representation — the former can be represented in a hierarchical fashion, while the latter looks like a huge string with multiple escaped characters.

Another reason is that sometimes we already have JSON objects to send. Using this extension we can avoid an extra call to JSON.stringify().

Optional: timing

In order to profile an I/O implementation, bundler may include optional time property, which is a number in milliseconds that takes to process a request.

This property can be added on an item level to measure how much time did it take to do an individual I/O request:

{
  "options": {"url": "/abc"}, // options object
  "time": 200, // in ms
  "response": {
    // a response object described above
  }
}

It can be added on a bundle level to measure the whole bundle:

{
  "bundle": "bundle",
  "time": 300, // in ms
  "results": [
    // a list of result items
  ]
}

Reference implementation

This reference implementation implements fully the main protocol and its optional time extension.