websocket13-jsonrpc
TypeScript icon, indicating that this package has built-in type declarations

2.0.1 • Public • Published

websocket13-jsonrpc

npm version npm downloads license sponsors

This is a server implementation of JSON-RPC 2.0 over WebSockets.

A browser client implementation is available under browser. If you want to write your own implementation, the only special consideration is that incoming connections must support the subprotocol jsonrpc-2.0.

Please note that this server implementation does not support JSON-RPC batches, since that practically isn't useful over a transport like WebSocket.

Exports

The module exports the following:

ConnectionState

See enum here

JsonRpcErrorCode

See enum here

Reserved JSON-RPC error codes are defined here.

WebSocketStatusCode

See enum here

RpcError

Constructed with 3 parameters:

  • message - A string containing an error message
  • code - A number containing an error code (MUST be an integer)
  • data - An optional value of any type to be sent along with the error

Example

const {RpcError} = require('websocket13-jsonrpc2');

throw new RpcError('File not found', 100, {filename: '/root/example.txt'});

WsRpcConnection

This class is not to be instantiated directly.

Properties

id

A string containing a UUIDv4 assigned to this connection.

remoteAddress

The remote IP address.

server

The WsRpcServer that spawned this connection.

state

The state of this connection. This is a value from ConnectionState

handshakeData

The data object from the handshake event for this connection.

groups

An array containing the string name of each group that this connection is a member of.

Methods

disconnect(statusCode[, reason])

  • statusCode - A value from WebSocketStatusCode
  • reason - An optional string reason for the closure

Closes an active connection. If the connection is already closed, does nothing.

getPeerCertificate([detailed])

Same as tls.TLSSocket.getPeerCertificate. Returns null if the current connection is not secure.

getSecurityProtocol()

Same as tls.TLSSocket.getProtocol. Returns null if the current connection is not secure.

data(key[, value])

  • key - String
  • value - Any type

Associate any arbitrary data with this connection. If value is undefined, returns the current value of key. If value is defined, sets key to that value and returns the previous value.

ping()

Sends a ping frame to the remote. Returns a Promise that is resolved with the time in milliseconds that it took to receive the reply.

Example

async function pingClient() {
    let latency = await client.ping();
    console.log(`Client ${client.id}'s latency is ${latency} milliseconds.`);
}

joinGroup(group)

  • group - String group name

Joins this connection to a group. Groups are used to broadcast messages to groups of connections all at once. For example, you might put connections in a particular chat room into one group, or you might put connections authenticated to a given user ID in a dedicated group.

Groups are ad-hoc and are created or destroyed as needed.

Returns true if this connection was joined to the group successfully, or false if it was already in the given group.

leaveGroup(group)

  • group - String group name

Leaves this connection from a group. If this was the last member of the group, the group is destroyed.

Returns true if this connection left the group successfully, or false if it was not in the given group.

notify(method[, params])

  • method - String method name
  • params - Any data type

Sends a notification to the remote. JSON-RPC notifications are messages which may not be responded to.

Returns true if the notification was sent, or false if the connection was not open.

invoke(method[, params])

  • method - String method name
  • params - Any data type

Sends a request to the remote. Returns a Promise that will be resolved with the result of the request, or rejected with a RpcError if the request fails.

Events

latency

  • pingTime - Round-trip latency in milliseconds

Emitted periodically (unless you've disabled pings in options) with the latency of the connection.

WsRpcServer

This class instantiates a WebSocket server.

The constructor takes a single options object:

  • requireObjectParams - See below
  • All other options from WS13.WebSocketServer are allowed, except protocols.

requireObjectParams

If requireObjectParams is set to true, then the following will happen:

  • If the params property of incoming messages is undefined or null, it will be converted to {} (empty object)
  • If the params property of incoming messages is not an object, array, undefined, or null, then the message will be rejected with error code -32602 and message "Invalid params"
  • If the params property of outgoing messages is undefined or null, it will be converted to {} (empty object)

This option is designed to allow you to do things like this without worrying about invalid incoming params causing a crash:

server.registerMethod('Add', (connection, [a, b]) => {
	if (typeof a != 'number' || typeof b != 'number') {
		throw new RpcError('An operand is not a number', JsonRpcErrorCode.InvalidParams);
    }
	
    return a + b;
});

The module cannot validate that the incoming params object actually contains the keys you're expecting, so you still need to check their types and values yourself.

Events

handshake

  • handshakeData
  • reject
  • accept

Same as websocket13's handshake event, with these exceptions:

  • accept() returns a WsRpcConnection instead of a WebSocket
  • It is not possible to override protocol in accept()

This event must be handled or else all incoming connections will stall.

connect

Emitted when a new connection is established.

disconnect

  • connection - The WsRpcConnection that disconnected
  • code - A value from WebSocketStatusCode
  • reason - A string, possibly empty, describing why they disconnected
  • initiatedByUs - A boolean indicating whether the disconnection was initiated by the server (true) or by the client (false)

Emitted when a remote disconnects.

Properties

connections

An array containing all currently-active WsRpcConnection objects.

groups

An array containing strings of the names of all groups that currently have members.

Methods

http(server)

Listen for WebSocket connections on this server. You can call this for more than one HTTP(S) server, but you shouldn't call it more than once per server. For example, if you're accepting both secure and insecure connections, you should call this once with an HTTP server, and once with an HTTPS server.

This binds to the server's upgrade event. If nothing else has bound to that event, then node-websocket13 will respond to bad handshakes with an HTTP 400 response. Otherwise, it will do nothing. Bad handshakes are those which match any of the following criteria:

  • Upgrade header does not match the value websocket (case-insensitively)
  • Connection header does not contain the value upgrade (case-insensitively, interpreting the header value as a comma-separated list)
    • For example, Connection: keep-alive, upgrade is valid, but Connection: keep-alive upgrade is not
  • Client HTTP version is not at least 1.1
  • Client request method is not GET
  • Client request is missing Sec-WebSocket-Key header or when base64-decoded, it is not 16 bytes in length
  • Client request is missing Sec-WebSocket-Version header or the header's value is not 13

groupMembers(group)

  • group - Either a group name or an array of group names

Returns an array of WsRpcConnection objects for the members in the given set of groups. If you pass a single string, returns the list of members of that group. If you pass an array of strings, returns a de-duplicated union of group members.

registerMethod(name, handler)

  • name - String method name
  • handler - A function to be invoked when the method is called

Registers a new method. When JSON-RPC messages invoking this method are received, the handler will be called with the signature (WsRpcConnection connection, any params).

Please note that unless the requireObjectParams option is set, params can be any JSON type (including null or undefined).

The handler function must return either a response value or a Promise which is resolved to the response value. If an error occurs while processing this method, you must throw (or reject the Promise with) a RpcError, which will be sent to the remote as an error response.

By default, if a method invocation is received that does not match any registered method, a method not found error will be sent back. If you want to process unregistered methods yourself, you can use the DEFAULT_HANDLER symbol.

Example

const {RpcError, JsonRpcErrorCode, DEFAULT_HANDLER} = require('websocket13-jsonrpc2');

server.registerMethod('Add', (connection, params) => {
    if (typeof params != 'object' || !Array.isArray(params) || params.length != 2 || typeof params[0] != 'number' || typeof params[1] != 'number') {
        throw new RpcError('Invalid params', JsonRpcErrorCode.InvalidParams);    
    }
    
    return params[0] + params[1];
});

server.registerMethod('AddAsync', async (connection, params) => {
    if (typeof params != 'object' || !Array.isArray(params) || params.length != 2 || typeof params[0] != 'number' || typeof params[1] != 'number') {
        throw new RpcError('Invalid params', JsonRpcErrorCode.InvalidParams);    
    }
    
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return params[0] + params[1];
});

// This is only safe if you've enabled requireObjectParams, otherwise an incoming params value that isn't an object or
// array will cause a crash.
server.registerMethod('Subtract', (connection, [a, b]) => {
	if (typeof a != 'number' || typeof b != 'number') {
		throw new RpcError('Invalid params', JsonRpcErrorCode.InvalidParams);
    }
	
	return a - b;
});

server.registerMethod(DEFAULT_HANDLER, (connection, method, params) => {
	console.log(`Client ${connection.id} invoked unregistered method ${method} with params ${params}`);
	return 1;
});

registerNotification(name, handler)

  • name - String name
  • handler - A function to be invoked when the notification is received

Registers a new notification. When JSON-RPC messages containing this notification are received, the handler will be called with the signature (WsRpcConnection connection, any params).

Please note that unless the requireObjectParams option is set, params can be any JSON type (including null or undefined).

As a JSON-RPC notification requires no response, handler should not return anything.

You can also register a default handler for notifications in the same way as for methods.

notify(group, name[, params])

  • group - String name of group or array of string names of groups to send notification to. Use null to send a notification to all active clients.
  • name - String name of notification to send
  • params - Any data type

Sends a JSON-RPC notification to an entire group at once. You can also pass an array of groups to send a notification to all members of all specified groups.

Returns a number indicating how many clients received this notification.

notifyAll(name[, params])

  • name - String name of notification to send
  • params - Any data type

Sends a JSON-RPC notification to all connected clients. Returns a number indicating how many clients received this notification.

WsRpcOutgoingConnection

This class extends WsRpcConnection. Methods, properties, and events inherited from that class are not listed below, so you should check those docs as well.

Used to establish outgoing connections. You should instantiate a new instance of this class to establish a new outgoing connection to a JSON-RPC server.

The constructor takes two arguments:

  • url - The WebSocket URL you want to connect to (e.g. ws://example.com/?some=query)
  • options - Optional. An object with zero or more of these properties:

Example

const {WsRpcOutgoingConnection} = require('websocket13-jsonrpc');

let conn = new WsRpcOutgoingConnection('ws://127.0.0.1:8080', {pingInterval: 30000});

Properties

server

Always null for outgoing connections.

groups

Always [] (empty array) for outgoing connections.

Methods

joinGroup()

Outgoing connections cannot be joined to groups, so this method throws an Error if invoked.

leaveGroup()

Outgoing connections cannot be joined to groups, so this method throws an Error if invoked.

registerMethod(name, handler)

  • name - String
  • handler - Function

Functionally identical to WsRpcServer#registerMethod(name, handler). This is how you should register methods for outgoing connections.

registerNotification(name, handler)

  • name - String
  • handler - Function

Functionally identical to WsRpcServer#registerNotification(name, handler). This is how you should register notifications for outgoing connections.

Events

connected

Emitted when the connection is successfully established.

disconnected

  • code - A value from WebSocketStatusCode
  • reason - A string, possibly empty, describing why we disconnected
  • initiatedByUs - A boolean indicating whether the disconnected was initiated by us/the client (true) or by the server (false)

Emitted when we disconnect from the server.

error

  • err - An Error object

Emitted when a fatal error causes our connection to fail (while connecting) or be disconnected (while connected). Under certain conditions, err may contain zero or more of these properties:

  • responseCode - The HTTP status code we received if the error occurred during the handshake
  • responseText - The HTTP status text we received if the error occurred during the handshake
  • httpVersion - The HTTP version employed by the server if the error occurred during the handshake
  • headers - An object containing the HTTP headers we received from the server if the error occurred during the handshake
  • expected - A string containing the Sec-WebSocket-Accept value we expected to receive, if the error occurred because we didn't
  • actual - A string containing the actual Sec-WebSocket-Accept value we received, if the error occurred because it didn't match what we expected
  • state - The connection state at the time of error. Always present.
  • code - A value from the WS13.StatusCode enum, if the error occurred after the WebSocket connection was established

Readme

Keywords

none

Package Sidebar

Install

npm i websocket13-jsonrpc

Weekly Downloads

1

Version

2.0.1

License

MIT

Unpacked Size

118 kB

Total Files

37

Last publish

Collaborators

  • doctormckay