Next: , Up: JSONRPC communication   [Contents][Index]

33.32.1 Overview

Quoting from the spec, JSONRPC "is transport agnostic in that the concepts can be used within the same process, over sockets, over http, or in many various message passing environments."

To model this agnosticism, the jsonrpc library uses objects of a jsonrpc-connection class, which represent a connection to a remote JSON endpoint (for details on Emacs’s object system, see EIEIO in EIEIO). In modern object-oriented parlance, this class is “abstract”, i.e. the actual class of a useful connection object is always a subclass of jsonrpc-connection. Nevertheless, we can define two distinct APIs around the jsonrpc-connection class:

  1. An API for building JSONRPC applications

    In this scenario, a new aspiring JSONRPC-based application selects a concrete subclass of jsonrpc-connection that provides the transport for the JSONRPC messages to be exchanged between endpoints.

    The application creates objects of that subclass using make-instance. To initiate a contact to a remote endpoint, the application passes this object to the functions such as jsonrpc-notify, jsonrpc-request, or jsonrpc-async-request.

    For handling remotely initiated contacts, which generally come in asynchronously, the make-instance instantiation should initialize it the :request-dispatcher and :notification-dispatcher EIEIO keyword arguments. These are both functions of 3 arguments: the connection object; a symbol naming the JSONRPC method invoked remotely; and a JSONRPC params object.

    The function passed as :request-dispatcher is responsible for handling the remote endpoint’s requests, which expect a reply from the local endpoint (in this case, the application you’re building). Inside that function, you may either return locally (a regular return) or non-locally (throw an error). Both exits from the request dispatcher cause a reply to the remote endpoint’s request to be sent through the transport.

    A regular return determines a success response, and the return value must be a Lisp object that can be serialized as JSON (see Parsing and generating JSON values). The result is forwarded to the server as the JSONRPC result object. A non-local return, achieved by calling the function jsonrpc-error, causes an error response to be sent to the server. The details of the accompanying JSONRPC error object are filled out with whatever was passed to jsonrpc-error. A non-local return triggered by an unexpected error of any other type also causes an error response to be sent (unless you have set debug-on-error, in which case this calls the Lisp debugger, see Entering the Debugger on an Error).

    It’s possible to use the jsonrpc library to build applications based on transport protocols that can be described as “quasi-JSONRPC”. These are similar, but not quite identical to JSONRPC, such as the DAP (Debug Adapter Protocol). These protocols also define request, response and notification messages but the format is not quite the same as JSONRPC. The generic functions jsonrpc-convert-to-endpoint and jsonrpc-convert-from-endpoint can be customized for converting between the internal representation of JSONRPC and whatever the endpoint accepts (see Generic Functions).

  2. An API for building JSONRPC transports

    In this scenario, jsonrpc-connection is sub-classed to implement a different underlying transport strategy (for details on how to subclass, see (eieio)Inheritance.). Users of the application-building interface can then instantiate objects of this concrete class (using the make-instance function) and connect to JSONRPC endpoints using that strategy. See Process-based JSONRPC connections for a built-in transport implementation.

    This API has mandatory and optional parts.

    To allow its users to initiate JSONRPC contacts (notifications or requests) or reply to endpoint requests, the new transport implementation must equip the jsonrpc-connection-send generic function with a specialization for the new subclass (see Generic Functions). This generic function is called automatically by primitives such as jsonrpc-request and jsonrpc-notify. The specialization should ensure that the message described in the argument list is sent through whatever underlying communication mechanism (a.k.a. “wire”) is used by the new transport to talk to endpoints. This “wire” may be a network socket, a serial interface, an HTTP connection, etc.

    Likewise, for handling the three types of remote contacts (requests, notifications, and responses to local requests), the transport implementation must arrange for the function jsonrpc-connection-receive to be called from Elisp after noticing some data on the “wire” that can be used to craft a JSONRPC (or quasi-JSONRPC) message.

    Finally, and optionally, the jsonrpc-connection subclass should add specializations to the jsonrpc-shutdown and jsonrpc-running-p generic functions if these concepts apply to the transport. The specialization of jsonrpc-shutdown should ensure the release of any system resources (e.g. processes, timers, etc.) used to listen for messages on the wire. The specialization of jsonrpc-running-p should tell if these resources are still active or have already been released (via jsonrpc-shutdown or otherwise).

Next: Process-based JSONRPC connections, Up: JSONRPC communication   [Contents][Index]