NAME

Riap - Rinci access protocol

VERSION

This document describes version 1.2.4 of Riap (from Perl distribution Riap), released on 2015-09-04.

SYNOPSIS

This document specifies a simple, extensible, client/server, request/response protocol for requesting metadata and performing actions on code entities.

Examples are written in JSON (sometimes with added comments), but data structures can actually be encoded using other formats.

DESCRIPTION

Rinci access protocol (Riap for short), is a client/server, request/response protocol for requesting metadata and performing actions on code entities. It is modeled closely after HTTP, but is a different protocol. It can be layered on top of HTTP (as its transport protocol) but can also use other transports, including direct TCP.

The server side is viewed as being a tree of code entities, with a package entity at the root. Other entities (such as subpackages, functions, variables) are discoverable by performing list actions on package entities. Entity's metadata can be retrieved using the meta action. There are other actions defined in the specification; and the protocol can be extended by introducing more actions.

One of the main use-cases of this protocol is to provide self-descriptive, self-documenting, machine-discoverable API service, much like the goal of SOAP and WSDL. However it is much simpler and programmer-friendly than those protocols.

SPECIFICATION VERSION

 1.2

STATUS

The 1.2 series does not guarantee full backward compatibility between revisions, so caveat implementor. However, major incompatibility will bump the version to 1.3 or 2.0.

TERMINOLOGIES

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

  • Server

  • Client

  • Response

    Response is an enveloped result as defined in the Rinci::function specification.

  • Request

    A Riap request is modelled after HTTP request. It consists of an action (analogous to HTTP request method), code entity URI, protocol version, and zero or more extra arguments (analogous to HTTP headers). Some extra arguments might be required, depending on the action.

    For simplicity, the request can be expressed as a hash (or dictionary) of key/value pairs. There should at least be these keys: action, uri, and v (for protocol version). This also means the extra arguments cannot have those names. They must also start with letters or underscores followed by zero or more letters/underscores/digits.

    Server should return 400 status if required keys are missing, unknown keys are sent, or keys contain invalid values.

RIAP URI SCHEMES

To refer to code entities on the Riap server, a new URI scheme riap is defined. The URI path describes path to code entities while the URI host describes the host language. Examples:

 # refer to Text::sprintfn Perl module
 riap://perl/Text/sprintfn/

 # refer to sprintf function in Text::sprintfn Perl module
 riap://perl/Text/sprintfn/sprintfn

 # refer to PHP variable
 riap://php/$var

 # UNDECIDED: refer to class metadata
 riap://perl/My/Class/:class

 # UNDECIDED: refer to distribution metadata
 riap://perl/My/Dist/:distribution

There are some other schemes recognized.

pl. The pl hostless scheme refers to code entities in Perl. This is preferred in many Perinci modules because the riap scheme is more verbose. Examples:

 # refer to Text::sprintfn Perl module
 pl:/Text/sprintfn/

 # refer to sprintf function in Text::sprintfn Perl module
 pl:/Text/sprintfn/sprintfn

riap+tcp. This scheme is for Riap::Simple over TCP socket. The host (+port) part describes TCP host (+port), and the path part describes path to code entity. Currently there is no default port so please always specify port number. Examples:

 riap+tcp://localhost:5000/Text/sprintfn/
 riap+tcp://localhost:5000/Text/sprintfn/sprintfn

riap+unix. This scheme is for Riap::Simple over Unix socket. There is no host part (Unix socket is localhost-only). The path part describes path to the socket and path to code entity (separated by //).

 riap+unix:/path/to/unix/socket
 riap+unix:/path/to/unix/socket//Text/sprintfn/sprintfn

riap+pipe. This scheme is for Riap::Simple over pipe, to refer to starting a program and talking the protocol over the pipe. There is no host. The path part describes path to program, // separator, arguments separated by /, //separator, and path to code entity.

 # refer to accessing code entity via executing "/path/to/program"
 riap+pipe:/path/to/program////Text/sprintfn/sprintfn

 # refer to via accessing code entity via executing "/path/to/program arg1 arg2"
 riap+pipe:/path/to/program//arg1/arg2//Text/sprintfn/sprintfn

RIAP OVER HTTP/HTTPS

For Riap over HTTP/HTTPS as the transport layer, the standard URI scheme http/https is used. The URI path for these do not necessarily map directly to code entities like in riap or pl URIs, they may also support additional features, as each service provider can choose custom URL layout. Example:

 # access tax::id::validate_npwp function, call with arguments
 http://gudangapi.com/ga/tax.id.npwp/validate_npwp?npwp=123456

 # namespace/module can also be omitted when function name is unique. arguments
 # can also be specified by position
 http://gudangapi.com/ga/validate_npwp/123456

However, any Riap request and any code entity URI can still be requested from any http/https URL following the Riap::HTTP protocol..

THE REQUEST

As mentioned previously, the Riap request is a mapping of request keys and their values. Common request keys:

  • v => FLOAT

    Specify Riap protocol version. If not specified, default is 1.1. Server should return 501 status if it does not support requested protocol version.

  • uri => STR

    Required. Specify the location to code entity. It is either a schemeless URI (e.g. /Package/SubPackage/func) to refer to "local" code entity or URI with a scheme to refer to a remote entity, e.g. http://example.org/api/Foo/Bar/func, in which case the server can decide to proxy it for the client or not.

    The server should return 404 status if uri does not map to an existing code entity, or 403 if uri is forbidden.

  • action => STR

    Required. Specify action to perform on the code entity.

    The server should return 501 status if an action is unknown for the specified URI. The server should return 401/403 status if authentication is required or action is not allowed for the specified code entity, respectively.

  • tx_id => STR

    Optional. Transaction ID. Only needed if you want to execute request inside transaction scope. See Riap::Transaction for more details.

Additional keys might be recognized and/or required according to the action.

COMMON ACTIONS

Below are the actions which can be implemented by the server. Server can choose to not implement any action, or implement additional actions. But for actions mentioned below, the specification must be followed.

Action: info

Get general information and information about the code entity. This action requires no additional request keys. Upon success, the server must return a hash result with at least the following keys (remember that the result is actually enveloped with a 200 status):

 [200,"OK",
  {
   // entity's type
   "type": "function",

   // canonical URI for the entity
   "uri": "/Package/SubPkg/",
  },
  {"riap.v": 1.2}
 ]

Server may add additional information keys.

Action: actions

List available actions for code entity. The server should return a list of action names:

 [200,"OK",
  ["info","actions","meta","call","complete_arg_val"],
  {"riap.v":1.2}
 ]

Additional request key: detail (bool, optional, default false, can be set to true to make server return a list of records instead).

Under detail, server should return something like this:

 [200,"OK",
  [
   {"name":"info", "summary":"Get general information about code entity"},
   {"name":"actions","summary":"List available actions for code entity"},
   {"name":"meta","summary":"Get metadata for code entity"},
   {"name":"call","summary":"Call function"},
   {"name":"complete_arg_val","summary":"Complete function's argument value"}
  ],
  {"riap.v":1.2}
 ]

It can return additional field like keys to explain additional required/optional request keys (XXX not yet specified).

Action: meta

Return Rinci metadata for the code entity. When the entity does not have metadata, server should return 404 status or better yet 534 (metadata not found).

ACTIONS FOR package ENTITIES

Below are actions that must be supported by the package entities.

Action: list

List entities contained in this package. Additional request keys are: type (string, optional, to limit only listing entities of a certain type; default is undef which means list all kinds of entities), recursive (bool, optional, can be set to true to search subpackages; default is false which means only list entities in this namespace), q (string, search terms, to only return matching some search terms; default is undef which means return all entities), detail (bool, optional, whether to return just a list of code entity URIs or a detailed record for each entry, defaults to false; if true, then server must return info hash for each entry, where each info hash like that returned by the info action).

The server should return 200 status or 206 if partial list is returned. If detail is true, for each entry a hash must be returned containing at least uri and type. Server may add additional information like summary, description, etc.

Example, a list action on the top namespace / might return the following:

 [200,"OK",
  ["Math/","Utils/"],
  {"riap.v":1.2}
 ]

Another example, a list action on the pl:/Math namespace, with type set to function and q to multiply, and detail set to true:

 [200,"OK",
  [
   {"uri": "multiply2",
    "type": "function",
    "summary": "Multiply two numbers"},

   {"uri": "multmany",
    "type": "function",
    "summary": "Multiply several numbers"}
  ],
  {"riap.v":1.2}
 ]

An abs_uri can also be provided by server for each record.

Action: child_metas

Get metadata for all the children entities of the package entity.

This action can reduce the number of round-trips, as opposed to client performing list action followed by meta for each child.

Example, an child_metas action on the top namespace / might return the following:

 [200,"OK",
  {
   "Math/":  {"v":1.1, "summary":"This is metadata for Math"},
  ,"Utils/": {"v":1.1, "summary":"This is metadata for Utils"}
  },
  {"riap.v":1.2}
 ]

ACTIONS FOR function ENTITIES

Below are actions that are available for the function entities. At least call must be implemented by the server.

Action: call

Call a function and return its result. Additional request keys include:

  • args

    Hash, optional, function arguments, defaults to {}.

    In general, do not pass special arguments (arguments prefixed by dash -) as they might be stripped/removed prior to processing. Some special arguments can be passed through other means (e.g. see confirm and dry_run below).

  • confirm => BOOL

    Optional. If set to true, will pass special argument -confirm => 1 to function. This is used as confirmation when function can return status 331 (requires confirmation). See Rinci::function for more details on status 331.

  • dry_run => BOOL

    Optional. If set to true, will either pass -dry_run => 1 to function supporting dry run, or -tx_action => 'check_state' to function supporting transaction (can more or less be regarded as the dry run equivalent).

  • arg_len => int

    Will be passed to function as special argument -arg_len to send partial argument value.

  • arg_part_start => int

    Will be passed to function as special argument -arg_part_start to send partial argument value.

  • art_part_len => int

    Will be passed to function as special argument -arg_part_len to send partial argument value.

  • res_part_start => int

    Will be passed to function as special argument -res_part_start to request partial result.

  • res_part_len => int

    Will be passed to function as special argument -res_part_len to request partial result.

  • stream_arg => bool

    Optional. If set to true, then will provide a filehandle or an iterable object as argument value for the function, where the function can read data from. And will also send special argument -arg_stream => 1 to function. Note that the function must have exactly a single argument that has its streaming property set to true. Otherwise, server MUST send 501 (unimplemented) response.

Action: complete_arg_val

Complete function argument value, a la Bash tab completion where you have a semicompleted word and request possible values started by that word. Additional Riap request keys include:

  • arg

    String, required, the name of function argument to complete.

  • word

    String, optional, word that needs to be completed. Defaults to empty string.

The server should return a list of possible completions. Example, when completing a delete_user function for the argument username, and word is "st", the server might return:

 [200,"OK",
  ["stella","steven","stuart"],
  {"riap.v":1.2}
 ]

When there is no completion, the server should return an empty list:

 [200,"OK",
  [],
  {"riap.v":1.2}
 ]

Action: complete_arg_elem

This is just like complete_arg_val, except this tries to complete the value of an element of an array argument, instead of the value of the argument itself. This is useful for example when completing this command-line:

 % somecmd --category foo --category <tab>

where category is argument of type array and its element contain some restricted value. The command-line option --category can be specified multiple times to set the array elements.

  • arg

    String, required, the name of function argument to complete.

  • index

    Integer, required, which element to complete for (starts from 0 for the first element).

  • word

    String, optional, word that needs to be completed. Defaults to empty string.

ACTIONS FOR variable ENTITIES

Below are actions that are available for the variable entities.

Action: get

Get variable value.

FAQ

Why no actions to modify metadata/code entities?

Since the specification is extensible by adding more actions, you can implement this on your system. These actions are not specified by this specification because currently the main goal of the protocol is to provide API service and read-only access to the metadata.

Alternatively, modifying metada/code entities can be implemented using calls to functions on the server which can perform the modifications.

There are also some issues which need to be considered when adding these actions. First of all, security. Second, you need to decide whether to modify the running/in-memory copy or the actual source code/files (as the code entities are usually stored as). When modifying the in-memory copy, the server-side architecture may have multiple copies (multiple processes and machines). Do you want to modify all those copies or just one the one process?

The name?

Riap stands for Rinci access protocol, but it is also an Indonesian word meaning: to gain in size or number.

THE RESPONSE

Server must return an enveloped response as specified in Rinci::function specification, namely a 4-element array: [STATUS, MESSAGE, RESULT, META]. STATUS is an integer, MESSAGE is a string, RESULT is of any type and is the actual result, and META is a hash.

Starting from Riap 1.2, server must return META which contains at least riap.v key, containing the Riap protocol version. Client must recognize and remove all riap.* keys from the response before returning the final result to user. If there is an unknown riap.* keys or an unrecognized value, it must fail by returning 501 status to user. This is to handle/detect incompatibilities/new features in future versions.

HISTORY

1.2 (Oct 2014)

Version bumped to 1.2. This version addresses sending/receiving binary data over JSON. In this version, client can send base64-encoded binary data in arguments using ARGNAME:base64 key. Server can return base64-encoded binary data in result, this is hinted by the riap.result_encoding attribute in result medatata. To detect/handle incompatibilities in future versions, client now must check riap.v and all riap.* properties in result metadata, and fail if it can't recognize an attribute.

1.1 (Jan 2012)

Rename specification to Riap. Version bumped to 1.1 to various backward-incompatible adjustments to Rinci's terminologies.

1.0 (Aug 2011)

Split specification to Sub::Spec::HTTP.

May 2011

First release of Sub::Spec::HTTP::Server.

SEE ALSO

Rinci

JSON RPC, (version 1 at http://json-rpc.org, version 2 at http://jsonrpc.org). First developed 2005, major revision to version 2 in 2009. Similar to Riap in several aspects: 1) request and response are a single hash (JSON objects); 2) transport-independent. Differences to Riap: 1) JSON RPC has a narrower scope, it only deals with RPC/method calls and the other things like metadata access/exchange, etc; 2) JSON RPC is tied to the JSON format, while Riap is serialization-format-independent; 3) JSON RPC mandates a request ID ("id") on every request, to allow pipelining (i.e. client sends an array of requests at once, answers will come potentially out of order). This could be added later to the Riap protocol when necessary.

SPORE, https://github.com/SPORE/specifications/blob/master/spore_description.pod.

SOAP, WSDL. Popular in the early 2000's, with similar goals (easier service discovery, "simple" request/response protocol which can utilize HTTP or other transport layer). Fell out of favor along with the rise of JavaScript and REST and increased criticism against the complexity of XML. Which is ironic because SOAP was originally created to be the simpler alternative to CORBA.

CORBA.

HOMEPAGE

Please visit the project's homepage at https://metacpan.org/release/Riap.

SOURCE

Source repository is at https://github.com/perlancar/perl-Riap.

BUGS

Please report any bugs or feature requests on the bugtracker website https://rt.cpan.org/Public/Dist/Display.html?Name=Riap

When submitting a bug or request, please include a test-file or a patch to an existing test-file that illustrates the bug or desired feature.

AUTHOR

perlancar <perlancar@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by perlancar@cpan.org.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.