msgpack-rpc: Accept method names in requests

This commit is contained in:
Thiago de Arruda 2014-08-28 15:46:21 -03:00
parent 9d5e2c34c9
commit aa23d2f835
4 changed files with 53 additions and 8 deletions

View File

@ -91,6 +91,7 @@ output:write([[
#include <assert.h> #include <assert.h>
#include <msgpack.h> #include <msgpack.h>
#include "nvim/map.h"
#include "nvim/log.h" #include "nvim/log.h"
#include "nvim/os/msgpack_rpc.h" #include "nvim/os/msgpack_rpc.h"
#include "nvim/os/msgpack_rpc_helpers.h" #include "nvim/os/msgpack_rpc_helpers.h"
@ -241,12 +242,49 @@ for i = 1, #api.functions do
end end
output:write('\n};\n\n') output:write('\n};\n\n')
-- Generate a function that initializes method names with handler functions
output:write([[ output:write([[
static Map(cstr_t, uint64_t) *rpc_method_ids = NULL;
void msgpack_rpc_init(void)
{
rpc_method_ids = map_new(cstr_t, uint64_t)();
]])
-- Msgpack strings must be copied to a 0-terminated temporary buffer before
-- searching in the map, so we keep track of the maximum method name length in
-- order to create the smallest possible buffer for xstrlcpy
local max_fname_len = 0
for i = 1, #api.functions do
local fn = api.functions[i]
output:write(' map_put(cstr_t, uint64_t)(rpc_method_ids, "'
..fn.name..'", '..i..');\n')
if #fn.name > max_fname_len then
max_fname_len = #fn.name
end
end
output:write('\n}\n\n')
output:write([[
#define min(X, Y) (X < Y ? X : Y)
Object msgpack_rpc_dispatch(uint64_t channel_id, Object msgpack_rpc_dispatch(uint64_t channel_id,
uint64_t method_id,
msgpack_object *req, msgpack_object *req,
Error *error) Error *error)
{ {
msgpack_object method = req->via.array.ptr[2];
uint64_t method_id = method.via.u64;
if (method.type == MSGPACK_OBJECT_RAW) {
char method_name[]]..(max_fname_len + 1)..[[];
xstrlcpy(method_name, method.via.raw.ptr, min(method.via.raw.size, ]] ..(max_fname_len)..[[) + 1);
method_id = map_get(cstr_t, uint64_t)(rpc_method_ids, method_name);
if (!method_id) {
method_id = UINT64_MAX;
}
}
]]) ]])
output:write('\n // method_id=0 is specially handled') output:write('\n // method_id=0 is specially handled')
output:write('\n assert(method_id > 0);') output:write('\n assert(method_id > 0);')

View File

@ -39,15 +39,14 @@ WBuffer *msgpack_rpc_call(uint64_t channel_id,
return serialize_response(response_id, err, NIL, sbuffer); return serialize_response(response_id, err, NIL, sbuffer);
} }
uint64_t method_id = req->via.array.ptr[2].via.u64; if (req->via.array.ptr[2].type == MSGPACK_OBJECT_POSITIVE_INTEGER
&& req->via.array.ptr[2].via.u64 == 0) {
if (method_id == 0) {
return serialize_metadata(response_id, channel_id, sbuffer); return serialize_metadata(response_id, channel_id, sbuffer);
} }
// dispatch the call // dispatch the call
Error error = { .set = false }; Error error = { .set = false };
Object rv = msgpack_rpc_dispatch(channel_id, method_id, req, &error); Object rv = msgpack_rpc_dispatch(channel_id, req, &error);
// send the response // send the response
msgpack_packer response; msgpack_packer response;
msgpack_packer_init(&response, sbuffer, msgpack_sbuffer_write); msgpack_packer_init(&response, sbuffer, msgpack_sbuffer_write);
@ -235,8 +234,9 @@ static char *msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req)
return "Message type must be 0"; return "Message type must be 0";
} }
if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER
return "Method id must be a positive integer"; && req->via.array.ptr[2].type != MSGPACK_OBJECT_RAW) {
return "Method must be a positive integer or a string";
} }
if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) {

View File

@ -21,6 +21,11 @@ typedef Object (*rpc_method_handler_fn)(uint64_t channel_id,
msgpack_object *req, msgpack_object *req,
Error *error); Error *error);
/// Initializes the msgpack-rpc method table
void msgpack_rpc_init(void);
/// Dispatches to the actual API function after basic payload validation by /// Dispatches to the actual API function after basic payload validation by
/// `msgpack_rpc_call`. It is responsible for validating/converting arguments /// `msgpack_rpc_call`. It is responsible for validating/converting arguments
/// to C types, and converting the return value back to msgpack types. /// to C types, and converting the return value back to msgpack types.
@ -33,11 +38,11 @@ typedef Object (*rpc_method_handler_fn)(uint64_t channel_id,
/// @param error Pointer to error structure /// @param error Pointer to error structure
/// @return Some object /// @return Some object
Object msgpack_rpc_dispatch(uint64_t channel_id, Object msgpack_rpc_dispatch(uint64_t channel_id,
uint64_t method_id,
msgpack_object *req, msgpack_object *req,
Error *error) Error *error)
FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3); FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3);
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/msgpack_rpc.h.generated.h" # include "os/msgpack_rpc.h.generated.h"
#endif #endif

View File

@ -54,6 +54,7 @@
#include "nvim/os/shell.h" #include "nvim/os/shell.h"
#include "nvim/os/signal.h" #include "nvim/os/signal.h"
#include "nvim/os/job.h" #include "nvim/os/job.h"
#include "nvim/os/msgpack_rpc.h"
#if defined(HAVE_SYS_IOCTL_H) #if defined(HAVE_SYS_IOCTL_H)
# include <sys/ioctl.h> # include <sys/ioctl.h>
@ -164,6 +165,7 @@ void mch_init(void)
mac_conv_init(); mac_conv_init();
#endif #endif
msgpack_rpc_init();
event_init(); event_init();
} }