Warning: You are viewing an old version (0.8) of this documentation. We recommend you view the latest version 1.0.

Features spec v0.8

A detailed test specification that applies to all client libraries is generated from the Ably Ruby client library’s acceptance and test suites. Whilst every official Ably client library has test coverage, the amount of test coverage varies, and as such our recommendation is to refer to the official test specification when developing a client library.

However, we have found the test specification can be difficult as a reference because of both its breadth and the fact that it applies to the Ruby client library which may be unfamiliar as a language for a lot of developers.

As a result, this document outlines the complete feature set of both the REST and Realtime client libraries. It is expected that every client library developer refers to this document to ensure that their client library provides the same API and features as the existing Ably client libraries. In addition to this, it is essential that there is test coverage over all of the features described below.

We recommend you use the IDL and the Ably Java library as a reference when reviewing how the API has been implemented.

Test guidelines

  • (G1) Every test should be executed using all supported protocols (i.e. JSON and MessagePack if supported). This includes both sending & receiving data
  • (G2) All tests by default are run against a special Ably sandbox environment. This environment allows apps to be provisioned without any authentication that can then be used for client library testing. Bear in mind that all apps created in the sandbox environment are automatically deleted after 60 minutes and have low limits to prevent abuse. Apps are configured by sending a POST request to https://sandbox-rest.ably.io/apps with a JSON body that specifies the keys and their associated capabilities, channel namespace rules and any presence fixture data that is required; see ably-common test-app-setup.json. See the Java test setup. Presence fixture data is necessary for the REST library presence tests as there is no way to register presence on a channel in the REST library
  • (G3) Testing statistics can be tricky due to timing issues and slow test suites as a result of sending requests to generate statistics. As such, we provide a special stats endpoint in our sandbox environment that allows stats to be injected into our metrics system so that stats tests can make predictable assertions. To create stats you must send an authenticated POST request to the stats JSON to https://sandbox-rest.ably.io/stats with the stats data you wish to create. See the Javascript stats fixture and setup helper as an example
  • (G4) All REST requests and WebSocket connections to Ably must include the current API version 0.8. Should any new API version with breaking changes be released, the client library will continue to use the API version explicitly requested

REST client library

Client library developers – clone our REST client library Google Doc spec when developing a REST client library to help you keep track of feature compliance and test coverage.

RestClient

  • (RSC1) The constructor accepts either an API key, a token string, or a set of ClientOptions. If invalid arguments are provided such as a no API key, no token and no means to create a token, then this will result in an error
  • (RSC2) The logger by default outputs to STDOUT (or other logging medium as appropriate to the platform) and the log level is set to warning
  • (RSC3) The log level can be changed
  • (RSC4) A custom logger can be provided in the constructor
  • (RSC5) RestClient#auth attribute provides access to the Auth object that was instanced with the ClientOptions provided in the RestClient constructor
  • (RSC6) RestClient#stats function:
    • (RSC6a) Returns a PaginatedResult page containing Stats objects in the PaginatedResult#items attribute returned from the stats request
    • (RSC6b) Supports the following params:
      • (RSC6b1) start and end are timestamp fields represented as milliseconds since epoch, or where suitable to the language, Date or Time objects. start must be equal to or less than end and is unaffected by the request direction
      • (RSC6b2) direction backwards or forwards; if omitted the direction defaults to the REST API default (backwards)
      • (RSC6b3) limit supports up to 1,000 items; if omitted the limit defaults to the REST API default (100)
      • (RSC6b4) unit is the period for which the stats will be aggregated by, values supported are minute, hour, day or month; if omitted the unit defaults to the REST API default (minute)
  • (RSC16) RestClient#time function sends a get request to rest.ably.io/time and returns the server time in milliseconds since epoch or as a Date/Time object where suitable
  • (RSC7) Sends REST requests over HTTP and HTTPS to the REST end-point rest.ably.io
    • (RSC7a) The header X-Ably-Version: 0.8 must be included in all REST requests to the Ably end-point
  • (RSC18) If ClientOptions#tls is true, then all communication is over HTTPS. If false, all communication is over HTTP however Basic Auth over HTTP will result in an error as private keys cannot be submitted over an insecure connection. See Auth below
  • (RSC8) Supports two protocols:
    • (RSC8a) MessagePack binary protocol (this is the default for environments having a suitable level or support for binary data)
    • (RSC8b) JSON text protocol (used when useBinaryProtocol option is false)
  • (RSC9) Uses Auth to establish what authentication scheme to use, how to authenticate, and automatic issuing of tokens when necessary
  • (RSC10) If a REST request responds with a token error (401 HTTP status code and an Ably error value 40140 <= @code < 40150), then the Auth class is responsible for reissuing a token and the request should be reattempted, see RSC14c and RSC14d
  • (RSC11) Requests are sent to the default endpoint rest.ably.io. However, if the restHost option is set, the client will send requests to the specified host. If environment option is configured and is not “production”, the environment name is prefixed to the default host endpoint and the restHost is set accordingly. For example, if the environment is set to sandbox, then the restHost endpoint will become sandbox-rest.ably.io
  • (RSC12) REST endpoint host is configurable in the Client constructor with the option restHost
  • (RSC13) The client library must use the connection and request timeouts specified in the ClientOptions, falling back to the defaults described in ClientOptions below
  • (RSC14) Authentication
    • (RSC14a) Supports basic authentication when an API key is provided with the key option. The API key follows the format "KEY_NAME:KEY_SECRET" so when authenticating using Basic Auth, the key name can be used as the username and the key secret as the password
    • (RSC14b) Uses token authentication whenever the criteria of RSA4 are met
    • (RSC14c) When a token or tokenDetails is used to instance the library, and no means to renew the token is provided (either an API key, authCallback or authUrl), if the server responds with a token error (401 HTTP status code and an Ably error value 40140 <= @code < 40150), then the client library should indicate an error and not retry the request
    • (RSC14d) When the client does have a means to renew the token automatically, and the token has expired or the server has responded with a token error (statusCode value of 401 and error code value in the range 40140 <= @code < 40150), then the client should automatically make a single attempt to reissue the token and resend the request using the new token. If the token creation failed or the subsequent request with the new token failed due to a token error, then the request should result in an error
  • (RSC15) Host Fallback
    • (RSC15b) The fallback behaviour described below only applies when the default rest.ably.io endpoint is being used and has not been overriden, see RSC11. If host fallback is not supported, failing HTTP requests that would have qualified for a retry against a fallback host, will instead result in an error immediately
    • (RSC15e) By default, every new HTTP request is first attempted to the primary host rest.ably.io, which, through DNS, is automatically routed to the client’s closest data centre. The client library must always prefer the default endpoint (closest data centre), even if a previous request to that endpoint has failed
    • (RSC15a) In the case of an error necessitating use of an alternative host (see RSC15d), try fallback hosts in random order, continuing to try further hosts if qualifying errors occur, failing when all have been tried or the configured httpMaxRetryCount has been reached (see TO3l5). This ensures that a client library is able to work around routing or other problems for the user’s closest data centre. For example, if a POST request to rest.ably.io fails because the default endpoint is unreachable or unserviceable, then the POST request should be retried again against the fallback hosts in attempt to find an alternate healthy data centre to service the request. The five fallback hosts are [a-e].ably-realtime.com.
    • (RSC15d) Errors that necessitate use of an alternative host include: host unresolvable or unreachable, request timeout, or a response but with an applicable HTTP status code in the range 500 <= code <= 504. Resending requests that have failed for other failure conditions will not fix the problem and will simply increase the load on other data centres unnecessarily
  • (RSC17) When instancing the library, if a clientId attribute is set in ClientOptions, then the Auth#clientId attribute will contain the provided clientId

Auth

  • (RSA1) Basic Auth connects over HTTPS by default. Any attempt to use Basic Auth over HTTP will result in an error
  • (RSA11) When using Basic Auth, the API key is Base64 encoded and included in an Authorization header, as specified in RFC7235
  • (RSA2) Basic Auth is the default authentication scheme if an API key exists
  • (RSA3) Token Auth:
    • (RSA3a) Can be used over HTTP or HTTPs
    • (RSA3b) For REST requests, the token string is Base64 encoded and used in the Authorization: Bearer header
    • (RSA3c) For Realtime connections, the querystring param accessToken is appended to the URL endpoint
  • (RSA4) Token Auth is used if useTokenAuth is set to true, or if useTokenAuth is unspecified and any one of the following conditions are met: a clientId is specified; authUrl or authCallback is provided; an explicit token or TokenDetails is provided
  • (RSA14) If Token Auth is selected, yet a token is not provided and there is no means to generate a token, then this will result in an error. For example, if only the option useTokenAuth is specified, and a key is not provided, then the client library is unable to authenticate or issue a token
  • (RSA15) If Token Auth is selected and clientId has been set in the ClientOptions when the library was instanced:
    • (RSA15a) Any clientId provided in ClientOptions must match any non wildcard ('*') clientId value in TokenDetails or connectionDetails of the CONNECTED ProtocolMessage, where applicable
    • (RSA15b) If the clientId from TokenDetails or connectionDetails contains only a wildcard string '*', then the client is permitted to be either unidentified (i.e. anonymous without a clientId) or identified by providing a clientId when communicating with Ably
    • (RSA15c) Following an auth request which uses a TokenDetails or TokenRequest object that contains an incompatible clientId, the library should in the case of Realtime change the connection state to FAILED and emit an error, and in the case of REST result in an appropriate error response
  • (RSA5) TTL for new tokens is specified in milliseconds (REST API default is 1 hour, see TK2a)
  • (RSA6) The capability for new tokens is JSON stringified (REST API default is allow all {"*":["*"]}, see TK2b)
  • (RSA7) clientId and authenticated clients:
    • (RSA7a) If a clientId is provided in the ClientOptions, or is present in the current authentication token, then the client is considered to be identified (it has a clientId that is implicit in all operations). Note that an authentication token clientId wildcard value of '*' is the exception where the client is unidentified and any clientId is permitted in operations with Ably. The following applies to identified clients:
      • (RSA7a1) All operations (such as message publishing or presence) will have an implicit clientId. The Ably service automatically updates the clientId attribute (when empty) for all Message and PresenceMessage messages received from that identified client, and any messages then published from the Ably service will have the clientId attribute populated. It is therefore expected that Ably client libraries do not explicitly set the clientId field on messages published when clientId is implicit in the connection or authentication scheme
      • (RSA7a2) If clientId is provided in ClientOptions, and an API key is provided along with no other means to generate a token, the client library will authenticate with Ably and obtain a token using the provided clientId ensuring the token is restricted to operations for that clientId
      • (RSA7a4) When a clientId value is provided in both ClientOptions#clientId and ClientOptions#defaultTokenParams, the ClientOptions#clientId takes precedence and is used for all Auth operations
    • (RSA12) Auth#clientId attribute is null when:
      • (RSA12a) The clientId attribute of a TokenRequest or TokenDetails used for authentication is null, or ConnectionDetails#clientId is null following a connection to Ably. In this case, the null value indicates that a clientId identity may not be assumed by this client i.e. the client is anonymous for all operations
      • (RSA12b) The client was instanced without assigning a value for ClientOptions#clientId (null), and the client has not yet authenticated or connected to Ably. In this case, the null value indicates that the client has not yet been able to confirm its identity, and therefore may change and become identified following later authentication or establishment of a connection with Ably
    • (RSA7b) Auth#clientId is not null when:
      • (RSA7b1) A clientId is provided in the ClientOptions. clientId should be a string
      • (RSA7b2) Token authentication is being used, and the TokenRequest or TokenDetails object, used for authentication, has a clientId value that is not null
      • (RSA7b3) Following a realtime connection being established, if the CONNECTED ProtocolMessages contains a clientId that is not null. clientId is an attribute of ProtocolMessage#connectionDetails within a CONNECTED ProtocolMessage
      • (RSA7b4) When a wildcard string '*' is present in the TokenRequest, TokenDetails, or ProtocolMessage#connectionDetails object, then the client does not have an identity but is allowed to assume an identity when performing operations with Ably. As such, Auth#clientId should contain the string value '*' indicating that the current client is allowed to perform operations on behalf of any clientId
    • (RSA7c) A clientId provided in the ClientOptions when instancing the library must be either null or a string, and cannot contain only a wildcard '*' string value as that client ID value is reserved
  • (RSA8) Auth#requestToken function:
    • (RSA8e) Method signature is requestToken(TokenParams, AuthOptions). TokenParams and AuthOptions are optional. When TokenParams or AuthOptions are provided, the values of each attribute are not merged with the configured client library defaults, but instead replace all corresponding values, even when null. If the object arguments are omitted, the client library configured defaults are used
    • (RSA8a) Implicitly creates a TokenRequest if required, and requests a token from Ably if required. Returns a TokenDetails object
    • (RSA8b) Supports all TokenParams in the function arguments, which override defaults for Client Auth
    • (RSA8c) When authUrl option is set, it will query the provided URL to obtain a TokenRequest or the token itself (either a token string or a TokenDetails). The query is performed using the given URL using the HTTP method in authMethod, headers (from authHeaders) and supplementary params (from authParams). The token retrieved is assumed by the library to be a token string if the response has Content-Type "text/plain", or taken to be a TokenRequest or TokenDetails object if the response has Content-Type "application/json". authMethod can be either GET or POST, or if not specified, will default to GET. It can be quite difficult to add test coverage for these scenarios – as such, we have developed a simple echo server that can be used in your tests, see the ably-js authUrl echo tests
      • (RSA8c1) TokenParams and any configured authParams and authHeaders are always sent to the authUrl as follows:
        • (RSA8c1a) When the authMethod is GET or unspecified, the TokenParams and authParams are merged and appended to the URL as query string params, and the authHeaders are sent as HTTP headers
        • (RSA8c1b) When the authMethod is POST, the TokenParams and authParams are merged and sent form-encoded in the body of the POST request, and the authHeaders are sent as HTTP headers
      • (RSA8c2) TokenParams take precedence over any configured authParams when a name conflict occurs
      • (RSA8c3) Specifying authParams or authHeaders as part of AuthOptions replaces any configured authParams or authHeaders specified in ClientOptions respectively. As the provided key/value pairs are not merged with the ClientOptions configured key/value pairs, this enables a developer to delete authParams or authHeaders where necessary by providing an entire new set of key/value pairs
    • (RSA8d) When authCallback option is set, it will invoke the callback, passing in the TokenParams, and expects either a token string, a TokenDetails object or a TokenRequest object to be returned, which will in turn be used to request a token from Ably
    • (RSA8f) A test should exist for the following:
      • (RSA8f1) Request a token with a null value clientId, authenticate a client with the token, publish a message without an explicit clientId, and ensure the message published does not have a clientId. Check that Auth#clientId is null
      • (RSA8f2) Request a token with a null value clientId, authenticate a client with the token, publish a message with an explicit clientId value, and ensure that the message is rejected
      • (RSA8f3) Request a token with a wildcard '*' value clientId, authenticate a client with the token, publish a message without an explicit clientId, and ensure the message published does not have a clientId. Check that Auth#clientId is a string with value '*'
      • (RSA8f4) Request a token with a wildcard '*' value clientId, authenticate a client with the token, publish a message with an explicit clientId value, and ensure that the message published has the provided clientId
  • (RSA9) Auth#createTokenRequest function:
    • (RSA9h) Method signature is createTokenRequest(TokenParams, AuthOptions). TokenParams and AuthOptions are optional.When TokenParams or AuthOptions are provided, the values of each attribute are not merged with the configured client library defaults, but instead replace all corresponding values, even when null. If the object arguments are omitted, the client library configured defaults are used
    • (RSA9a) Returns a signed TokenRequest object that can be used to obtain a token from Ably. This is useful for servers that can create a TokenRequest signed with the API key without communicating with Ably directly. The TokenRequest can then be passed to a designated client that is then responsible for communicating with Ably and requesting a token for authentication from that TokenRequest
    • (RSA9b) Supports all AuthOptions
    • (RSA9c) Generates a unique 16+ character nonce if none is provided; the nonce is used to protect against replay attacks
    • (RSA9d) Generates a timestamp from current time if not provided, will retrieve the server time if queryTime is true
    • (RSA9e) TTL is optional and specified in milliseconds
    • (RSA9f) Capability JSON text can be provided that specifies the rights of the token in terms of the channel(s) authorised and the permitted operations on each
    • (RSA9g) A valid HMAC is created using the key secret to sign the TokenRequest so that it can be used by any client to request a token without having or exchanging any secrets
    • (RSA9i) Adheres to all requirements in RSA8 relating to TokenParams
  • (RSA10) Auth#authorise function:
    • (RSA10a) Instructs the library to create a token if needed and uses Token Auth for all future requests
    • (RSA10j) Method signature is authorise(TokenParams, AuthOptions). TokenParams and AuthOptions are optional. When the arguments are present, even if empty, the TokenParams and AuthOptions supersede any previously client library configured TokenParams and AuthOptions. For example, if a client is initialized with TokenParams#ttl configured with a custom value, and a TokenParams object is passed in as an argument to #authorise with a null value for ttl, then the ttl used for every subsequent authorisation will be null. See RSA10d for an exception
    • (RSA10b) Supports all AuthOptions and TokenParams in the function arguments
    • (RSA10c) Will not create a new token unless no previous token exists or current token has expired. Please note that a buffer of 15s for token expiry is recommended to avoid race conditions where the token is valid at the time of the request, but invalid when it reaches the server. Therefore, we recommend that a token is considered expired 15s before the time field `expires`
    • (RSA10d) If the AuthOption argument’s force attribute is true, it will force Auth#authorise to issue a new token even if an existing token exists. A special case convenience exists for AuthOption stating that if all its attributes are null apart from force, then when passed to authorise as an argument, the previously configured authentication options will remain intact. This behaviour takes precedence over RSA10j
    • (RSA10k) If the AuthOption argument’s queryTime attribute is true, it will obtain the server time once and persist the offset from the local clock. All future token requests generated directly or indirectly via a call to authorise will not obtain the server time, but instead use the local clock offset to calculate the server time. A client MAY discard the cached local clock offset in situations in which it may have been invalidated, such as if there is a local change to the date, time, or timezone, of the client device
    • (RSA10e) Adheres to the implementation of requestToken when issuing new tokens
    • (RSA10f) Returns a TokenDetails object that contains the token string + token metadata
    • (RSA10g) Stores the AuthOptions and TokenParams arguments as defaults for subsequent authorizations with the exception of the attributes TokenParams#timestamp, AuthOptions#queryTime and AuthOptions#force
    • (RSA10h) Will use the value from Auth#clientId by default, if not null
    • (RSA10i) Adheres to all requirements in RSA8 relating to TokenParams, authCallback and authUrl

Channels

  • (RSN1) Channels is a collection of Channel objects accessible through Rest#channels
  • (RSN2) Methods should exist to check if a channel exists or iterate through the existing channels
  • (RSN3) Channels#get function:
    • (RSN3a) Creates a new Channel object for the specified channel if none exists, or returns the existing channel. ChannelOptions can be specified when instancing a new Channel
    • (RSN3b) If options are provided, the options are set on the Channel
    • (RSN3c) Accessing an existing Channel with options in the form Channels#get(channel, options) will update the options on the channel and then return the existing Channel object
  • (RSN4) Channels#release function:
    • (RSN4a) Releases the channel resource i.e. it’s deleted and can then be garbage collected

Channel

  • (RSL1) Channels#publish function:
    • (RSL1a) Expects either an array of Message objects or a name string and data payload
    • (RSL1b) When name and data is provided, a single message is published to Ably
    • (RSL1c) When an array of Message objects is provided, a single request is made to Ably. When publishing multiple messages, this approach is more efficient. However, a yet to be implemented feature should limit the total number of messages bundled in a single POST based on the default max request size, and would reject the publish and indicate an error if any single message exceeds that limit
    • (RSL1d) Indicates an error if the message was not successfully published to Ably
    • (RSL1e) Allows name and or data to be null. If any of the values are null, then key is not sent to Ably i.e. a payload with a null value for data would be sent as follows { "name": "click" }
    • (RSL1f) Unidentified clients using Basic Auth (i.e. any clientId is permitted as no clientId specified):
      • (RSL1f1) When a Message with a clientId value is published, Ably will accept and publish that message with the provided clientId. A test should assert via the history API that the clientId of the published Message is populated
    • (RSL1g) Identified clients with a clientId (as a result of either an explicitly configured clientId in ClientOptions, or implicitly through Token Auth):
      • (RSL1g1) When publishing a Message with the clientId attribute set to null:
        • (RSL1g1a) It is unnecessary for the client to set the clientId of the Message before publishing
        • (RSL1g1b) Ably will assign the identified clientId upon receiving the Message. A test should assert via the history API that the clientId value is the identified clientId for the Message when received
      • (RSL1g2) When publishing a Message with the clientId attribute value set to the identified client’s clientId, Ably will accept the message and publish it. A test should assert that the clientId value is populated for the Message when received
      • (RSL1g3) When publishing a Message with a different clientId attribute value to the identified client’s clientId, the client library should reject the message, and indicate an error. The connection and channel remain available for further operations
      • (RSL1g4) When publishing a message with an explicit clientId that is incompatible with the identified client’s clientId (either inferred or explicitly configured in the token or ClientOptions), the library will reject the message immediately and indicate an error
    • (RSL1h) Where the library language permits, the Channel#publish(name, data) method should provide an optional argument that allows the clientId value to be specified such as Channel#publish('event', 'data', { clientId: 'John' })
  • (RSL2) Channel#history function:
    • (RSL2a) Returns a PaginatedResult page containing the first page of messages in the PaginatedResult#items attribute returned from the history request
    • (RSL2b) Supports the following params:
      • (RSL2b1) start and end are timestamp fields represented as milliseconds since epoch, or where suitable to the language, Time objects. start must be equal to or less than end and is unaffected by the request direction
      • (RSL2b2) direction backwards or forwards; if omitted the direction defaults to the REST API default (backwards)
      • (RSL2b3) limit supports up to 1,000 items; if omitted the direction defaults to the REST API default (100)
  • (RSL3) Channel#presence attribute returns a Presence object for this channel
  • (RSL4) Message encoding
    • (RSL4a) Payloads must be binary, strings, or objects capable of JSON representation, or can be empty (omitted). Any other data type should not be permitted and result in an error
    • (RSL4b) If a message is encoded, the encoding attribute represents the encoding(s) applied in right to left format i.e. “utf-8/base64” indicates that the original payload has “utf-8” encoding and has subsequently been encoded in Base64 format
    • (RSL4c) When using MessagePack Message encoding
      • (RSL4c1) a binary Message payload is encoded as MessagePack binary type
      • (RSL4c2) a string Message payload is encoded as MessagePack string type
      • (RSL4c3) a JSON Message payload is stringified either as a JSON Object or Array and encoded as MessagePack string type and the encoding attribute is set to “json”
      • (RSL4c4) All messages received will deliver payloads in the format they were sent in i.e. binary, string, or a structured type containing the parsed JSON
    • (RSL4d) When using JSON Message encoding
      • (RSL4d1) a binary Message payload is encoded as Base64 and represented as a JSON string the encoding attribute is set to “base64”
      • (RSL4d2) a string Message payload is represented as a JSON string
      • (RSL4d3) a JSON Message payload is stringified either as a JSON Object or Array and represented as a JSON string and the encoding attribute is set to “json”
      • (RSL4d4) All messages received will be decoded based on the encoding field and deliver payloads in the format they were sent in i.e. binary, string, or a structured type containing the parsed JSON
  • (RSL5) Message payload encryption
    • (RSL5a) When a Channel is instanced with a (non-null) cipher channelOption, message payloads will be automatically encrypted when sent to Ably and decrypted when received on this channel, using the cipher configuration
    • (RSL5b) AES 256 and 128 CBC encryption must be supported
    • (RSL5c) Tests must exist that encrypt and decrypt the following fixture data for AES 128 and AES 256 to ensure the client library encryption is compatible across libraries
  • (RSL6) Message decoding
    • (RSL6a) All messages received will be decoded automatically based on the encoding field and the payloads will be converted into the format they were originally sent using i.e. binary, string, or JSON
    • (RSL6b) If, for example, incompatible encryption details are provided or invalid Base64 is detected in the message payload, an error message will be sent to the logger, but the message will still be delivered with last successful decoding and the encoding field. For example, if a message had a decoding of “utf-8/cipher+aes-128-cbc/base64”, and the payload was successfully Base64 decoded but the payload could not be encrypted because the CipherParam details were not configured, the message would be delivered with a binary payload and an encoding with the value “utf-8/cipher+aes-128-cbc”

Presence

  • (RSP1) Presence object is associated with a single channel and is accessible through Channel#presence
  • (RSP2) There is no way to register a member as present on a channel via the REST API
  • (RSP3) Presence#get function:
    • (RSP3a) Returns a PaginatedResult page containing the first page of members present in the PaginatedResult#items attribute returned from the presence request. Each member is represented as a PresenceMessage. Supports the following params:
      • (RSP3a1) limit supports up to 1,000 items; if unspecified it defaults to the REST API default (100)
      • (RSP3a2) clientId filters members by the provided clientId
      • (RSP3a3) connectionId filters members by the provided connectionId
  • (RSP4) Presence#history function:
    • (RSP4a) Returns a PaginatedResult page containing the first page of messages in the PaginatedResult#items attribute returned from the presence request
    • (RSP4b) Supports the following params:
      • (RSP4b1) start and end are timestamp fields represented as milliseconds since epoch, or where appropriate to the language, Date/Time objects. start must be equal to or less than end and is unaffected by the request direction
      • (RSP4b2) direction backwards or forwards; if unspecified defaults to the REST API default (backwards)
      • (RSP4b3) limit supports up to 1,000 items; if unspecified defaults to the REST API default (100)
  • (RSP5) Presence Messages retrieved are decoded in the same way that messages are decoded

Encryption

  • (RSE1) Crypto::getDefaultParams function:
    • (RSE1a) Returns a complete CipherParams instance, using the default values for any field not supplied
    • (RSE1b) Takes a hashmap (or language equivalent) consisting of any subset of CipherParams fields that includes a key
    • (RSE1c) The key must be either a binary (e.g. a byte array, depending on the language), or a base64-encoded string. If the key is a string, the function should base64-decode it into a binary. Since the conversion to base64 is not under Ably control, this should be done leniently — in particular, it should work with base64url (RFC 4648 s.5, which uses - and _ instead of + and /) as well as base64 (RFC 4648 s.4)
    • (RSE1d) Calculates a keyLength from the key (its size in bits).
    • (RSE1e) Checks that the provided options are valid and self-consistent as best it can, raises an exception if not. At a minimum, this should include checking the calculated keyLength is a valid key length for the encryption algorithm (for example, 128 or 256 for AES)
  • (RSE2) Crypto::generateRandomKey function
    • (RSE2a) Takes an optional keyLength parameter, which is the length in bits of the key to be generated. If unspecified, this is equal to the default keyLength of the default algorithm: for AES, 256 bits.
    • (RSE2b) Returns (or calls back with, if the language cryptographic randomness primitives are blocking or async) the key as a binary (e.g. a byte array, depending on the language)

Realtime client library features

The Ably Realtime client libraries establish and maintain a persistent connection to Ably and provide methods to publish and subscribe to messages over a low latency realtime connection.

The Realtime library is a superset of the REST library and as such all Realtime libraries provide the functionality available in the REST library in addition to Realtime-specific features.

The threading and/or asynchronous model for each realtime library will vary by language and it is therefore up to the developer to decide on the best approach for each given client library. For example, Node.js and Ruby (EventMachine) use a similar callback single threaded evented approach that ensures all public methods are non-blocking. Java and .NET use a threaded model whereby the Connection runs in its own thread. Go makes extensive use of goroutines and channels.

RealtimeClient

  • (RTC1) Supports all the same ClientOptions as the RestClient in addition to:
    • (RTC1a) echoMessages boolean is true by default. If false, it prevents messages originating from this connection being echoed back on the same connection
    • (RTC1b) autoConnect boolean is true by default. If true, as soon as the client library is instanced, it will connect to Ably. If false, the client library will wait for an explicit Connection#connect to be called before connecting
    • (RTC1c) recover string, when set, will attempt to recover the connection state of a previous connection
    • (RTC1d) realtimeHost string, when set, will modify the realtime endpoint host used by this client library
    • (RTC1e) environment string, when set, will modify both the REST and realtime endpoint hosts by prefixing the environment to the default endpoint host with a hypen delimiter. For example, a RealtimeClient with an environment of “sandbox”, would use “sandbox-rest.ably.io” as the restHost and sandbox-realtime.ably.io as the realtimeHost
  • (RTC2) RealtimeClient#connection attribute provides access to the underlying Connection object
  • (RTC3) RealtimeClient#channels attribute provides access to the underlying Channels object
  • (RTC4) RealtimeClient#auth attribute provides access to the Auth object that was instanced with the ClientOptions provided in the RealtimeClient constructor
    • (RTC4a) Unlike the stateless REST client library, the Auth#clientId is populated when the connection is established. The CONNECTED ProtocolMessage contains the confirmed clientId for this connected client i.e. the client is considered identified. See RSA7b and RSA12 for further info
  • (RTC5) RealtimeClient#stats function:
    • (RTC5a) Proxy to RestClient#stats presented with an async or threaded interface as appropriate
    • (RTC5b) Accepts all the same params as RestClient#stats and provides all the same functionality
  • (RTC6) RealtimeClient#time function:
    • (RTC6a) Proxy to RestClient#time presented with an async or threaded interface as appropriate
  • (RTC7) The client library must use the configured timeouts specified in the ClientOptions, falling back to the client library defaults and defaults described in ClientOptions below
  • (RTC8) Authentication token changes for the current connection are possible when the client is in the CONNECTED, CONNECTING or DISCONNECTED states. If auth.authorise is called with AuthOptions#force set to true and the state is CONNECTED, CONNECTING, or DISCONNECTED, the client will obtain a new token, disconnect the current transport and resume the connection (see RTN15c for details on resume). A test should exist that performs an upgrade of capabilities without any message loss during the upgrade process. A test should also exist for a token change that fails due to an incompatible token, which should result in the connection entering the FAILED state

Connection

  • (RTN1) Connection connects to the Ably service using a websocket connection. The ably-js library supports additional transports such as Comet and XHR streaming; however non-browser client libraries typically use only a websocket transport
  • (RTN2) The default host used for realtime websocket connections is realtime.ably.io, and the following query string params should be used when opening a new connection:
    • (RTN2a) format should be msgpack (default) or json
    • (RTN2b) echo should be true by default; false will prevent messages published by the client being echoed back
    • (RTN2d) clientId contains the provided clientId option of ClientOptions, unless clientId is null
    • (RTN2e) Depending on the authentication scheme, either accessToken contains the token string, or key contains the API key
    • (RTN2f) API version param v should be 0.8
  • (RTN3) If connection option autoConnect is true, a connection is initiated immediately; otherwise a connection is only initiated following an explicit call to connect()
  • (RTN4) EventEmitter and states:
    • (RTN4a) Implements EventEmitter and emits events for state changes INITIALIZED, CONNECTING, CONNECTED, DISCONNECTED, SUSPENDED, CLOSING, CLOSED, FAILED
    • (RTN4b) A new connection will emit the following events in order when connecting: CONNECTING, then CONNECTED
    • (RTN4c) A connection will emit the following events when closing the connection: CLOSING, then CLOSED
    • (RTN4d) Connection#state attribute is the current state of the connection
    • (RTN4e) A ConnectionStateChange object is emitted as the first argument for every connection state change
    • (RTN4f) Additionally, a ConnectionStateChange can be emitted that contains a reason which contains an ErrorInfo object with details of the error that has occurred for the Connection
  • (RTN5) A test should exist that instances many (50+) clients simultaneously and performs a few basic operations such as attaching to a channel, publishing a message, and expecting all of those messages to arrive on all clients to ensure that there are no concurrency issues with the client library
  • (RTN6) A Connection is successful and considered CONNECTED once the websocket connection is open and the initial CONNECTED ProtocolMessage has been received
  • (RTN19) If the CONNECTED ProtocolMessage contains a connectionDetails property, the attributes within ConnectionDetails will be used as the defaults for this client library, overriding any configured options at the time the CONNECTED ProtocolMessage is received
  • (RTN7) ACK and NACK:
    • (RTN7a) All ProtocolMessage Presence and Message objects sent to Ably expect either an ACK or NACK from Ably to confirm successful receipt and acceptance or failure respectively
    • (RTN7b) Every ProtocolMessage sent must contain a unique serially incrementing msgSerial integer value starting at zero. The msgSerial along with the count for incoming ACK and NACK ProtocolMessages indicates which messages succeeded or failed to be delivered
    • (RTN7c) If a connection enters the CLOSED or FAILED state, or if the connection state is lost, and a ACK or NACK has not yet been received for a message, the client should consider the delivery of those messages as failed
  • (RTN8) Connection#id attribute:
    • (RTN8a) Is null until connected
    • (RTN8b) Is a unique string provided by Ably. You should have a test to ensure multiple connected clients have unique connection IDs
  • (RTN9) Connection#key attribute:
    • (RTN9a) Is null until connected
    • (RTN9b) Is a unique private connection key provided by Ably that is used to reconnect and retain connection state following an unexpected disconnection. You should have a test to ensure multiple connected clients have unique connection keys
  • (RTN10) Connection#serial attribute:
    • (RTN10a) Is set to -1 once CONNECTED
    • (RTN10b) Is always 0 when the first message has been sent and an ACK has been received confirming receipt of the message. The serial is updated whenever a ProtocolMessage is received that contains a connectionSerial value. A test should exist that checks that the serial number is not updated when a message is sent, but then increments by one when the ACK is received
    • (RTN10c) The last known ProtocolMessage connectionSerial is used when restoring connection state
  • (RTN11) Connection#connect function:
    • (RTN11a) Explicitly connects to the Ably service if not already connected
    • (RTN11b) If the state is CLOSING, the client should make a new connection with a new transport instance and remove all references to the old one. In particular, it should make sure that, when the CLOSED ProtocolMessage arrives for the old connection, it doesn’t affect the new one.
    • (RTN11c) If the state is DISCONNECTED or SUSPENDED, aborts the retry process described in RTN14d and RTN14e and immediately tries to reconnect.
    • (RTN11d) If the state is FAILED, moves all the channels to INITIALIZED, sets their errorReason to null, and sets the connection’s errorReason to null.
  • (RTN12) Connection#close function:
    • (RTN12f) If the connection state is CONNECTING, moves immediately to CLOSING. If the connection attempt succeeds, ie. a CONNECTED ProtocolMessage arrives from Ably, then do as specified in RTN12a. If it doesn’t succeed, move to CLOSED.
    • (RTN12a) If the connection state is CONNECTED, sends a CLOSE ProtocolMessage to the server, sets the state to CLOSING and waits for a CLOSED ProtocolMessage to be received
    • (RTN12b) If the CLOSED ProtocolMessage is not received within the default realtime request timeout, the transport will be disconnected and the connection will automatically move to the CLOSED state
    • (RTN12c) If the transport is abruptly closed following a CLOSE ProtocolMessage being sent, then the connection will automatically move to the CLOSED state
    • (RTN12d) If the connection state is DISCONNECTED or SUSPENDED, aborts the retry process described in RTN14d and RTN14e and moves the connection immediately to the CLOSED state
  • (RTN13) Connection#ping function:
    • (RTN13c) If the connection state is CONNECTING or DISCONNECTED, do the operation once the connection state is CONNECTED
    • (RTN13a) Will send a ProtocolMessage with action HEARTBEAT the Ably service when connected and expects a HEARTBEAT message in response. If the client library language supports callbacks, then the callback will be called with the response time or error
    • (RTN13b) Will indicate an error if in, or has moved to, the INITIALIZED, SUSPENDED, CLOSING, CLOSED or FAILED state
    • (RTN13c) Will fail if a HEARTBEAT ProtocolMessage is not received within the default realtime request timeout
  • (RTN14) Connection opening failures:
    • (RTN14a) If an API key is invalid, then the connection will enter the FAILED state and the Connection#errorReason will be set on the Connection object as well as the emitted ConnectionStateChange
    • (RTN14b) If a connection request fails due to an ERROR ProtocolMessage being sent to the client containing a token error (statusCode value of 401 and error code value in the range 40140 <= @code < 40150), then if the token is renewable, the error should not emitted, a single attempt to create a new token will be made and a new connection attempt will be initiated using the new token. If the token creation fails, the next connection attempt fails again due to a token error, or the token was not renewable, the connection will transition to the FAILED state and the Connection#errorReason will be set
    • (RTN14c) A new connection attempt will fail if not connected within the default realtime request timeout
    • (RTN14d) If a connection attempt fails for any recoverable reason (i.e. a network failure or a recoverable authentication failure), the Connection#state will change to DISCONNECTED, the Connection#errorReason will be updated, a ConnectionStateChange with the reason will be emitted, and new connection attempts will periodically be made until the maximum time in that state threshold is reached. The retryIn attribute of the ConnectionStateChange object will contain the time in milliseconds until the next connection attempt. See the disconnectedRetryTimeout of ClientOptions below. Each time a new connection attempt is made the state will change to CONNECTING and then to CONNECTED if successful, or DISCONNECTED if unsuccessful and the default connectionStateTtl has not been exceeded
    • (RTN14e) Once the connection state has been in the DISCONNECTED state for more than the default connectionStateTtl, the state will change to SUSPENDED and be emitted with the reason, and the Connection#errorReason will be updated. In this state, a new connection attempt will be made periodically as specified within suspendedRetryTimeout of ClientOptions
    • (RTN14f) The connection will remain in the SUSPENDED state indefinitely, whilst periodically attempting to reestablish a connection
  • (RTN15) Connection failures once CONNECTED:
    • (RTN15h) If a DISCONNECTED message is received from Ably, then that transport will subsequently be closed by Ably. If the DISCONNECTED message contains a token error (statusCode value of 401 and error code value in the range 40140 <= @code < 40150), if the token is renewable, the error should not emitted, a single attempt to create a new token will be made and a new connection attempt will be initiated using the new token. If the token creation fails, the next connection attempt fails due to a token error, or the token was not renewable, the connection will transition to the FAILED state and the Connection#errorReason will be set
    • (RTN15a) If a Connection transport is disconnected unexpectedly or if a token expires, then the Connection manager will immediately attempt to reconnect and restore the connection state. Connection state recovery is provided by the Ably service and ensures that whilst the client is disconnected, all events are queued and channel state is retained on the Ably servers. When a new connection is made with the correct connection recovery key, the client is able to catch up by receiving the queued ProtocolMessages from Ably. Connection state is only maintained for a brief period, up to a minute, so if a client is disconnected for a longer period connection state cannot be resumed
    • (RTN15b) In order for a connection to be resumed and connection state to be recovered, the client library reconnects to the websocket endpoint with two additional querystring params:
      • (RTN15b1) resume is the private connection key assigned to the connection when the first CONNECTED ProtocolMessage was received
      • (RTN15b2) connectionSerial is the most recent ProtocolMessage#connectionSerial received from Ably or Connection#serial which should be identical
    • (RTN15c) The system’s response to a resume request will be one of the following:
      • (RTN15c1) CONNECTED ProtocolMessage with the same connectionId as the current client, and no error. In this case, the server is indicating that the resume succeeded, all channels are still attached, and all backlog messages are available. The client should not change the state of attached channels, and immediately process any queued messages for that channel
      • (RTN15c2) CONNECTED ProtocolMessage with the same connectionId as the current client, and an error. In this case, the server is indicating that the resume succeeded but with a non-fatal error, all channels are still attached, and some backlog messages may be unavailable. The ErrorInfo received should be emitted on the client Connection and the Connection#errorReason should be set. The client should not change the state of attached channels, and immediately process any queued messages for that channel. Any channels that are not resumed in full may receive an ATTACHED ProtocolMessage with an error, see RTL12
      • (RTN15c3) CONNECTED ProtocolMessage with a new connectionId, and an error in error. In this case, a new connection has been established, the resume has failed, the channels are no longer attached, and the error indicates the resume problem which should be emitted. The client library should immediately detach all channels, fail any queued messages on those channels, and set the Channel#errorReason on each detached Channel. Additionally, the internal msgSerial counter is reset so that the first message published to Ably will contain a msgSerial value of 0
      • (RTN15c4) ERROR ProtocolMessage indicating a fatal error in the connection. The server will close the transport immediately after. If the ERROR is non-recoverable, the client will move to the FAILED state triggering all attached channels to move to the FAILED state as well
    • (RTN15g) When the connection resume has failed, all channels should be detached with a suitable error reason
    • (RTN15f) ACK and NACK responses for published messages can only ever be received on the transport connection on which those messages were sent. Therefore, once a transport drops, the client library must either fail the publish attempt, or re-attempt by re-sending the messages on a new transport if the resume was successful (i.e. the CONNECTED response includes the expected connectionId)
    • (RTN15d) Client libraries should have test coverage to ensure connection state recovery is working as expected by forcibly disconnecting a client and checking that messages published on channels are delivered once the connection is resumed
    • (RTN15e) When a connection is resumed, the Connection#key may change and will be provided in the first CONNECTED ProtocolMessage#connectionDetails when the connection is established. The client library must update the Connection#key value with the new connectionKey value every time
  • (RTN20) When the client library can subscribe to the Operating System events for network/internet connectivity changes:
    • (RTN20a) When CONNECTED, CONNECTING or DISCONNECTING, if the operating system indicates that the underlying internet connection is no longer available, then the client library should immediately change the state to DISCONNECTED with emit a state change with an appropriate reason. This state change will automatically trigger the client library to attempt to reconnect, see RTN15 above
    • (RTN20b) When DISCONNECTED or SUSPENDED, if the operating system indicates that the underlying internet connection is now available, the client library should immediately attempt to connect
  • (RTN16) Connection recovery:
    • (RTN16a) Connection recovery is similar to the automatic connection resume except that connection state is recovered explicitly because a recover key is passed to the Realtime library when instanced. Once a connection is recovered, all channels must be explicitly attached by the developer, and any messages queued to be delivered whilst the client was disconnected will be delivered
    • (RTN16b) Connection#recoveryKey is an attribute composed of the connection key and latest serial received on the connection
    • (RTN16c) Connection#recoveryKey becomes Null when a connection is explicitly CLOSED or CLOSED by the server, as connection state is not retained for connections closed intentionally. The Connection#key and Connection#id is set to Null
    • (RTN16d) When a connection is successfully recovered, the Connection#id will be identical to the id of the connection that was recovered, and Connection#key will always be updated to the ConnectionDetails#connectionKey provided in the first CONNECTED ProtocolMessage
    • (RTN16e) If the recover option is missing or no longer valid when connecting to Ably, the client will connect anyway, but emit a ConnectionStateChange with a reason, and will additionally set the Connection#errorReason with an ErrorInfo object describing the failure
  • (RTN17) Host Fallback
    • (RTN17b) The fallback behaviour described below only applies when the default realtime.ably.io endpoint is being used and has not been overriden, see RTC1d and RTC1e.
    • (RTN17a) By default, every connection attempt is first attempted to the primary host realtime.ably.io, which, through DNS, is automatically routed to the client’s closest data centre. The client library must always prefer the default endpoint (closest data centre), even if a previous connection attempt to that endpoint has failed
    • (RTN17c) In the case of an error necessitating use of an alternative host (see RTN17d), the Connection manager should first check if an internet connection is available by issuing a GET request to https://internet-up.ably-realtime.com/is-the-internet-up.txt. If the request succeeds and the text “yes” is included in the body, then the client library can assume it has a viable internet connection and should then immediately retry the connection against all fallback hosts to find an alternative healthy data centre. The five fallback hosts are [a-e].ably-realtime.com and should be attempted in random order
    • (RTN17d) Errors that necessitate use of an alternative host include: host unresolvable or unreachable, connection timeout, or a response but with an error body statusCode or HTTP response status code in the range 500 <= code <= 504. Attempting to reconnect to a fallback host for other failure conditions will not fix the problem and will simply increase the load on other data centres unnecessarily
    • (RTN17e) If the realtime client is connected to a fallback host endpoint, then for the duration that the transport is connected to that host, all HTTP requests, such as history or token requests, should be first attempted to the same data centre the realtime connection is established with i.e. the same fallback host must be used as the default HTTP request host. If however the HTTP request against that fallback host fails, then the normal fallback host behaviour should be followed attempting the request against another fallback host as described in RSC15
  • (RTN18) Connection state change side effects:
    • (RTN18a) When a connection enters the DISCONNECTED state, it will have no effect on the channel states. Channels in the ATTACHED state will queue messages that are sent as soon as the connection is resumed
    • (RTN18b) When a connection enters the CLOSED or SUSPENDED state, all channels will move to the DETACHED state. Channels in the DETACHED state should reject publishing of messages
    • (RTN18c) When a connection enters the FAILED state, all channels will move to the FAILED state. Channels in the FAILED state should reject publishing of messages
  • (RTN19) Transport state side effects – when a transport is upgraded or disconnected for any reason:
    • (RTN19a) Any ProtocolMessage that is awaiting an ACK/NACK on the old transport will not receive the ACK/NACK on the new transport. The client library must therefore resend any ProtocolMessage that is awaiting a ACK/NACK to Ably in order to receive the expected ACK/NACK for that message. The Ably service is responsible for keeping track of messages, ignoring duplicates and responding with suitable ACK/NACK messages
    • (RTN19b) If there are any pending channels i.e. in the ATTACHING or DETACHING state, the respective ATTACH or DETACH message should be resent to Ably
    • (RTN19b) If a SYNC is underway, ensure the client library adheres to RTP3

Channels

  • (RTS1) Channels is a collection of Channel objects accessible through Realtime#channels
  • (RTS2) Methods should exist to check if a channel exists or iterate through the existing channels
  • (RTS3) Channels#get function:
    • (RTS3a) Creates a new Channel object for the specified channel if none exists, or returns the existing channel. ChannelOptions can be specified when instancing a new Channel
    • (RTS3b) If options are provided, the options are set on the Channel when creating a new Channel
    • (RTS3c) Accessing an existing Channel with options in the form Channels#get(channel, options) will update the options on the channel and then return the existing Channel object
  • (RTS4) Channels#release function:
    • (RTS4a) Detaches the channel and then releases the channel resource i.e. it’s deleted and can then be garbage collected

Channel

  • (RTL1) As soon as a Channel becomes attached, all incoming messages and presence messages are processed and emitted where applicable. PRESENCE and SYNC messages are passed to the Presence object ensuring it maintains a map of current members on a channel in realtime
  • (RTL2) EventEmitter and states:
    • (RTL2a) Implements EventEmitter and emits events for state changes INITIALIZED, ATTACHING, ATTACHED, DETACHING, DETACHED, FAILED
    • (RTL2b) Channel#state attribute is the current state of the channel
    • (RTL2c) Additionally, an ERROR event is emitted that contains an ErrorInfo object with details on an error that has occurred for the Channel
  • (RTL3) Connection state change side effects:
    • (RTL3a) If the connection state changes to FAILED then an ATTACHING or ATTACHED channel state will transition to FAILED, set the Channel#errorReason and emit the error event
    • (RTL3b) If the connection state changes to CLOSED or SUSPENDED then an ATTACHING or ATTACHED channel state will transition to DETACHED
  • (RTL11) If a channel enters the DETACHED or FAILED state, then all messages that are still queued for send on that channel should be deleted from the queue triggering a failure for the publish or presence methods invoked for those messages
  • (RTL4) Channel#attach function:
    • (RTL4a) If already ATTACHED nothing is done
    • (RTL4h) If the channel is in a pending state DETACHING or ATTACHING, do the attach operation after the completion of the pending request
    • (RTL4g) If the channel is in the FAILED state, the attach request sets its errorReason to null, and proceeds with a channel attach described in RTL4b, RTL4h and RTL4c
    • (RTL4b) If the connection state is CLOSED, CLOSING, SUSPENDED or FAILED, the attach request results in an error
    • (RTL4h) If the connection state is INITIALIZED, CONNECTING or DISCONNECTED, do the operation once the connection state is CONNECTED
    • (RTL4c) Otherwise an ATTACH ProtocolMessage is sent to the server, the state changes to ATTACHING and the channel becomes ATTACHED when the confirmation ATTACHED ProtocolMessage is received
    • (RTL4f) Once an ATTACH ProtocolMessage is sent, if an ATTACHED ProtocolMessage is not received within the default realtime request timeout, the attach request should be treated as though it has failed and the channel will return to its previous state
    • (RTL4d) If the language permits, a callback can be provided that is called when the channel is attached successfully or the attach fails and the ErrorInfo error is passed as an argument to the callback
    • (RTL4e) If the user does not have sufficient permissions to attach to the channel, the channel state FAILED is emitted with the error ErrorInfo. An error event is also emitted, and the Channel#errorReason attribute of the Channel is set to the error ErrorInfo object
  • (RTL5) Channel#detach function:
    • (RTL5a) If the channel state is INITIALIZED or DETACHED nothing is done
    • (RTL5i) If the channel is in a pending state DETACHING or ATTACHING, do the detach operation after the completion of the pending request
    • (RTL5b) If the channel state is FAILED, the detach request results in an error
    • (RTL5g) If the connection state is CLOSING or FAILED, the detach request results in an error
    • (RTL5h) If the connection state is INITIALIZED, CONNECTING or DISCONNECTED, do the detach operation once the connection state is CONNECTED
    • (RTL5d) Otherwise a DETACH ProtocolMessage is sent to the server, the state changes to DETACHING and the channel becomes DETACHED when the confirmation DETACHED ProtocolMessage is received
    • (RTL5f) Once a DETACH ProtocolMessage is sent, if a DETACHED ProtocolMessage is not received within the default realtime request timeout, the detach request should be treated as though it has failed and the channel will return to its previous state
    • (RTL5e) If the language permits, a callback can be provided that is called when the channel is detached successfully or the detach fails and the ErrorInfo error is passed as an argument to the callback
  • (RTL6) Channel#publish function:
    • (RTL6a) Messages are encoded in the same way as the REST Channel#publish method
    • (RTL6b) An optional callback can be provided to the #publish method that is called when the message is successfully delivered or upon failure with the appropriate ErrorInfo error. A test should exist to publish lots of messages on a few connections to ensure all message success callbacks are called for all messages published
    • (RTL6i) Expects either an array of Message objects or a name string and data payload:
    • (RTL6i1) When name and data is provided, a single ProtocolMessage containing one Message is published to Ably
    • (RTL6i2) When an array of Message objects is provided, a single ProtocolMessage is used to publish all Message objects in the array. However, a yet to be implemented feature should limit the total number of messages bundled in a single ProtocolMessage based on the default max message size
    • (RTL6i3) Allows name and or data to be null. If any of the values are null, then key is not sent to Ably i.e. a payload with a null value for data would be sent as follows { "name": "click" }
    • (RTL6c) Connection and channel state conditions:
      • (RTL6c1) If the connection is CONNECTED and the channel is ATTACHED then the messages are published immediately
      • (RTL6c2) If the connection is INITIALIZED, CONNECTING or DISCONNECTED or the channel is INITIALIZED or ATTACHING, and ClientOptions#queueMessages has not been explicitly set to false, then the message will be queued and delivered as soon as the connection state becomes CONNECTED and the channel is ATTACHED
      • (RTL6c4) If the connection is SUSPENDED, CLOSING, CLOSED, or FAILED, or the channel is DETACHING, DETACHED or FAILED, the operation will result in an error
      • (RTL6c3) Implicitly attaches the Channel if the channel is in the INITIALIZED state. However, if the channel is in or moves to the DETACHED or FAILED state before the operation succeeds, it will result in an error
    • (RTL6d) Messages are delivered using a single ProtocolMessage where possible by bundling in all messages for that channel into the ProtocolMessage#messages array. However, a yet to be implemented feature should limit the total number of messages bundled per ProtocolMessage based on the default max message size, and would reject the publish and indicate an error if any single message exceeds that limit
    • (RTL6e) Unidentified clients using Basic Auth (i.e. any clientId is permitted as no clientId specified):
      • (RTL6e1) When a Message with a clientId value is published, Ably will accept and publish that message with the provided clientId. A test should assert that the clientId of the published Message is populated
    • (RTL6g) Identified clients with a clientId (as a result of either an explicitly configured clientId in ClientOptions, or implicitly through Token Auth):
      • (RTL6g1) When publishing a Message with the clientId attribute set to null:
        • (RTL6g1a) It is unnecessary for the client to set the clientId of the Message before publishing
        • (RTL6g1b) Ably will assign a clientId upon receiving the Message. A test should assert that the clientId value is populated for the Message when received
      • (RTL6g2) When publishing a Message with the clientId attribute value set to the identified client’s clientId, Ably will accept the message and publish it. A test should assert that the clientId value is populated for the Message when received
      • (RTL6g3) When publishing a Message with a different clientId attribute value from the identified client’s clientId, the client library should reject that publish operation immediately. The message should not be sent to Ably and it should result in an error, typically in the form of an error callback. The connection and channel must remain available for further operations
      • (RTL6g4) When using Token Auth, unless a clientId has been provided in ClientOptions or inferred following authentication, the client library is unidentified and will not be constrained when publishing messages with any explicit clientId. If a Message with a clientId value is published before the clientId is configured or inferred following authentication, the client library should not reject any explicit clientId specified in a message. A test should instance a library without an explicit clientId and an authCallback that returns a tokenDetails object with a clientId, then publish a message with the same clientId before authentication, and ensure that the message is published following authentication and received back with the clientId intact. A further test should follow the same sequence of events, but should instead use an incompatible clientId in the message, expecting that the message is rejected by the Ably service and the message error should contain the server error message, and the connection and channel should remain available for further operations
    • (RTL6h) Where the library language permits, the Channel#publish(name, data) method should provide an optional argument that allows the clientId value to be specified such as Channel#publish('event', 'data', { clientId: 'John' })
    • (RTL6f) Message#connectionId should match the current Connection#id for all published messages, a test should exist to ensure the connectionId for received messages matches that of the publisher
  • (RTL7) Channel#subscribe function:
    • (RTL7a) Subscribe with no arguments subscribes a listener to all messages
    • (RTL7b) Subscribe with a single name argument subscribes a listener to only messages whose name member matches the string name
    • (RTL7c) Implicitly attaches the Channel if the channel is in the INITIALIZED state. However, if the channel is in or moves to the DETACHED or FAILED state before the operation succeeds, it will result in the listener not being registered and an error being indicated, typically to the optional callback where the language permits
    • (RTL7d) Messages delivered are automatically decoded based on the encoding attribute; see REST Channel encoding features. If there is an error decoding a message, the message is still delivered, but in addition to sending an error message to the logger, an ErrorInfo error object is emitted as an error on the Channel. Tests should exist to publish and subscribe to encoded messages using the AES 128 and AES 256 fixture test data
    • (RTL7e) If a message cannot be decoded or decrypted successfully, it should be delivered to the listener with the encoding attribute set indicating the residual encoding state, and an error should be emitted on the channel
    • (RTL7f) A test should exist ensuring published messages are not echoed back to the subscriber when echoMessages is set to false in the Realtime library constructor
  • (RTL8) Channel#unsubscribe function:
    • (RTL8a) Unsubscribe with no arguments unsubscribes the provided listener to all messages if subscribed
    • (RTL8b) Unsubscribe with a single name argument unsubscribes the provided listener if previously subscribed with a name-specific subscription
  • (RTL9) Channel#presence attribute:
    • (RTL9a) Returns the Presence object for this channel
  • (RTL10) Channel#history function:
    • (RTL10a) Supports all the same params as REST Channel#history
    • (RTL10b) Additionally supports the param untilAttach, which if true, will only retrieve messages prior to the moment that the channel was attached. This bound is specified by passing the querystring param fromSerial with the serial number assigned to the channel in the ATTACHED ProtocolMessage. If the untilAttach param is specified when the channel is not attached, it results in an error
    • (RTL10c) Returns a PaginatedResult page containing the first page of messages in the PaginatedResult#items attribute returned from the history request
    • (RTL10d) A test should exist that publishes messages from one client, and upon confirmation of message delivery, a history request should be made on another client to ensure all messages are available
  • (RTL12) An attached channel may receive an additional ATTACHED ProtocolMessage from Ably at any point, however this is typically triggered following a transport being upgraded or resumed. If the ATTACHED message contains an error, the error should be emitted on the channel, the Channel#errorReason should be set, and the channel should remain attached. An ATTACHED state change event should not be emitted by the Channel if the Channel is already ATTACHED (i.e. no state change has occurred)

Presence

  • (RTP1) When a channel ATTACHED ProtocolMessage is received, the ProtocolMessage may contain a bit flag with value 1 indicating that there are currently members present on the channel. If members are present, subsequent ProtocolMessage SYNC messages will be delivered with presence members (messages) until the SYNC operation is complete. If there is no flag or the right most bit is zero, then the presence map should be considered in sync immediately as there are no members present on the channel at the time of attach
  • (RTP2) A PresenceMap should be used to maintain a list of members present on a channel. As there are no guarantees that during the SYNC phase presence events will arrive in order i.e. a leave event for a member can arrive before that member is later registered as as present as part of the initial SYNC operation. As such, until the SYNC operation is complete, timestamps must be recorded for all members to ensure the most recent present state is used, see the Java implementation. Once a SYNC event is complete, the members that are now considered ABSENT can be removed from the map, see the Java implementation
  • (RTP3) If a SYNC operation is underway but not yet complete, and the transport is disconnected unexpectedly, then if the connection is resumed successfully, it is the responsibility of the client library to complete the SYNC operation. The client library requests a SYNC resume by sending a SYNC ProtocolMessage with the last received sync serial number. See the Ruby implementation and the Ruby test
  • (RTP4) Ensure a test exists that enters 250 members using Presence#enterClient on a single connection, and checks for PRESENT events to be emitted on another connection for each member, and once sync is complete, all 250 members should be present in a Presence#get request
  • (RTP5) Channel state change side effects:
    • (RTP5a) If the channel enters the DETACHED or FAILED state then all queued presence messages will fail immediately, and the presence map is cleared
    • (RTP5b) If a channel enters the ATTACHED state then all queued presence messages will be sent immediately and a presence SYNC will be initiated implicitly
  • (RTP16) Connection state conditions:
    • (RTP16a) If the connection is CONNECTED and the channel is ATTACHED then all presence messages are published immediately
    • (RTP16b) If the connection is INITIALIZED, CONNECTING or DISCONNECTED or the channel is ATTACHING, and ClientOptions#queueMessages has not been explicitly set to false, then all presence messages will be queued and delivered as soon as the connection state returns to CONNECTED and the channel is ATTACHED
    • (RTP16c) Else publishing presence messages will result in an error
  • (RTP6) Presence#subscribe function:
    • (RTP6a) Subscribe with no arguments subscribes a listener to all presence messages
    • (RTP6b) Subscribe with a single action argument – such as ENTER, LEAVE, UPDATE or PRESENT – subscribes a listener to receive only presence messages with that action
    • (RTP6c) Implicitly attaches the Channel if the channel is in the INITIALIZED state. However, if the channel is in or moves to the FAILED state before the operation succeeds, it will result in the listener not being registered and an error being indicated, typically to the optional callback where the language permits
  • (RTP7) Presence#unsubscribe function:
    • (RTP7a) Unsubscribe with no arguments unsubscribes the listener if previously subscribed with an action-specific subscription
    • (RTP7b) Unsubscribe with a single action argument unsubscribes the provided listener to all presence messages for that action
  • (RTP8) Presence#enter function:
    • (RTP8a) Enters the current client into this channel, optionally with the data provided
    • (RTP8b) Optionally a callback can be provided that is called for both success or failure to enter
    • (RTP8c) A PRESENCE ProtocolMessage with a PresenceMessage with the action ENTER is sent to the Ably service. The clientId attribute of the PresenceMessage must not be present. Entering without an explicit PresenceMessage#clientId, implicitly uses the clientId for the current connection
    • (RTP8d) Implicitly attaches the Channel if the channel is in the INITIALIZED state. However, if the channel is in the DETACHED or FAILED state, the enter request results in an error
    • (RTP8e) Optional data can be included when entering a channel that will be encoded / decoded as with normal messages. A test should exist to ensure data used with enter is encoded & decoded correctly. Also, when data is provided when entering, but no data is provided when leaving, the data attribute should be emitted in the LEAVE event for this client
    • (RTP8f) If the client library is authenticated but unidentified (i.e. clientId is a wildcard '*' or client is anonymous), the enter request results in an error immediately
    • (RTP8g) If the channel is DETACHED or FAILED, the enter request results in an error immediately
    • (RTP8h) If the Ably service determines that the client does not have required presence permission, a NACK is sent to the client resulting in an error
    • (RTP8i) If the Ably service determines that the client is unidentified, a NACK is sent to the client resulting in an error
  • (RTP9) Presence#update function:
    • (RTP9a) Updates the data for the present member with a value or null
    • (RTP9b) If the client was not already entered, it enters this client into this channel
    • (RTP9c) Optionally a callback can be provided that is called for both success or failure to update
    • (RTP9d) A PRESENCE ProtocolMessage with a PresenceMessage with the action UPDATE is sent to the Ably service. The clientId attribute of the PresenceMessage must not be present. Updating without an explicit PresenceMessage#clientId, implicitly uses the clientId for the current connection
    • (RTP9e) In all other ways, this method is identical to Presence#enter and should have matching tests
  • (RTP10) Presence#leave function:
    • (RTP10a) Leaves this client from the channel and the data will be updated with the value provided. If the language permits the data argument to be omitted, then the previously set data value will be sent as a convenience
    • (RTP10b) Optionally a callback can be provided that is called for both success or failure to leave
    • (RTP10c) A PRESENCE ProtocolMessage with a PresenceMessage with the action LEAVE is sent to the Ably service. The clientId attribute of the PresenceMessage must not be present. Leaving without an explicit PresenceMessage#clientId, implicitly uses the clientId for the current connection
    • (RTP10d) If the client is not currently ENTERED, it will result in an error
    • (RTP10e) In all other ways, this method is identical to Presence#enter and should have matching tests
  • (RTP11) Presence#get function:
    • (RTP11a) Returns the list of current members on the channel in a callback. By default, will wait for the SYNC to be completed, see RTP11c1
    • (RTP11b) Implicitly attaches the Channel if the channel is in the INITIALIZED state. However, if the channel is in or moves to the DETACHED or FAILED state before the operation succeeds, it will result in an error
    • (RTP11c) An optional set of params can be provided:
      • (RTP11c1) waitForSync (default true). When true, method will wait until SYNC is complete before returning a list of members. When false, known set of presence members is returned immediately, which may be incomplete if the SYNC is not finished
      • (RTP11c2) clientId filters members by the provided clientId
      • (RTP11c3) connectionId filters members by the provided connectionId
  • (RTP12) Presence#history function:
    • (RTP12a) Supports all the same params as REST Presence#history
    • (RTP12b) Additionally supports the param untilAttach, which if true, will only retrieve messages up to the moment that the channel was attached. This bound is specified by passing the querystring param fromSerial with the serial number assigned to the channel in the ATTACHED ProtocolMessage. If the untilAttach param is specified when the channel is not attached, it will result in an error
    • (RTP12c) Returns a PaginatedResult page containing the first page of messages in the PaginatedResult#items attribute returned from the history request
    • (RTP12d) A test should exist that registers presence with a few clients, and upon confirmation of entering the channel for all clients, a presence history request should be made using another client to ensure all presence events are available
  • (RTP13) Presence#syncComplete returns true if the initial SYNC operation has completed for the members present on the channel
  • (RTP14) Presence#enterClient function:
    • (RTP14a) Enters into presence on a channel on behalf of another clientId. This allows a single client with suitable permissions to register presence on behalf of any number of clients using a single connection
    • (RTP14b) Optionally a callback can be provided that is called for both success or failure to enter
    • (RTP14c) Data can optionally be provided when entering and will follow the normal encoding & decoding rules
    • (RTP14d) A test should exist that registers a number of members each with a different clientId on a presence channel, and then a Presence#get should be used to verify that all members are present as expected
  • (RTP15) Presence#enterClient Presence#updateClient and Presence#leaveClient function:
    • (RTP15a) Performs an enter, update or leave for given clientId. These methods apply if the Realtime library was not initialized with a specific clientId. This allows a single client with suitable permissions to update presence on behalf of any number of clients using a single connection. Otherwise these are functionality equivalent to the corresponding enter, update and leave methods, and equivalent test coverage should be provided
    • (RTP15b) Tests should use enterClient, updateClient and leaveClient for many members from one Realtime client and check that the operations are reflected in the presence map and the expected events are emitted on a separate client
    • (RTP15c) Tests should also ensure that using these methods has no side effects on a client that has entered normally using Presence#enter
    • (RTP15d) A callback can be provided that will be called upon success or failure
    • (RTP15e) Implicitly attaches the Channel if the channel is in the INITIALIZED state. However, if the channel is in or moves to the DETACHED or FAILED state before the operation succeeds, it will result in an error
    • (RTP15f) If the client is identified and has a valid clientId, and the clientId argument does not match the client’s clientId, then it should indicate an error. The connection and channel remain available for further operations

EventEmitter mixin / interface

  • (RTE1) EventEmitter is a generic interface for event registration and delivery used in a number of the types in the Realtime client library. For example, the Connection object emits events for connection state using the EventEmitter pattern
  • (RTE2) Where objects provide subscribe or unsubscribe methods, they should follow the specification for the EventEmitter#on and EventEmitter#off methods respectively
  • (RTE3) EventEmitter#on registers the provided listener for either all events when no event argument is provided, or for only a single named event when an event argument is provided. If on is called more than once with the same listener and event, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using on, and an event is emitted once, the listener would be invoked twice
  • (RTE4) EventEmitter#once registers the provided listener for either the first event that is emitted when no event argument is provided, or for only the first occurrence of a single named event when an event argument is provided. If once is called more than once with the same listener, the listener is added multiple times to its listener registry. Therefore, as an example, assuming the same listener is registered twice using once, and an event is emitted once, the listener would be invoked twice. However, all subsequent events emitted would not invoke the listener as once ensures that each registration is only invoked once
  • (RTE5) EventEmitter#off deregisters a listener. If called with a specific event and a listener, it removes all registrations that match both the given listener and the given event; if called only with a listener, it removes all registrations matching the given listener, regardless of whether they are associated with an event or not; if called with no arguments, it removes all registrations, for all events and listeners
  • (RTE6) EventEmitter#emit emits an event, calling registered listeners with the given event name and any other given arguments. If an exception is raised in any of the listeners, the exception is caught by the EventEmitter and the exception is logged to the Ably logger. Tests must exist to ensure exceptions raised in client code do not propagate and inhibit other event processing within the client library

State conditions and operations

Connection.state effects on realtime operations

Initialized Connecting Connected Disconnected Suspended Closing Closed Failed
connect RTN11a No-op No-op RTN11c RTN11c RTN11b RTN11a RTN11d
close No-op RTN12f RTN12a RTN12d RTN12d No-op No-op No-op
ping RTN13b RTN13c RTN13a RTN13c RTN13b RTN13b RTN13b RTN13b
Channel attach RTL4h RTL4h See channel states table RTL4h RTL4b RTL4b RTL4b RTL4b
Channel detach RTL5h RTL5h See channel states table RTL5h See channel states table RTL5g See channel states table RTL5g
Channel publish RTL6c2 RTL6c2 See channel states table RTL6c2 RTL6c4 RTL6c4 RTL6c4 RTL6c4
Presence ops. RTP16b RTP16b See channel states table RTP16b RTP16c RTP16c RTP16c RTP16c

RealtimeChannel.state effects on channel operations

Initialized Attaching Attached Detaching Detached Failed
attach RTL4c RTL4h RTL4a RTL4h RTL4c RTL4g
detach RTL5h RTL5i RTL5d RTL5i RTL5a RTL5b
publish RTL6c2 RTL6c2 RTL6c1 RTL6c4 RTL6c4 RTL6c3
Presence ops. RTP16b RTP16b RTP16a RTP16c RTP16c RTP16c

Types

Data types

Message

  • (TM1) A Message represents an individual message to be sent or received via the Ably Realtime service. See the Ruby Message documentation, but bear in mind the attributes following underscore naming in Ruby
  • (TM2) Attributes available in a Message, see the Ruby Message documentation for an explanation of each attribute:
    • (TM2a) id string – unique ID for this message
    • (TM2b) clientId string
    • (TM2c) connectionId string
    • (TM2g) name string
    • (TM2d) data string, buffer or JSON-encodable object or array
    • (TM2e) encoding string
    • (TM2f) timestamp time in milliseconds since epoch

PresenceMessage

  • (TP1) A PresenceMessage represents an individual presence message to be sent or received via the Ably Realtime service. See the Ruby PresenceMessage documentation, but bear in mind the attributes following underscore naming in Ruby
  • (TP2) PresenceMessage Action enum has the following values in order from zero: ABSENT, PRESENT, ENTER, LEAVE, UPDATE
  • (TP3) Attributes available in a PresenceMessage, see the Ruby PresenceMessage documentation for an explanation of each attribute:
    • (TP3a) id string – unique ID for this message
    • (TP3b) action enum
    • (TP3c) clientId string
    • (TP3d) connectionId string
    • (TP3e) data string, buffer or JSON-encodable object or array
    • (TP3f) encoding string
    • (TP3g) timestamp time in milliseconds since epoch
    • (TP3h) memberKey string function that combines the connectionId and clientId ensuring multiple connected clients with the same clientId are uniquely identifiable

ProtocolMessage

  • (TR1) A ProtocolMessage represents the type used to send and receive messages over the Realtime protocol. A ProtocolMessage always relates either to the connection or to a single channel only, but can contain multiple individual Messages or PresenceMessages. See the Ruby ProtocolMessage documentation, but bear in mind the attributes following underscore naming in Ruby
  • (TR2) ProtocolMessage Action enum has the following values in order from zero: HEARTBEAT, ACK, NACK, CONNECT, CONNECTED, DISCONNECT, DISCONNECTED, CLOSE, CLOSED, ERROR, ATTACH, ATTACHED, DETACH, DETACHED, PRESENCE, MESSAGE, SYNC
  • (TR3) ProtocolMessage Flag enum has the following values in order from zero: HAS_PRESENCE, HAS_BACKLOG
  • (TR4) Attributes available in a ProtocolMessage, see the Ruby ProtocolMessage documentation for an explanation of each attribute:
    • (TR4a) action enum
    • (TR4n) id string
    • (TR4b) channel string
    • (TR4c) channelSerial string
    • (TR4d) connectionId string
    • (TR4e) connectionKey string. Note that this field is soon to be deprecated; when ConnectionDetails#connectionKey is present, it should be considered the definitive connectionKey for the current connection
    • (TR4f) connectionSerial long
    • (TR4o) connectionDetails ConnectionDetails object – provides details on the constraints or defaults for the connection such as max message size, client ID or connection state TTL
    • (TR4g) count integer
    • (TR4h) error ErrorInfo object
    • (TR4i) flags integer
    • (TR4j) msgSerial long
    • (TR4k) messages Array of Message objects
    • (TR4l) presence Array of PresenceMessage objects
    • (TR4m) timestamp time in milliseconds since epoch

PaginatedResult

  • (TG1) A PaginatedResult is a type that represents a page of results from a paginated query. The response is accompanied by metadata that indicates the relative queries available
  • (TG2) PaginatedResult wraps all message and presence history, stats and REST presence requests. Instancing this type should not result in an error if paging headers are not returned from the REST API
  • (TG3) PaginatedResult#items attribute contains a page of results (for example an Array of Message objects for a channel history request)
  • (TG4) PaginatedResult#next function returns a new PaginatedResult loaded with the next page of results. If there are no further pages, then null is returned
  • (TG5) PaginatedResult#first function returns a new PaginatedResult for the first page of results
  • (TG6) PaginatedResult#hasNext function returns true if there are further pages
  • (TG7) PaginatedResult#isLast function returns true if this page is the last page i.e. !hasNext

TokenRequest

  • (TE1) TokenRequest is a type containing the token request details sent to the REST requestToken endpoint
  • (TE2) String attributes keyName, clientId, nonce and mac
  • (TE3) capability is a string attribute containing capabilities JSON stringified
  • (TE5) timestamp long – The timestamp (in milliseconds since the epoch) of this request. Timestamps, in conjunction with the nonce, are used to prevent requests from being replayed
  • (TE4) ttl attribute represents time to live (expiry) of this token in milliseconds

TokenDetails

  • (TD1) TokenDetails is a type containing the token request response from the REST requestToken endpoint
  • (TD2) TokenDetails#token attribute contains the token string
  • (TD3) TokenDetails#expires attribute contains the expiry time in milliseconds. Where idiomatic in the language, this can be a Date/Time object
  • (TD4) TokenDetails#issued attribute contains the time the token was issued in milliseconds. Where idiomatic in the language, this can be a Date/Time object
  • (TD5) TokenDetails#capability attribute contains the capability JSON stringified
  • (TD6) TokenDetails#clientId attribute contains the clientId assigned to the token. If clientId is null or omitted, then the token is prohibited from assuming a clientId in any operations, however if clientId is a wildcard string '*', then the token is permitted to assume any clientId. Any other string value for clientId implies that the clientId is both enforced and assumed for all operations for this token

Stats

ErrorInfo

  • (TI1) Provides a generic Ably ErrorInfo object that contains Ably code, statusCode (analogous to HTTP status code) and message attributes
  • (TI2) Errors returned from the Ably server are compatible with the ErrorInfo structure and should result in errors that inherit from ErrorInfo
  • (TE3) Ably-common should be included as a submodule so that consistent error codes can be used

ConnectionStateChange

  • (TA1) Whenever the connection state changes, a ConnectionStateChange object is emitted on the Connection object
  • (TA2) The ConnectionStateChange object contains the current state in attribute current, the previous state in attribute previous, and when the client is not connected and a connection attempt will be made automatically by the library, the amount of time in milliseconds until the next retry in the attribute retryIn
  • (TA3) If the connection state change includes error information, then the reason attribute will contain an ErrorInfo object describing the reason for the error
  • (TA4) See the Java library implementation of this object

Capability – API not defined yet

  • (TC1) This type represents a capability for a key or token
  • (TC2) For now a string representation of the JSON will suffice wherever capability is used

ConnectionDetails

  • (CD1) Connection details are optionally passed to the client library in the CONNECTED ProtocolMessage#connectionDetails attribute to inform the client about any constraints it should adhere to, and provide additional metadata about the connection. For example, if a request is made to publish a message that exceeds the maxMessageSize, the client library can reject the message immediately, without communicating with the Ably service
  • (CD2) Attributes available in ConnectionDetails:
    • (CD2a) clientId contains the client ID assigned to the token. If clientId is null or omitted, then the client is prohibited from assuming a clientId in any operations, however if clientId is a wildcard string '*', then the client is permitted to assume any clientId. Any other string value for clientId implies that the clientId is both enforced and assumed for all operations from this client
    • (CD2b) connectionKey is the connection secret key string that is used to resume a connection and its state. When present, this connectionKey should be considered the definitive connectionKey for the current connection and the soon to be deprecated ProtocolMessage#connectionKey should be ignored
    • (CD2c) maxMessageSize is the maximum individual message size in bytes
    • (CD2d) maxFrameSize is the maximum size for a single frame of data sent to Ably. This restriction applies to a ProtocolMessage sent over a realtime connection, or the total body size for a REST request
    • (CD2e) maxInboundRate is the maximum allowable number of requests per second from a client or Ably. In the case of a realtime connection, this restriction applies to the number of ProtocolMessage objects sent, whereas in the case of REST, it is the total number of REST requests per second
    • (CD2f) connectionStateTtl is the duration that Ably will persist the connection state when a Realtime client is abruptly disconnected
    • (CD2g) serverId string is a unique identifier for the front-end server that the client has connected to. This server ID is only used for the purposes of debugging

Option types

ClientOptions

  • (TO1) Ably library options used when instancing a REST or Realtime client library, see Java ClientOptions which extends Java AuthOptions as a reference
  • (TO2) Note: ClientOptions does not currently define a default for max message size or request size. This will be added in the future to ensure that REST requests does not exceed the limits before the request is made to the server. In the case of realtime, the connection constraints will be sent to the client in the initial CONNECTED ProtocolMessage
  • (TO3) The attributes of ClientOptions consist of:
    • (TO3a) clientId string – the id of the client represented by this instance
    • (TO3b) logLevel – controls the level of verbosity of log messages from the library. The implementation of this is likely to vary by platform
    • (TO3c) logHandler – allows the client to intercept log messages and handle them in a client-specific way. The implementation of this is likely to vary by platform
    • (TO3d) tls boolean – defaults to true. If false, will not use TLS for all connections
    • (TO3e) autoConnect boolean – defaults to true. If false, suppresses the automatic initiation of a connection when the library is instanced
    • (TO3f) useBinaryProtocol boolean – defaults to true. If false, forces the library to use the JSON encoding for REST and Realtime operations, instead of the default binary msgpack encoding
    • (TO3g) queueMessages boolean – defaults to true. If false, suppresses the default queueing of messages when connection states that anticipate imminent connection (connecting and disconnected). Instead, publish and presence state changes will fail immediately if not in the connected state
    • (TO3h) echoMessages boolean – defaults to true. If false, suppresses messages originating from this connection being echoed back on the same connection
    • (TO3i) recover string – A connection recovery string, specified with the intention of inheriting the state of an earlier connection
    • (TO3j) Auth option attributes:
      • (TO3j1) key string – Full Ably key string as obtained from dashboard
      • (TO3j2) token string – An authentication token string issued for this application
      • (TO3j3) tokenDetails TokenDetails – An authentication token issued for this application
      • (TO3j4) useTokenAuth boolean – When true, token authentication will always be used by the client. If clientId is unspecified, then the token issued will inherently be anonymous i.e. it will contain an empty clientId
      • (TO3j5) authCallback – A callback to call to obtain a signed TokenRequest, TokenDetails or a token string. This enables a client to obtain token requests or tokens from another entity, so tokens can be renewed without the client requiring a key
      • (TO3j6) authUrl string – A URL to query to obtain a signed TokenRequest, TokenDetails or a token string. This enables a client to obtain token request or token from another entity, so tokens can be renewed without the client requiring a key
      • (TO3j7) authMethod – The HTTP verb to be used when a request is made by the library to the authUrl. Defaults to GET, supports GET and POST
      • (TO3j8) authHeaders – Headers to be included in any request made by the library to the authUrl
      • (TO3j9) authParams – Additional params to be included in any request made by the library to the authUrl, either as query params added to the URL in the case of GET, or form-encoded in the body in the case of POST
      • (TO3j10) queryTime – If true, the library will query the Ably system for the current time instead of relying on a locally-available time of day
      • (TO3j11) defaultTokenParams – When a TokenParams object is provided, it will override the client library defaults described in TokenParams
    • (TO3k) Development environment attributes:
      • (TO3k1) environment string – for development environments only; allows a non-default Ably environment to be used such as sandbox
      • (TO3k2) restHost string – for development environments only; allows a non-default Ably REST host to be specified. It is never valid to provide both a restHost and environment value
      • (TO3k3) realtimeHost string – for development environments only; allows a non-default Ably Realtime host to be specified. It is never valid to provide both a realtimeHost and environment value
      • (TO3k4) port integer – for development environments only; allows a non-default Ably non-TLS port to be specified
      • (TO3k5) tlsPort integer – for development environments only; allows a non-default Ably TLS port to be specified
    • (TO3l) The follow attributes, if set, are used to change the default behaviour of the library:
      • (TO3l1) disconnectedRetryTimeout integer – default 15,000 (15s). When the connection enters the DISCONNECTED state, after this delay in milliseconds, if the state is still DISCONNECTED, the client library will attempt to reconnect automatically
      • (TO3l2) suspendedRetryTimeout integer – default 30,000 (30s). When the connection enters the SUSPENDED state, after this delay in milliseconds, if the state is still SUSPENDED, the client library will attempt to reconnect automatically
      • (TO3l3) httpOpenTimeout integer – default 4,000 (4s). Timeout for opening the connection, available in the client library if supported by the transport
      • (TO3l4) httpRequestTimeout integer – default 15,000 (15s). Timeout for any single HTTP request and response
      • (TO3l5) httpMaxRetryCount integer – default 3. Max number of fallback hosts to use as a fallback when an HTTP request to the primary host is unreachable or indicates that it is unserviceable
      • (TO3l6) httpMaxRetryDuration integer – default 10,000 (10s). Max elapsed time in which fallback host retries for HTTP requests will be attempted i.e. if the first default host attempt takes 5s, and then the subsequent fallback retry attempt takes 7s, no further fallback host attempts will be made as the total elapsed time of 12s exceeds the default 10s limit

TokenParams

  • (TK1) A class providing parameters of a token request. These params are used when invoking Auth#authorise, Auth#requestToken and Auth#createTokenRequest
  • (TK2) The attributes of TokenParams consist of:
  • (TK2a) ttl long – Requested time to live for the token in milliseconds. When omitted, the REST API default of 60 minutes is applied by Ably
  • (TK2b) capability string – Capability requirements JSON stringified for the token. When omitted, the REST API default to allow all operations is applied by Ably, with the string value {"*":["*"]}
  • (TK2c) clientId string – A clientId string to associate with this token. If clientId is null or omitted, then the token is prohibited from assuming a clientId in any operations, however if clientId is a wildcard string '*', then the token is permitted to assume any clientId. Any other string value for clientId implies that the clientId is both enforced and assumed for all operations for this token
  • (TK2d) timestamp long – The timestamp (in milliseconds since the epoch) of this request. Timestamps, in conjunction with the nonce, are used to prevent requests from being replayed. timestamp is a “one-time” value, and is valid in a request, but is not validly a member of any default token params such as ClientOptions#defaultTokenParams

AuthOptions

  • (AO1) A class providing configurable authentication options used when authenticating or issuing tokens explicitly. These options are used when invoking Auth#authorise, Auth#requestToken and Auth#createTokenRequest
  • (AO2) The attributes of AuthOptions consist of:
    • (AO2a) key string – Full Ably key string, as obtained from dashboard, used when signing token requests locally
    • (AO2b) authCallback – A callback to call to obtain a signed TokenRequest, TokenDetails or a token string. This enables a client to obtain token requests or tokens from another entity, so tokens can be renewed without the client requiring a key
    • (AO2c) authUrl string – A URL to query to obtain a signed TokenRequest, TokenDetails or a token string. This enables a client to obtain token request or token from another entity, so tokens can be renewed without the client requiring a key
    • (AO2d) authMethod – The HTTP verb to be used when a request is made by the library to the authUrl. Defaults to GET, supports GET and POST
    • (AO2e) authHeaders – Headers to be included in any request made by the library to the authUrl
    • (AO2f) authParams – Additional params to be included in any request made by the library to the authUrl, either as query params in the case of GET, or form-encoded in the body in the case of POST
    • (AO2g) queryTime – If true, the library will query the Ably system for the current time instead of relying on a locally-available time of day
    • (AO2h) force – when true, indicates that a new token should be requested

ChannelOptions

  • (TB1) options provided when instancing a channel, see Java ChannelOptions as a reference
  • (TB2) The attributes of ChannelOptions consist of:
    • (TB2b) cipher, which is either:
      • (TB2b1) A CipherParams instance, or
      • (TB2b2) an options hash (or language equivalent) consisting of any subset of CipherParams fields that includes a key. In this case, the client library should call `getDefaultParams`, passing it the options hash, to obtain a CipherParams instance
  • (TB3) The client lib may optionally provide an alternative constructor withCipherKey for ChannelOptions that takes a key only. (This must be differentiated from the normal constructor such that it is clear that the value being passed in is a key). (This is intended for languages where requiring a hash map is unidiomatic)

CipherParams

  • (TZ1) params to configure encryption for a channel, see Java CipherParams class as a reference
  • (TZ2) The attributes of CipherParams consist of anything necessary to implement the supported algorithms, in addition to the following standardised attributes:
    • (TZ2a) algorithm string – Default is AES. Optionally specify the algorithm to use for encryption, currently only AES is supported
    • (TZ2b) keyLength integer – the length in bits of the key
    • (TZ2d) key binary – private key used to encrypt and decrypt payloads
    • (TZ2c) mode string – Default is CBC. Optionally specify cipher mode, currently only CBC is supported

Client Library defaults

The following default values are configured for the client library:

  • (DF1) Realtime defaults:
    • (DF1a) connectionStateTtl integer – default 60s. The duration that Ably will persist the connection state when a Realtime client is abruptly disconnected. When the client is in the DISCONNECTED state, once this TTL has passed, the client should change the state to the SUSPENDED state signifying that the state is now lost i.e. channels need to be re-attached manually. Note that this default is overriden by connectionStateTtl, if specified in the ConnectionDetails of the CONNECTED ProtocolMessage
    • (DF1b) realtimeRequestTimeout – default 10s. When a realtime client library is establishing a connection with Ably, or sending a HEARTBEAT, CONNECT, ATTACH, DETACH or CLOSE ProtocolMessage to Ably, this is the amount of time that the client library will wait before considering that request as failed and triggering a suitable failure condition

Interface Definition

The following bespoke IDL (Interface Definition Language) describes the types and classes present in the Rest and Realtime client libraries.

Please note the following conventions:

  • Types are capitalized
  • Attribute and method names are lowercase
  • Attributes are denoted as attributeName: Type
  • Instance methods are denoted as methodName(argName: Type, argName: Type) -> ReturnType (ReturnType may be omitted)
  • Callback functions / closures are denoted as (argName: Type, argName: Type) -> ReturnType (ReturnType is usually omitted)
  • Values produced by I/O (e.g. a request) are prefixed with => io. In some platforms (JS) those values are passed as arguments to a callback argument, instead of being returned. I/O always can fail, but how do they fail is undefined in the spec, so it’s also undefined here
  • Enums are denoted as .A | .B | .C
  • Type? denotes a nullable type
  • Type default value denotes that the thing being annotated with Type has value as default. Type api-default value denotes that the Ably server API uses those defaults and therefore it is unnecessary for the client library to send these default values to the API
  • Class fields (as opposed to instance fields) are prefixed with a +
  • Duration and Time types are typically represented as milliseconds since the epoch. Where needed, a more idiomatic language specific duration may be used such as seconds or Time respectively for Ruby
  • Data is a message payload type, see RSL4a for a list of supported payload types
class Rest:
  constructor(keyStr: String) // RSC1
  constructor(tokenStr: String) // RSC1
  constructor(ClientOptions) // RSC1
  auth: Auth // RSC5
  channels: Channels<RestChannel> // RSN1
  stats(
    start: Time, // RSC6b1
    end: Time api-default now(), // RSC6b1
    direction: .Backwards | .Forwards api-default .Backwards, // RSC6b2
    limit: int api-default 100, // RSC6b3
    unit: .Minute | .Hour | .Day | .Month api-default .Minute // RSC6b4
  ) => io PaginatedResult<Stats> // RSC6a
  time() => io Time // RSC16

class Realtime:
  constructor(keyStr: String) // RSC1
  constructor(tokenStr: String) // RSC1
  constructor(ClientOptions) // RSC1
  auth: Auth // RTC4
  channels: Channels<RealtimeChannel> // RTC3, RTS1
  clientId: String? // proxy for RSA7
  connection: Connection // RTC2
  stats: // Same as Rest.stats, RTC5a
  close() // proxy for RTN12
  connect() // proxy for RTN11
  time() => io Time // RTC6a

class ClientOptions:
  embeds AuthOptions // TO3j
  autoConnect: Bool default true // RTC1b, TO3e
  clientId: String? // RSC17, RSA4, RSA15, TO3a
  defaultTokenParams: TokenParams? // TO3j11
  echoMessages: Bool default true // RTC1a, TO3h
  environment: String? // RSC15b, TO3k1
  logHandler: // platform specific - TO3c
  logLevel: // platform specific - TO3b
  port: Int default 80 // TO3k4
  queueMessages: Bool default true // RTP16b, TO3g
  restHost: String default "rest.ably.io" // RSC12, TO3k2
  realtimeHost: String default "realtime.ably.io" // RTC1d, TO3k3
  recover: String? // RTC1c, TO3i
  tls: Bool default true // RSC18, TO3d
  tlsPort: Int default 443 // TO3k5
  useBinaryProtocol: Bool default true // TO3f
  // configurable retry and failure defaults
  disconnectedRetryTimeout: Duration default 15s // TO311
  suspendedRetryTimeout: Duration default 30s // RTN14d, TO312
  httpOpenTimeout: Duration default 4s // TO313
  httpRequestTimeout: Duration default 15s // TO314
  httpMaxRetryCount: Int default 3 // TO315
  httpMaxRetryDuration: Duration default 10s // TO315

class AuthOptions: // RSA8e
  authCallback: (String | TokenDetails | TokenRequest) ->? // RSC14c, RSA4, TO3j5, AO2b
  authHeaders: [(String, String)]? // RSA8c3, TO3j8, AO2e
  authMethod: .GET | .POST default .GET // RSA8c, TO3j7, AO2d
  authParams: [String: [String]]? // RSA8c3, RSA8c1, TO3j9, AO2f
  authUrl: String? // RSC14c, RSA4, RSA8c, TO3j6, AO2c
  force: Bool default false // RSA10d, AO2h
  key: String? // RSC14a, RSA14, TO3j1, AO2a
  queryTime: Bool default false // RSA9d, TO3j10, AO2a
  token: String? | TokenDetails? // RSC14c, RSA4, TO3j2
  tokenDetails: TokenDetails? // RSC14c, RSA4, TO3j3
  useTokenAuth: Bool? // RSA4, RSA14, TO3j4

class TokenParams: // RSAA8e
  capability: String api-default '{"*":["*"]}' // RSA9f, TK2b
  clientId: String? // TK2c
  nonce: String? // RSA9c, Tk2d
  timestamp: Time? // RSA9d, Tk2d
  ttl: Duration api-default 60min // RSA9e, TK2a

class Auth:
  clientId: String? // RSA7, RSC17, RSA12
  authorise(TokenParams?, AuthOptions?) => io TokenDetails // RSA10
  createTokenRequest(TokenParams?, AuthOptions?) => io TokenRequest // RSA9
  requestToken(TokenParams?, AuthOptions?) => io TokenDetails // RSA8e

class TokenDetails:
  capability: String // TD5
  clientId: String? // TD6
  expires: Time // TD3
  issued: Time // TD4
  token: String // TD2

class TokenRequest:
  capability: String // TE3
  clientId: String? // TE2
  keyName: String // TE2
  mac: String // TE2
  nonce: String // TE2
  timestamp: Time? // TE5
  ttl: Duration? api-default 60min // TE4

class Channels<ChannelType>:
  exists(String) -> Bool // RSN2, RTS2
  get(String) -> ChannelType // RSN3a, RTS3a
  get(String, ChannelOptions) -> ChannelType // RSN3c, RTS3c
  iterate() -> Iterator<ChannelType> // RSN2, RTS2
  release(String) // RSN4, RTS4

class RestChannel:
  name: String?
  presence: RestPresence // RSL3
  history(
    start: Time, // RSL2b1
    end: Time api-default now(), // RSL2b1
    direction: .Backwards | .Forwards api-default .Backwards, // RSL2b2
    limit: int api-default 100 // RSL2b3
  ) => io PaginatedResult<Message> // RSL2a
  publish([Message]) => io // RSL1
  publish(name: String?, data: Data?) => io // RSL1
  publish(name: String?, data: Data?, clientId: String) => io // RSL1h

class RealtimeChannel:
  embeds EventEmitter<ChannelEvent, ErrorInfo?> // RTL2
  errorReason: ErrorInfo? // RTL4e
  state: ChannelState // RTL2b
  presence: RealtimePresence // RTL9
  attach() => io // RTL4d
  detach() => io // RTL5e
  history(
    start: Time, // RTL10a
    end: Time api-default now(), // RTL10a
    direction: .Backwards | .Forwards api-default .Backwards, // RTL10a
    limit: int api-default 100, // RTL10a
    untilAttach: Bool default false // RTL10b
  ) => io PaginatedResult<Message> // RSL2a
  publish([Message]) => io // RTL6i
  publish(name: String?, data: Data?) => io // RTL6i
  publish(name: String?, data: Data?, clientId: String) => io // RTL6h
  subscribe((Message) ->) => io // RTL7a
  subscribe(String, (Message) ->) => io // RTL7b
  unsubscribe() // RTL8a, RTE5
  unsubscribe((Message) ->) // RTL8a
  unsubscribe(String, (Message) ->) // RTL8a

enum ChannelState:
  INITIALIZED
  ATTACHING
  ATTACHED
  DETACHING
  DETACHED
  FAILED

enum ChannelEvent:
  embeds ChannelState
  ERROR // RTL2c

class ChannelOptions:
  +withCipherKey(key: Binary | String)? -> ChannelOptions // TB3
  cipher: (CipherParams | Params)? // RSL5a, TB2b

class CipherParams:
  algorithm: String default "AES" // TZ2a
  key: Binary // TZ2d
  keyLength: Int // TZ2b
  mode: String default "CBC" // TZ2c

class Crypto:
  +getDefaultParams(Params) -> CipherParams // RSE1
  +generateRandomKey(keyLength: Int?) => io Binary // RSE2

class RestPresence:
  get(
    limit: int api-default 100, // RSP3a
    clientId: String?, // RSP3a2
    connectionId: String?, // RSP3a3
  ) => io PaginatedResult<PresenceMessage> // RSPa
  history(
    start: Time, // RSP4b1
    end: Time api-default now(), // RSP4b1
    direction: .Backwards | .Forwards api-default .Backwards, // RSP4b2
    limit: int api-default 100, // RSP4b3
  ) => io PaginatedResult<PresenceMessage> // RSP4a

class RealtimePresence:
  syncComplete: Bool // RTP13
  get(
    waitForSync: Bool default true, // RTP11c1
    clientId: String?, // RTP11c2
    connectionId: String?, // RTP11c3
  ) => io [PresenceMessage] // RTP11
  history(
    start: Time, // RTP12a
    end: Time, // RTP12a
    direction: .Backwards | .Forwards api-default .Backwards, // RTP12a
    limit: int api-default 100, // RTP12a
    untilAttach: Bool default false // RTP12b
  ) => io PaginatedResult<PresenceMessage> // RTP12c
  subscribe((PresenceMessage) ->) => io // RTP6a
  subscribe(PresenceAction, (PresenceMessage) ->) => io // RTP6b
  unsubscribe() // RTP7a, RTE5
  unsubscribe((PresenceMessage) ->) // RTP7a
  unsubscribe(PresenceAction, (PresenceMessage) ->) // RTP7b
  // presence state modifiers
  enter(Data?) => io // RTP8
  update(Data?) => io // RTP9
  leave(Data?) => io // RTP10
  enterClient(clientId: String, Data?) => io // RTP4, RTP14, RTP15
  updateClient(clientId: String, Data?) => io // RTP15
  leaveClient(clientId: String, Data?) => io // RTP15

enum PresenceAction:
  ABSENT // TP2
  PRESENT // TP2
  ENTER // TP2
  LEAVE // TP2
  UPDATE // TP2

class ConnectionDetails:
  clientId: String? // RSA12a, CD2a
  connectionKey: String // RTN15e, CD2b
  connectionStateTtl: Duration // CD2f, RTN14e, DF1a
  maxFrameSize: Int // CD2d
  maxInboundRate: Int // CD2e
  maxMessageSize: Int // CD2c
  serverId: String // CD2g

class Message:
  constructor(name: String?, data: Data?) // TM2
  constructor(name: String?, data: Data?, clientId: String?) // TM2
  clientId: String? // RSL1g1, TM2b
  connectionId: String? // TM2c
  data: Data? // TM2d
  encoding: String? // TM2e
  id: String // TM2a
  name: String? // TM2g
  timestamp: Time // TM2f

class PresenceMessage
  action: PresenceAction // TP3b
  clientId: String // TP3c
  connectionId: String // TP3d
  data: Data? // TP3e
  encoding: String? // TP3f
  id: String // TP3a
  timestamp: Time // TP3g
  memberKey() -> String // TP3h

class ProtocolMessage:
  action: ProtocolMessageAction // TR2, TR4a
  channel: String? // TR4b
  channelSerial: String? // TR4c
  connectionDetails: ConnectionDetails? // RSA7b3, RTN19, TR4o
  connectionId: String? // RTN15c1, TR4d
  connectionKey: String? // TR4e
  connectionSerial: Int? // RTN10c, TR4f
  count: Int? // TR4g
  error: ErrorInfo? // RTN15c2, TR4h
  flags: .HAS_PRESENCE & .HAS_BACKLOG ? // RTP1, TR3, TR4i
  id: String? // TR4b
  messages: [Message]? // TR4k
  msgSerial: Int? // RTN7b, TR4j
  presence: [PresenceMessage]? // TR4l
  timestamp: Time? // TR4m

enum ProtocolMessageAction:
  HEARTBEAT // TR2
  ACK // TR2
  NACK // TR2
  CONNECT // TR2
  CONNECTED // TR2
  DISCONNECT // TR2
  DISCONNECTED // TR2
  CLOSE // TR2
  CLOSED // TR2
  ERROR // TR2
  ATTACH // TR2
  ATTACHED // TR2
  DETACH // TR2
  DETACHED // TR2
  PRESENCE // TR2
  MESSAGE // TR2
  SYNC // TR2

class Connection:
  embeds EventEmitter<ConnectionState, ConnectionStateChange> // RTN4a, RTN4e
  errorReason: ErrorInfo? // RTN14a
  id: String? // RTN8
  key: String? // RTN9
  recoveryKey: String? // RTN16b, RTN16c
  serial: Int // RTN10
  state: ConnectionState // RTN4d
  close() // RTN12
  connect() // RTC1b, RTN3, RTN11
  ping() => io // RTN13

enum ConnectionState:
  INITIALIZED
  CONNECTING
  CONNECTED
  DISCONNECTED
  SUSPENDED
  CLOSING
  CLOSED
  FAILED

class ConnectionStateChange:
  current: ConnectionState // TA2
  previous: ConnectionState // TA2
  reason: ErrorInfo? // RTN4f, TA3
  retryIn: Duration? // RTN14d, TA2

class Stats:
  all: StatsMessageTypes // http://goo.gl/TpIh5I
  apiRequests: StatsRequestCount // http://goo.gl/lFhZLC
  channels: StatsResourceCount // http://goo.gl/jfu0q1
  connections: StatsConnectionTypes // http://goo.gl/72fQ7Z
  inbound: StatsMessageTraffic // http://goo.gl/8SjQAJ
  intervalGranularity: StatsIntervalGranularity
  intervalId: String
  intervalTime: Time
  outbound: StatsMessageTraffic // http://goo.gl/8SjQAJ
  persisted: StatsMessageTypes // http://goo.gl/TpIh5I
  tokenRequests: StatsRequestCount // http://goo.gl/lFhZLC

enum StatsIntervalGranularity:
  MINUTE
  HOUR
  DAY
  MONTH

class ErrorInfo:
  code: Int // TI1
  message: String // TI1
  statusCode: Int // TI1

class EventEmitter<Event, Data>:
  on((Data...) ->) // RTE4
  on(Event, (Data...) ->) // RTE4
  once((Data...) ->) // RTE4
  once(Event, (Data...) ->) // RTE4
  off() // RTE5
  off((Data...) ->) // RTE5
  off(Event, (Data...) ->) // RTE5
  emit(Event, Data...)  // RTE6

class PaginatedResult<T>:
  items: [T] // TG3
  first() => io PaginatedResult<T> // TG5
  hasNext() -> Bool // TG6
  isLast() -> Bool // TG7
  next() => io PaginatedResult<T>? // TG4

Get started now with our free plan

It includes 3m messages per month, 100 peak connections, 100 peak channels, and loads of features.

Create your free account