Files
palladum-lightning/common/jsonrpc_io.c
Rusty Russell bc4bb2b0ef libplugin: use jsonrpc_io logic for sync requests too.
It's a little overkill, but it's clear.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2025-10-24 11:28:57 +10:30

152 lines
3.7 KiB
C

#include "config.h"
#include <ccan/io/io.h>
#include <ccan/membuf/membuf.h>
#include <ccan/tal/str/str.h>
#include <common/jsonrpc_io.h>
#include <common/utils.h>
#include <errno.h>
#include <unistd.h>
#define READ_CHUNKSIZE 64
struct jsonrpc_io {
MEMBUF(char) membuf;
jsmn_parser parser;
jsmntok_t *toks;
/* Amount just read by io_read_partial */
size_t bytes_read;
};
struct jsonrpc_io *jsonrpc_io_new(const tal_t *ctx)
{
struct jsonrpc_io *json_in;
json_in = tal(ctx, struct jsonrpc_io);
json_in->bytes_read = 0;
membuf_init(&json_in->membuf,
tal_arr(json_in, char, READ_CHUNKSIZE),
READ_CHUNKSIZE, membuf_tal_resize);
json_in->toks = toks_alloc(json_in);
jsmn_init(&json_in->parser);
return json_in;
}
/* Empty new bytes read into our unparsed buffer */
static void add_newly_read(struct jsonrpc_io *json_in)
{
/* Now added it to our unparsed buffer */
assert(json_in->bytes_read <= membuf_num_space(&json_in->membuf));
membuf_added(&json_in->membuf, json_in->bytes_read);
json_in->bytes_read = 0;
}
const char *jsonrpc_newly_read(struct jsonrpc_io *json_in,
size_t *len)
{
*len = json_in->bytes_read;
add_newly_read(json_in);
return membuf_space(&json_in->membuf) - *len;
}
const char *jsonrpc_io_parse(const tal_t *ctx,
struct jsonrpc_io *json_in,
const jsmntok_t **toks,
const char **buf)
{
bool complete;
/* If we're read any more, add that */
add_newly_read(json_in);
*toks = NULL;
*buf = NULL;
/* Our JSON parser is pretty good at incremental parsing, but
* `getrawblock` gives a giant 2MB token, which forces it to re-parse
* every time until we have all of it. However, we can't complete a
* JSON object without a '}', so we do a cheaper check here.
*/
if (!memchr(membuf_elems(&json_in->membuf), '}',
membuf_num_elems(&json_in->membuf)))
return NULL;
if (!json_parse_input(&json_in->parser, &json_in->toks,
membuf_elems(&json_in->membuf),
membuf_num_elems(&json_in->membuf),
&complete)) {
return tal_fmt(ctx, "Failed to parse RPC JSON response '%.*s'",
(int)membuf_num_elems(&json_in->membuf),
membuf_elems(&json_in->membuf));
}
if (!complete)
return NULL;
/* Must have jsonrpc to be valid! */
if (!json_get_member(membuf_elems(&json_in->membuf),
json_in->toks,
"jsonrpc")) {
return tal_fmt(ctx,
"JSON-RPC message does not contain \"jsonrpc\" field: '%.*s'",
(int)membuf_num_elems(&json_in->membuf),
membuf_elems(&json_in->membuf));
}
*toks = json_in->toks;
*buf = membuf_elems(&json_in->membuf);
return NULL;
}
void jsonrpc_io_parse_done(struct jsonrpc_io *json_in)
{
size_t bytes_parsed = json_in->toks[0].end;
membuf_consume(&json_in->membuf, bytes_parsed);
jsmn_init(&json_in->parser);
toks_reset(json_in->toks);
}
struct io_plan *jsonrpc_io_read_(struct io_conn *conn,
struct jsonrpc_io *json_in,
struct io_plan *(*next)(struct io_conn *,
void *),
void *arg)
{
/* Make sure there's more room */
membuf_prepare_space(&json_in->membuf, READ_CHUNKSIZE);
/* Try to read more. */
json_in->bytes_read = 0;
return io_read_partial(conn,
membuf_space(&json_in->membuf),
membuf_num_space(&json_in->membuf),
&json_in->bytes_read,
next, arg);
}
bool jsonrpc_sync_read(struct jsonrpc_io *json_in, int infd)
{
int r;
/* Make sure there's more room */
membuf_prepare_space(&json_in->membuf, READ_CHUNKSIZE);
/* Try to read more. */
r = read(infd,
membuf_space(&json_in->membuf),
membuf_num_space(&json_in->membuf));
if (r < 0)
return false;
if (r == 0) {
errno = 0;
return false;
}
json_in->bytes_read = r;
return true;
}