aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlisue <lambdalisue@gmail.com>2023-08-06 23:19:29 +0900
committerAlisue <lambdalisue@gmail.com>2023-08-26 19:14:06 +0900
commit01fe6b9e6a84338d4752c93a286262d79120f163 (patch)
treee81fa1f15567242a7517ed2d33ae523ba4cd56be /src
parentdeb6fd670479b2e00b99481ce59fdc187408d99d (diff)
downloadrneovim-01fe6b9e6a84338d4752c93a286262d79120f163.tar.gz
rneovim-01fe6b9e6a84338d4752c93a286262d79120f163.tar.bz2
rneovim-01fe6b9e6a84338d4752c93a286262d79120f163.zip
feat(msgpack_rpc): support out-of-order responses on `msgpack-rpc`
Added to support MessagePack-RPC fully compliant clients that do not return responses in request order. Although it is currently not an efficient implementation for full compliance and full compliance cannot be guaranteed, the addition of the new client type `msgpack-rpc` creates a situation where "if the client type is `msgpack-rpc`, then backward compatibility is ignored and full compliance with MessagePack- RPC compliance is justified even if backward compatibility is ignored if the client type is `msgpack-rpc`.
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval/funcs.c2
-rw-r--r--src/nvim/msgpack_rpc/channel.c45
-rw-r--r--src/nvim/msgpack_rpc/channel_defs.h11
3 files changed, 51 insertions, 7 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index cf8525e66a..1ed0994222 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -6730,7 +6730,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
const char *name = NULL;
Channel *chan = find_channel(chan_id);
if (chan) {
- name = rpc_client_name(chan);
+ name = get_client_info(chan, "name");
}
msg_ext_set_kind("rpc_error");
if (name) {
diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c
index a80424d78b..095e392092 100644
--- a/src/nvim/msgpack_rpc/channel.c
+++ b/src/nvim/msgpack_rpc/channel.c
@@ -306,6 +306,17 @@ end:
channel_decref(channel);
}
+static ChannelCallFrame *find_call_frame(RpcState *rpc, uint32_t request_id)
+{
+ for (size_t i = 0; i < kv_size(rpc->call_stack); i++) {
+ ChannelCallFrame *frame = kv_Z(rpc->call_stack, i);
+ if (frame->request_id == request_id) {
+ return frame;
+ }
+ }
+ return NULL;
+}
+
static void parse_msgpack(Channel *channel)
{
Unpacker *p = channel->rpc.unpacker;
@@ -321,13 +332,15 @@ static void parse_msgpack(Channel *channel)
}
arena_mem_free(arena_finish(&p->arena));
} else if (p->type == kMessageTypeResponse) {
- ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
- if (p->request_id != frame->request_id) {
+ ChannelCallFrame *frame = channel->rpc.client_type == kClientTypeMsgpackRpc
+ ? find_call_frame(&channel->rpc, p->request_id)
+ : kv_last(channel->rpc.call_stack);
+ if (frame == NULL || p->request_id != frame->request_id) {
char buf[256];
snprintf(buf, sizeof(buf),
- "ch %" PRIu64 " returned a response with an unknown request "
+ "ch %" PRIu64 " (type=%" PRIu32 ") returned a response with an unknown request "
"id %" PRIu32 ". Ensure the client is properly synchronized",
- channel->id, p->request_id);
+ channel->id, (unsigned)channel->rpc.client_type, p->request_id);
chan_close_with_error(channel, buf, LOGLVL_ERR);
}
frame->returned = true;
@@ -691,6 +704,25 @@ void rpc_set_client_info(uint64_t id, Dictionary info)
api_free_dictionary(chan->rpc.info);
chan->rpc.info = info;
+
+ // Parse "type" on "info" and set "client_type"
+ const char *type = get_client_info(chan, "type");
+ if (type == NULL || strequal(type, "remote")) {
+ chan->rpc.client_type = kClientTypeRemote;
+ } else if (strequal(type, "msgpack-rpc")) {
+ chan->rpc.client_type = kClientTypeMsgpackRpc;
+ } else if (strequal(type, "ui")) {
+ chan->rpc.client_type = kClientTypeUi;
+ } else if (strequal(type, "embedder")) {
+ chan->rpc.client_type = kClientTypeEmbedder;
+ } else if (strequal(type, "host")) {
+ chan->rpc.client_type = kClientTypeHost;
+ } else if (strequal(type, "plugin")) {
+ chan->rpc.client_type = kClientTypePlugin;
+ } else {
+ chan->rpc.client_type = kClientTypeUnknown;
+ }
+
channel_info_changed(chan, false);
}
@@ -699,14 +731,15 @@ Dictionary rpc_client_info(Channel *chan)
return copy_dictionary(chan->rpc.info, NULL);
}
-const char *rpc_client_name(Channel *chan)
+const char *get_client_info(Channel *chan, const char *key)
+ FUNC_ATTR_NONNULL_ALL
{
if (!chan->is_rpc) {
return NULL;
}
Dictionary info = chan->rpc.info;
for (size_t i = 0; i < info.size; i++) {
- if (strequal("name", info.items[i].key.data)
+ if (strequal(key, info.items[i].key.data)
&& info.items[i].value.type == kObjectTypeString) {
return info.items[i].value.data.string.data;
}
diff --git a/src/nvim/msgpack_rpc/channel_defs.h b/src/nvim/msgpack_rpc/channel_defs.h
index 1b5f0bb298..79aa8c1b13 100644
--- a/src/nvim/msgpack_rpc/channel_defs.h
+++ b/src/nvim/msgpack_rpc/channel_defs.h
@@ -14,6 +14,16 @@
typedef struct Channel Channel;
typedef struct Unpacker Unpacker;
+typedef enum {
+ kClientTypeUnknown = -1,
+ kClientTypeRemote = 0,
+ kClientTypeMsgpackRpc = 5,
+ kClientTypeUi = 1,
+ kClientTypeEmbedder = 2,
+ kClientTypeHost = 3,
+ kClientTypePlugin = 4,
+} ClientType;
+
typedef struct {
uint32_t request_id;
bool returned, errored;
@@ -37,6 +47,7 @@ typedef struct {
uint32_t next_request_id;
kvec_t(ChannelCallFrame *) call_stack;
Dictionary info;
+ ClientType client_type;
} RpcState;
#endif // NVIM_MSGPACK_RPC_CHANNEL_DEFS_H