docs: describe how to use WebSockets with the REST API
We add a new document that shows two examples of how to use the WebSocket REST API with JavaScript.
This commit is contained in:
parent
c5c28564e9
commit
c7cb2c0a78
99
docs/rest/websockets.md
Normal file
99
docs/rest/websockets.md
Normal file
@ -0,0 +1,99 @@
|
||||
# WebSockets with `lnd`'s REST API
|
||||
|
||||
This document describes how streaming response REST calls can be used correctly
|
||||
by making use of the WebSocket API.
|
||||
|
||||
As an example, we are going to write a simple JavaScript program that subscribes
|
||||
to `lnd`'s
|
||||
[block notification RPC](https://api.lightning.community/#v2-chainnotifier-register-blocks).
|
||||
|
||||
The WebSocket will be kept open as long as `lnd` runs and JavaScript program
|
||||
isn't stopped.
|
||||
|
||||
## Browser environment
|
||||
|
||||
When using WebSockets in a browser, there are certain security limitations of
|
||||
what header fields are allowed to be sent. Therefore, the macaroon cannot just
|
||||
be added as a `Grpc-Metadata-Macaroon` header field as it would work with normal
|
||||
REST calls. The browser will just ignore that header field and not send it.
|
||||
|
||||
Instead we have added a workaround in `lnd`'s WebSocket proxy that allows
|
||||
sending the macaroon as a WebSocket "protocol":
|
||||
|
||||
```javascript
|
||||
const host = 'localhost:8080'; // The default REST port of lnd, can be overwritten with --restlisten=ip:port
|
||||
const macaroon = '0201036c6e6402eb01030a10625e7e60fd00f5a6f9cd53f33fc82a...'; // The hex encoded macaroon to send
|
||||
const initialRequest = { // The initial request to send (see API docs for each RPC).
|
||||
hash: "xlkMdV382uNPskw6eEjDGFMQHxHNnZZgL47aVDSwiRQ=", // Just some example to show that all `byte` fields always have to be base64 encoded in the REST API.
|
||||
height: 144,
|
||||
}
|
||||
|
||||
// The protocol is our workaround for sending the macaroon because custom header
|
||||
// fields aren't allowed to be sent by the browser when opening a WebSocket.
|
||||
const protocolString = 'Grpc-Metadata-Macaroon+' + macaroon;
|
||||
|
||||
// Let's now connect the web socket. Notice that all WebSocket open calls are
|
||||
// always GET requests. If the RPC expects a call to be POST or DELETE (see API
|
||||
// docs to find out), the query parameter "method" can be set to overwrite.
|
||||
const wsUrl = 'wss://' + host + '/v2/chainnotifier/register/blocks?method=POST';
|
||||
let ws = new WebSocket(wsUrl, protocolString);
|
||||
ws.onopen = function (event) {
|
||||
// After the WS connection is establishes, lnd expects the client to send the
|
||||
// initial message. If an RPC doesn't have any request parameters, an empty
|
||||
// JSON object has to be sent as a string, for example: ws.send('{}')
|
||||
ws.send(JSON.stringify(initialRequest));
|
||||
}
|
||||
ws.onmessage = function (event) {
|
||||
// We received a new message.
|
||||
console.log(event);
|
||||
|
||||
// The data we're really interested in is in data and is always a string
|
||||
// that needs to be parsed as JSON and always contains a "result" field:
|
||||
console.log("Payload: ");
|
||||
console.log(JSON.parse(event.data).result);
|
||||
}
|
||||
ws.onerror = function (event) {
|
||||
// An error occured, let's log it to the console.
|
||||
console.log(event);
|
||||
}
|
||||
```
|
||||
|
||||
## Node.js environment
|
||||
|
||||
With Node.js it is a bit easier to use the streaming response APIs because we
|
||||
can set the macaroon header field directly. This is the example from the API
|
||||
docs:
|
||||
|
||||
```javascript
|
||||
// --------------------------
|
||||
// Example with websockets:
|
||||
// --------------------------
|
||||
const WebSocket = require('ws');
|
||||
const fs = require('fs');
|
||||
const macaroon = fs.readFileSync('LND_DIR/data/chain/bitcoin/simnet/admin.macaroon').toString('hex');
|
||||
let ws = new WebSocket('wss://localhost:8080/v2/chainnotifier/register/blocks?method=POST', {
|
||||
// Work-around for self-signed certificates.
|
||||
rejectUnauthorized: false,
|
||||
headers: {
|
||||
'Grpc-Metadata-Macaroon': macaroon,
|
||||
},
|
||||
});
|
||||
let requestBody = {
|
||||
hash: "<byte>",
|
||||
height: "<int64>",
|
||||
}
|
||||
ws.on('open', function() {
|
||||
ws.send(JSON.stringify(requestBody));
|
||||
});
|
||||
ws.on('error', function(err) {
|
||||
console.log('Error: ' + err);
|
||||
});
|
||||
ws.on('message', function(body) {
|
||||
console.log(body);
|
||||
});
|
||||
// Console output (repeated for every message in the stream):
|
||||
// {
|
||||
// "hash": <byte>,
|
||||
// "height": <int64>,
|
||||
// }
|
||||
```
|
Loading…
Reference in New Issue
Block a user