Next: , Previous: , Up: The Prolog Library   [Contents][Index]


10.21 JSON mediated process communication—library('jsonrpc/jsonrpc_server')

This library implements a generic JSON-RPC server where the requests are handled by user-supplied hooks.

This library should be considered pre-release, in that it may evolve, also incompatibly, as we gain more experience with how it is used.

Please note: You should always take the security implications into account if exposing any service to untrusted clients, e.g. on Internet.

The server can only handle one client connection, i.e. it does not provide any facility for multiplexing between different client streams.

The server implements both “ordinary” JSON-RPC messages, dispatched to the user-supplied “request hook”, and “Prolog-style” messages (which provide things like multiple solutions, backtracking, failure and cut), dispatched to the user supplied “call hook”.

See Simple JSON-RPC Server for a simplified way to use this module. Its documentation also contains examples.

The basic structure of the server is a loop that repeatedly reads one request and lets the user-supplied hook (or hooks) handle it, until the hook tells the server to return from the loop (or a fatal error occurs).

A user-specified state is threaded through the loop and the hooks, so the hooks can use and modify the state as they process the incoming requests. The state can be any non-variable term, it does not have to be representable as JSON. For instance, the state could contain constrained variables (as used by library(clpfd)).

10.21.1 Requests

For our purposes, a JSON-RPC request object (henceforth request) is a JSON (https://json.org) object, with the following members:

jsonrpc

A string with value "2.0".

id

An integer identifier established by the client for referring to this request. Identifiers should be unique, although this is not currently a strict requirement.

If the identifier is absent, this indicates a special kind of request, a “notification”, and a variable will be used. Notifications are currently not implemented.

method

A string naming the method to invoke.

params

An optional object or array with parameters to the method. If not supplied, the empty list will be used.

other members are allowed, but will be ignored.

An incoming request is represented as a request term request(Method,Id,Params,RPC) where each argument is the Prolog representation of the corresponding JSON value (see library(json) for details), and RPC is the representation of the entire request.

10.21.2 The Request Hook

When a request comes in from the client, the request hook will be called with four arguments:

Message

This is the request term as described above.

ResultDescription

On success, the request hook should bind this to a value that specifies how the server should continue.

result(JSONValue)

This is the common case, and tells the server to reply to the client with the JSONValue as the result. The value should use the representation of the JSON library. If no specific result value is desired, you can specify the JSON null, i.e. @(null).

quit(JSONValue)

This is like result(JSONValue) but also tells the server to quit, i.e. the server loop will return to the caller.

error(ErrorCode, Message)

Replies to the client with an error response with the specified error code and error message (a string). The error code must be an integer outside the range reserved by this library and by the JSON-RPC specification, see Responses for details. The Message should be a short string that briefly describes the error.

Please note: this is not considered an error (or failure) by the server itself, i.e. any remaining processing of the request will proceed as usual. In particular the request will be considered handled by the request hook, and no fallback processing will happen.

StateIn

This is an input argument specifying the current state. The initial state is specified when the server is started. You can use any non-variable term as state, it does not have to be a value that can be represented as JSON.

StateOut

This is an output argument specifying the new state to use for subsequent requests.

If the request hook fails, the server will do its default processing, which includes handling the “Prolog-style” messages. For this reason it is important that the request hook quietly fails for any method it does not recognize.

10.21.3 Prolog-style Messages

If a call hook was specified, it will be used for handling the special methods ‘call’ and ‘once’ (unless the request hook decides to handle these methods in some other way). The params member of these messages specifies how to create a Prolog term that will then be passed to the call hook.

10.21.3.1 Specifying the Term

The params member can specify the term in two different ways, either directly using a name and a list (JSON array) of arguments, or indirectly using a string with text in Prolog syntax which will be read to create the corresponding term. The params should be one of:

[F|Args]. A JSON array with one or more arguments, or
An object with members name with value F and args with value Args.

The value F should be a string denoting the name of the callable term, and the Args should be an array of the zero or more arguments. The term will be created similarly to Term =.. [F|Args].

An object with members read and (optionally) bindings.

The value of the read member should be a string in Prolog syntax, including a final full stop, and the term will be created by reading from this string.

If bindings is supplied, it should be an object where the value of each member will be used for providing an initial value of the variable with the same name (if any) in the term. Extraneous members are ignored.

The variable names and their values will be recorded when reading the term, as with the read_term/3 option variable_names/1. This value will be passed to the call hook (for other ways of specifying the term, the variables will be an empty list).

Once the term has been created, it is passed, together with the list of named variables, to the call hook, similarly to how the request hook is invoked, but with some special considerations for handling backtracking and cut.

10.21.3.2 The Call Hook

When a Prolog-style request comes in from the client, the call hook will be called with five arguments:

Message

This is the request term as described above.

Variables

This is the list of variable names and values for the term, if the term was read from a string (with any bindings specified in the request already applied). This can be used for giving special meaning to variables that occur deep within the term, without the server code needing to know the exact structure of the term that the client sent.

ResultDescription

On success, the request hook should bind this to a value that specifies how the server should continue.

result(JSONValue)

This is the same as for the request hook.

quit(JSONValue)

Not supported. Unlike the request hook, a call hook cannot tell the server to quit.

error(ErrorCode, Message)

This is the same as for the request hook.

Please note: this is not considered an error (or failure) by the server itself, i.e. any remaining processing of the request will proceed as usual. In particular the call request will still be active and must be closed with cut or retry.

StateIn

This is an input argument specifying the current state. The initial state is specified when the server is started. You can use any non-variable term as state, it does not have to be a value that can be represented as JSON.

StateOut

This is an output argument specifying the new state to use for subsequent requests.

If the call hook fails, possibly after a control message, this will be reported back to the client as a failure response (see Responses, below).

10.21.3.3 Single Solution Request

If the incoming request specifies the method once, a term is created as above, and the call hook will be called as if by once/1, i.e. it can succeed or fail (or throw an error), but it cannot leave any choice points behind.

If the call hook succeeds, the response will be as specified by the ResultDescription argument. If the call fails, a failure response will be sent back to the client.

In most cases an ordinary request, handled by the request hook, is preferable, unless the additional ways to specify the term is required, or it is important to be able to report failure as distinct from unhandled.

10.21.3.4 Multi-Solution Request

If the incoming request specifies the method call, a term is created as above, and the call hook will be called as if by call/1, i.e. it can succeed or fail (or throw an error), and it can leave choice points behind.

If the call hook succeeds, it is becomes active, and the response will be as specified by the ResultDescription argument. If the call fails, the call will not become active, and a failure response will be sent back to the client.

An active call corresponds to a Prolog choicepoint and will consume resources in the server until the call is closed by a control message, i.e. by a cut (see Cut), or by a (failing) retry (see Backtracking). A call request should specify a unique identifier member, to ensure that any cut or retry can refer to it unambiguously.



Send feedback on this subject.