From 09605cec03ea23e87ee285fd950a23ce8d23678d Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Fri, 20 Jun 2014 10:53:02 -0300 Subject: channel/msgpack_rpc: Refactor msgpack_rpc_notification/serialize_event - Generalize some argument names(event type -> event name, event data -> event arg) - Rename serialize_event to serialize_message - Rename msgpack_rpc_notification to msgpack_rpc_message - Extract the message type out of msgpack_rpc_message - Add 'id' parameter to msgpack_rpc_message/serialize_message to create messages that are not notifications --- src/nvim/os/msgpack_rpc.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'src/nvim/os/msgpack_rpc.c') diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index 63e1245028..0d9a7ae3de 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -115,13 +115,22 @@ void msgpack_rpc_call(uint64_t id, msgpack_object *req, msgpack_packer *res) msgpack_rpc_dispatch(id, req, res); } -void msgpack_rpc_notification(String type, Object data, msgpack_packer *pac) +void msgpack_rpc_message(int type, + uint64_t id, + String method, + Object arg, + msgpack_packer *pac) { - msgpack_pack_array(pac, 3); - msgpack_pack_int(pac, 2); - msgpack_pack_raw(pac, type.size); - msgpack_pack_raw_body(pac, type.data, type.size); - msgpack_rpc_from_object(data, pac); + msgpack_pack_array(pac, id ? 4 : 3); + msgpack_pack_int(pac, type); + + if (id) { + msgpack_pack_uint64(pac, id); + } + + msgpack_pack_raw(pac, method.size); + msgpack_pack_raw_body(pac, method.data, method.size); + msgpack_rpc_from_object(arg, pac); } void msgpack_rpc_error(char *msg, msgpack_packer *res) -- cgit From bc0380038e0a4ff4f4bfaa939b0cef26c5e53582 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Mon, 23 Jun 2014 11:42:29 -0300 Subject: channel/msgpack_rpc: Refactor to better split functions across modules Move validation/conversion functions and to msgpack_rpc_helpers to separate those from the functions that are used from the channel module --- src/nvim/os/msgpack_rpc.c | 453 +++++++++++----------------------------------- 1 file changed, 102 insertions(+), 351 deletions(-) (limited to 'src/nvim/os/msgpack_rpc.c') diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index 0d9a7ae3de..5e2ec6aa10 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -3,59 +3,25 @@ #include -#include "nvim/os/msgpack_rpc.h" #include "nvim/vim.h" #include "nvim/memory.h" - -#define REMOTE_FUNCS_IMPL(t, lt) \ - bool msgpack_rpc_to_##lt(msgpack_object *obj, t *arg) \ - { \ - *arg = obj->via.u64; \ - return obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER; \ - } \ - \ - void msgpack_rpc_from_##lt(t result, msgpack_packer *res) \ - { \ - msgpack_pack_uint64(res, result); \ - } - -#define TYPED_ARRAY_IMPL(t, lt) \ - bool msgpack_rpc_to_##lt##array(msgpack_object *obj, t##Array *arg) \ - { \ - if (obj->type != MSGPACK_OBJECT_ARRAY) { \ - return false; \ - } \ - \ - arg->size = obj->via.array.size; \ - arg->items = xcalloc(obj->via.array.size, sizeof(t)); \ - \ - for (size_t i = 0; i < obj->via.array.size; i++) { \ - if (!msgpack_rpc_to_##lt(obj->via.array.ptr + i, &arg->items[i])) { \ - return false; \ - } \ - } \ - \ - return true; \ - } \ - \ - void msgpack_rpc_from_##lt##array(t##Array result, msgpack_packer *res) \ - { \ - msgpack_pack_array(res, result.size); \ - \ - for (size_t i = 0; i < result.size; i++) { \ - msgpack_rpc_from_##lt(result.items[i], res); \ - } \ - } \ - \ - void msgpack_rpc_free_##lt##array(t##Array value) { \ - for (size_t i = 0; i < value.size; i++) { \ - msgpack_rpc_free_##lt(value.items[i]); \ - } \ - \ - free(value.items); \ - } - +#include "nvim/os/wstream.h" +#include "nvim/os/msgpack_rpc.h" +#include "nvim/os/msgpack_rpc_helpers.h" +#include "nvim/func_attr.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "os/msgpack_rpc.c.generated.h" +#endif + +/// Validates the basic structure of the msgpack-rpc call and fills `res` +/// with the basic response structure. +/// +/// @param id The channel id +/// @param req The parsed request object +/// @param res A packer that contains the response void msgpack_rpc_call(uint64_t id, msgpack_object *req, msgpack_packer *res) + FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3) { // The initial response structure is the same no matter what happens, // we set it up here @@ -115,302 +81,19 @@ void msgpack_rpc_call(uint64_t id, msgpack_object *req, msgpack_packer *res) msgpack_rpc_dispatch(id, req, res); } -void msgpack_rpc_message(int type, - uint64_t id, - String method, - Object arg, - msgpack_packer *pac) -{ - msgpack_pack_array(pac, id ? 4 : 3); - msgpack_pack_int(pac, type); - - if (id) { - msgpack_pack_uint64(pac, id); - } - - msgpack_pack_raw(pac, method.size); - msgpack_pack_raw_body(pac, method.data, method.size); - msgpack_rpc_from_object(arg, pac); -} - -void msgpack_rpc_error(char *msg, msgpack_packer *res) -{ - size_t len = strlen(msg); - - // error message - msgpack_pack_raw(res, len); - msgpack_pack_raw_body(res, msg, len); - // Nil result - msgpack_pack_nil(res); -} - -bool msgpack_rpc_to_boolean(msgpack_object *obj, Boolean *arg) -{ - *arg = obj->via.boolean; - return obj->type == MSGPACK_OBJECT_BOOLEAN; -} - -bool msgpack_rpc_to_integer(msgpack_object *obj, Integer *arg) -{ - if (obj->type == MSGPACK_OBJECT_POSITIVE_INTEGER - && obj->via.u64 <= INT64_MAX) { - *arg = (int64_t)obj->via.u64; - return true; - } - - *arg = obj->via.i64; - return obj->type == MSGPACK_OBJECT_NEGATIVE_INTEGER; -} - -bool msgpack_rpc_to_float(msgpack_object *obj, Float *arg) -{ - *arg = obj->via.dec; - return obj->type == MSGPACK_OBJECT_DOUBLE; -} - -bool msgpack_rpc_to_string(msgpack_object *obj, String *arg) -{ - if (obj->type != MSGPACK_OBJECT_RAW) { - return false; - } - - arg->data = xmemdupz(obj->via.raw.ptr, obj->via.raw.size); - arg->size = obj->via.raw.size; - return true; -} - -bool msgpack_rpc_to_object(msgpack_object *obj, Object *arg) -{ - switch (obj->type) { - case MSGPACK_OBJECT_NIL: - arg->type = kObjectTypeNil; - return true; - - case MSGPACK_OBJECT_BOOLEAN: - arg->type = kObjectTypeBoolean; - return msgpack_rpc_to_boolean(obj, &arg->data.boolean); - - case MSGPACK_OBJECT_POSITIVE_INTEGER: - case MSGPACK_OBJECT_NEGATIVE_INTEGER: - arg->type = kObjectTypeInteger; - return msgpack_rpc_to_integer(obj, &arg->data.integer); - - case MSGPACK_OBJECT_DOUBLE: - arg->type = kObjectTypeFloat; - return msgpack_rpc_to_float(obj, &arg->data.floating); - - case MSGPACK_OBJECT_RAW: - arg->type = kObjectTypeString; - return msgpack_rpc_to_string(obj, &arg->data.string); - - case MSGPACK_OBJECT_ARRAY: - arg->type = kObjectTypeArray; - return msgpack_rpc_to_array(obj, &arg->data.array); - - case MSGPACK_OBJECT_MAP: - arg->type = kObjectTypeDictionary; - return msgpack_rpc_to_dictionary(obj, &arg->data.dictionary); - - default: - return false; - } -} - -bool msgpack_rpc_to_position(msgpack_object *obj, Position *arg) -{ - return obj->type == MSGPACK_OBJECT_ARRAY - && obj->via.array.size == 2 - && msgpack_rpc_to_integer(obj->via.array.ptr, &arg->row) - && msgpack_rpc_to_integer(obj->via.array.ptr + 1, &arg->col); -} - - -bool msgpack_rpc_to_array(msgpack_object *obj, Array *arg) -{ - if (obj->type != MSGPACK_OBJECT_ARRAY) { - return false; - } - - arg->size = obj->via.array.size; - arg->items = xcalloc(obj->via.array.size, sizeof(Object)); - - for (uint32_t i = 0; i < obj->via.array.size; i++) { - if (!msgpack_rpc_to_object(obj->via.array.ptr + i, &arg->items[i])) { - return false; - } - } - - return true; -} - -bool msgpack_rpc_to_dictionary(msgpack_object *obj, Dictionary *arg) -{ - if (obj->type != MSGPACK_OBJECT_MAP) { - return false; - } - - arg->size = obj->via.array.size; - arg->items = xcalloc(obj->via.map.size, sizeof(KeyValuePair)); - - - for (uint32_t i = 0; i < obj->via.map.size; i++) { - if (!msgpack_rpc_to_string(&obj->via.map.ptr[i].key, - &arg->items[i].key)) { - return false; - } - - if (!msgpack_rpc_to_object(&obj->via.map.ptr[i].val, - &arg->items[i].value)) { - return false; - } - } - - return true; -} - -void msgpack_rpc_from_boolean(Boolean result, msgpack_packer *res) -{ - if (result) { - msgpack_pack_true(res); - } else { - msgpack_pack_false(res); - } -} - -void msgpack_rpc_from_integer(Integer result, msgpack_packer *res) -{ - msgpack_pack_int64(res, result); -} - -void msgpack_rpc_from_float(Float result, msgpack_packer *res) -{ - msgpack_pack_double(res, result); -} - -void msgpack_rpc_from_string(String result, msgpack_packer *res) -{ - msgpack_pack_raw(res, result.size); - msgpack_pack_raw_body(res, result.data, result.size); -} - -void msgpack_rpc_from_object(Object result, msgpack_packer *res) -{ - switch (result.type) { - case kObjectTypeNil: - msgpack_pack_nil(res); - break; - - case kObjectTypeBoolean: - msgpack_rpc_from_boolean(result.data.boolean, res); - break; - - case kObjectTypeInteger: - msgpack_rpc_from_integer(result.data.integer, res); - break; - - case kObjectTypeFloat: - msgpack_rpc_from_float(result.data.floating, res); - break; - - case kObjectTypeString: - msgpack_rpc_from_string(result.data.string, res); - break; - - case kObjectTypeArray: - msgpack_rpc_from_array(result.data.array, res); - break; - - case kObjectTypeDictionary: - msgpack_rpc_from_dictionary(result.data.dictionary, res); - break; - - default: - abort(); - } -} - -void msgpack_rpc_from_position(Position result, msgpack_packer *res) -{ - msgpack_pack_array(res, 2);; - msgpack_pack_int64(res, result.row); - msgpack_pack_int64(res, result.col); -} - -void msgpack_rpc_from_array(Array result, msgpack_packer *res) -{ - msgpack_pack_array(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_object(result.items[i], res); - } -} - -void msgpack_rpc_from_dictionary(Dictionary result, msgpack_packer *res) -{ - msgpack_pack_map(res, result.size); - - for (size_t i = 0; i < result.size; i++) { - msgpack_rpc_from_string(result.items[i].key, res); - msgpack_rpc_from_object(result.items[i].value, res); - } -} - -void msgpack_rpc_free_string(String value) -{ - if (!value.data) { - return; - } - - free(value.data); -} - -void msgpack_rpc_free_object(Object value) -{ - switch (value.type) { - case kObjectTypeNil: - case kObjectTypeBoolean: - case kObjectTypeInteger: - case kObjectTypeFloat: - break; - - case kObjectTypeString: - msgpack_rpc_free_string(value.data.string); - break; - - case kObjectTypeArray: - msgpack_rpc_free_array(value.data.array); - break; - - case kObjectTypeDictionary: - msgpack_rpc_free_dictionary(value.data.dictionary); - break; - - default: - abort(); - } -} - -void msgpack_rpc_free_array(Array value) -{ - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_object(value.items[i]); - } - - free(value.items); -} - -void msgpack_rpc_free_dictionary(Dictionary value) -{ - for (uint32_t i = 0; i < value.size; i++) { - msgpack_rpc_free_string(value.items[i].key); - msgpack_rpc_free_object(value.items[i].value); - } - - free(value.items); -} - +/// Try to unpack a msgpack document from the data in the unpacker buffer. This +/// function is a replacement to msgpack.h `msgpack_unpack_next` that lets +/// the called know if the unpacking failed due to bad input or due to missing +/// data. +/// +/// @param unpacker The unpacker containing the parse buffer +/// @param result The result which will contain the parsed object +/// @return kUnpackResultOk : An object was parsed +/// kUnpackResultFail : Got bad input +/// kUnpackResultNeedMore: Need more data UnpackResult msgpack_rpc_unpack(msgpack_unpacker* unpacker, msgpack_unpacked* result) + FUNC_ATTR_NONNULL_ALL { if (result->zone != NULL) { msgpack_zone_free(result->zone); @@ -434,12 +117,80 @@ UnpackResult msgpack_rpc_unpack(msgpack_unpacker* unpacker, return kUnpackResultNeedMore; } -REMOTE_FUNCS_IMPL(Buffer, buffer) -REMOTE_FUNCS_IMPL(Window, window) -REMOTE_FUNCS_IMPL(Tabpage, tabpage) +/// Finishes the msgpack-rpc call with an error message. +/// +/// @param msg The error message +/// @param res A packer that contains the response +void msgpack_rpc_error(char *msg, msgpack_packer *res) + FUNC_ATTR_NONNULL_ALL +{ + size_t len = strlen(msg); -TYPED_ARRAY_IMPL(Buffer, buffer) -TYPED_ARRAY_IMPL(Window, window) -TYPED_ARRAY_IMPL(Tabpage, tabpage) -TYPED_ARRAY_IMPL(String, string) + // error message + msgpack_pack_raw(res, len); + msgpack_pack_raw_body(res, msg, len); + // Nil result + msgpack_pack_nil(res); +} + +/// Serializes a msgpack-rpc request or notification(id == 0) +WBuffer *serialize_request(uint64_t id, + String method, + Object arg, + msgpack_sbuffer *sbuffer) + FUNC_ATTR_NONNULL_ALL +{ + msgpack_packer pac; + msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); + msgpack_pack_array(&pac, id ? 4 : 3); + msgpack_pack_int(&pac, id ? 0 : 2); + + if (id) { + msgpack_pack_uint64(&pac, id); + } + + msgpack_pack_raw(&pac, method.size); + msgpack_pack_raw_body(&pac, method.data, method.size); + msgpack_rpc_from_object(arg, &pac); + WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), + sbuffer->size, + free); + msgpack_rpc_free_object(arg); + msgpack_sbuffer_clear(sbuffer); + return rv; +} + +/// Serializes a msgpack-rpc response +WBuffer *serialize_response(uint64_t id, + String err, + Object arg, + msgpack_sbuffer *sbuffer) + FUNC_ATTR_NONNULL_ALL +{ + msgpack_packer pac; + msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); + msgpack_pack_array(&pac, 4); + msgpack_pack_int(&pac, 1); + msgpack_pack_uint64(&pac, id); + + if (err.size) { + // error message + msgpack_pack_raw(&pac, err.size); + msgpack_pack_raw_body(&pac, err.data, err.size); + // Nil result + msgpack_pack_nil(&pac); + } else { + // Nil error + msgpack_pack_nil(&pac); + // Return value + msgpack_rpc_from_object(arg, &pac); + } + + WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), + sbuffer->size, + free); + msgpack_rpc_free_object(arg); + msgpack_sbuffer_clear(sbuffer); + return rv; +} -- cgit From 296da85198a7d5da36dbb2e6f213edb5da511635 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Mon, 23 Jun 2014 11:52:42 -0300 Subject: channel/msgpack_rpc: Refactor API dispatching This is how API dispatching worked before this commit: - The generated `msgpack_rpc_dispatch` function receives a the `msgpack_packer` argument. - The response is incrementally built while validating/calling the API. - Return values/errors are also packed into the `msgpack_packer` while the final response is being calculated. Now the `msgpack_packer` argument is no longer provided, and the `msgpack_rpc_dispatch` function returns `Object`/`Error` values to `msgpack_rpc_call`, which will use those values to build the response in a single pass. This was done because the new `channel_send_call` function created the possibility of having recursive API invocations, and this wasn't possible when sharing a single `msgpack_sbuffer` across call frames(it was shared implicitly through the `msgpack_packer` instance). Since we only start to build the response when the necessary information has been computed, it's now safe to share a single `msgpack_sbuffer` instance across all channels and API invocations. Some other changes also had to be performed: - Handling of the metadata discover was moved to `msgpack_rpc_call` - Expose more types as subtypes of `Object`, this was required to forward the return value from `msgpack_rpc_dispatch` to `msgpack_rpc_call` - Added more helper macros for casting API types to `Object` any --- src/nvim/os/msgpack_rpc.c | 162 +++++++++++++++++++++++++++++----------------- 1 file changed, 101 insertions(+), 61 deletions(-) (limited to 'src/nvim/os/msgpack_rpc.c') diff --git a/src/nvim/os/msgpack_rpc.c b/src/nvim/os/msgpack_rpc.c index 5e2ec6aa10..85569372da 100644 --- a/src/nvim/os/msgpack_rpc.c +++ b/src/nvim/os/msgpack_rpc.c @@ -8,77 +8,53 @@ #include "nvim/os/wstream.h" #include "nvim/os/msgpack_rpc.h" #include "nvim/os/msgpack_rpc_helpers.h" +#include "nvim/api/private/helpers.h" #include "nvim/func_attr.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/msgpack_rpc.c.generated.h" #endif +extern const uint8_t msgpack_metadata[]; +extern const unsigned int msgpack_metadata_size; + /// Validates the basic structure of the msgpack-rpc call and fills `res` /// with the basic response structure. /// -/// @param id The channel id +/// @param channel_id The channel id /// @param req The parsed request object /// @param res A packer that contains the response -void msgpack_rpc_call(uint64_t id, msgpack_object *req, msgpack_packer *res) - FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3) +WBuffer *msgpack_rpc_call(uint64_t channel_id, + msgpack_object *req, + msgpack_sbuffer *sbuffer) + FUNC_ATTR_NONNULL_ARG(2) + FUNC_ATTR_NONNULL_ARG(3) { - // The initial response structure is the same no matter what happens, - // we set it up here - // Array of size 4 - msgpack_pack_array(res, 4); - // Response type is 1 - msgpack_pack_int(res, 1); - - // Validate the basic structure of the msgpack-rpc payload - if (req->type != MSGPACK_OBJECT_ARRAY) { - msgpack_pack_int(res, 0); // no message id yet - msgpack_rpc_error("Request is not an array", res); - return; - } - - if (req->via.array.size != 4) { - msgpack_pack_int(res, 0); // no message id yet - char error_msg[256]; - snprintf(error_msg, - sizeof(error_msg), - "Request array size is %u, it should be 4", - req->via.array.size); - msgpack_rpc_error(error_msg, res); - return; - } + uint64_t response_id; + char *err = msgpack_rpc_validate(&response_id, req); - if (req->via.array.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - msgpack_pack_int(res, 0); // no message id yet - msgpack_rpc_error("Id must be a positive integer", res); - return; + if (err) { + return serialize_response(response_id, err, NIL, sbuffer); } - // Set the response id, which is the same as the request - msgpack_pack_uint64(res, req->via.array.ptr[1].via.u64); + uint64_t method_id = req->via.array.ptr[2].via.u64; - if (req->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - msgpack_rpc_error("Message type must be an integer", res); - return; + if (method_id == 0) { + return serialize_metadata(response_id, channel_id, sbuffer); } - if (req->via.array.ptr[0].via.u64 != 0) { - msgpack_rpc_error("Message type must be 0", res); - return; - } + // dispatch the call + Error error = { .set = false }; + Object rv = msgpack_rpc_dispatch(channel_id, method_id, req, &error); + // send the response + msgpack_packer response; + msgpack_packer_init(&response, sbuffer, msgpack_sbuffer_write); - if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - msgpack_rpc_error("Method id must be a positive integer", res); - return; + if (error.set) { + return serialize_response(response_id, error.msg, NIL, sbuffer); } - if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { - msgpack_rpc_error("Paremeters must be an array", res); - return; - } - - // dispatch the message - msgpack_rpc_dispatch(id, req, res); + return serialize_response(response_id, NULL, rv, sbuffer); } /// Try to unpack a msgpack document from the data in the unpacker buffer. This @@ -134,19 +110,19 @@ void msgpack_rpc_error(char *msg, msgpack_packer *res) } /// Serializes a msgpack-rpc request or notification(id == 0) -WBuffer *serialize_request(uint64_t id, +WBuffer *serialize_request(uint64_t request_id, String method, Object arg, msgpack_sbuffer *sbuffer) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(4) { msgpack_packer pac; msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); - msgpack_pack_array(&pac, id ? 4 : 3); - msgpack_pack_int(&pac, id ? 0 : 2); + msgpack_pack_array(&pac, request_id ? 4 : 3); + msgpack_pack_int(&pac, request_id ? 0 : 2); - if (id) { - msgpack_pack_uint64(&pac, id); + if (request_id) { + msgpack_pack_uint64(&pac, request_id); } msgpack_pack_raw(&pac, method.size); @@ -161,19 +137,20 @@ WBuffer *serialize_request(uint64_t id, } /// Serializes a msgpack-rpc response -WBuffer *serialize_response(uint64_t id, - String err, +WBuffer *serialize_response(uint64_t response_id, + char *err_msg, Object arg, msgpack_sbuffer *sbuffer) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(4) { msgpack_packer pac; msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); msgpack_pack_array(&pac, 4); msgpack_pack_int(&pac, 1); - msgpack_pack_uint64(&pac, id); + msgpack_pack_uint64(&pac, response_id); - if (err.size) { + if (err_msg) { + String err = {.size = strlen(err_msg), .data = err_msg}; // error message msgpack_pack_raw(&pac, err.size); msgpack_pack_raw_body(&pac, err.data, err.size); @@ -194,3 +171,66 @@ WBuffer *serialize_response(uint64_t id, return rv; } +WBuffer *serialize_metadata(uint64_t id, + uint64_t channel_id, + msgpack_sbuffer *sbuffer) + FUNC_ATTR_NONNULL_ALL +{ + msgpack_packer pac; + msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); + msgpack_pack_array(&pac, 4); + msgpack_pack_int(&pac, 1); + msgpack_pack_uint64(&pac, id); + // Nil error + msgpack_pack_nil(&pac); + // The result is the [channel_id, metadata] array + msgpack_pack_array(&pac, 2); + msgpack_pack_uint64(&pac, channel_id); + msgpack_pack_raw(&pac, msgpack_metadata_size); + msgpack_pack_raw_body(&pac, msgpack_metadata, msgpack_metadata_size); + WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), + sbuffer->size, + free); + msgpack_sbuffer_clear(sbuffer); + return rv; +} + +static char *msgpack_rpc_validate(uint64_t *response_id, msgpack_object *req) +{ + // response id not known yet + + *response_id = 0; + // Validate the basic structure of the msgpack-rpc payload + if (req->type != MSGPACK_OBJECT_ARRAY) { + return "Request is not an array"; + } + + if (req->via.array.size != 4) { + return "Request array size should be 4"; + } + + if (req->via.array.ptr[1].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { + return "Id must be a positive integer"; + } + + // Set the response id, which is the same as the request + *response_id = req->via.array.ptr[1].via.u64; + + if (req->via.array.ptr[0].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { + return "Message type must be an integer"; + } + + if (req->via.array.ptr[0].via.u64 != 0) { + return "Message type must be 0"; + } + + if (req->via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { + return "Method id must be a positive integer"; + } + + if (req->via.array.ptr[3].type != MSGPACK_OBJECT_ARRAY) { + return "Paremeters must be an array"; + } + + return NULL; +} -- cgit