in libs/dfi/src/json_rpc.c [95:200]
int jsonRpc_call(const dyn_interface_type* intf, void* service, const char* request, char** out) {
int status = OK;
json_error_t error;
json_auto_t* js_request = json_loads(request, 0, &error);
if (js_request == NULL) {
celix_err_pushf("Got json error: %s", error.text);
return ERROR;
}
json_t* arguments = NULL;
const char* sig;
if (json_unpack(js_request, "{s:s}", "m", &sig) != 0) {
celix_err_push("Error getting method signature");
return ERROR;
}
arguments = json_object_get(js_request, "a");
if (arguments == NULL || !json_is_array(arguments)) {
celix_err_pushf("Error getting arguments array for %s", sig);
return ERROR;
}
const struct method_entry* method = dynInterface_findMethod(intf, sig);
if (method == NULL) {
celix_err_pushf("Cannot find method with sig '%s'", sig);
return ERROR;
}
struct generic_service_layout* serv = service;
const struct dyn_function_arguments_head* dynArgs = dynFunction_arguments(method->dynFunc);
const dyn_function_argument_type* last = TAILQ_LAST(dynArgs, dyn_function_arguments_head);
int nrOfArgs = dynFunction_nrOfArguments(method->dynFunc);
if (nrOfArgs > CELIX_JSON_RPC_MAX_ARGS) {
celix_err_pushf("Too many arguments for %s: %d > %d", sig, nrOfArgs, CELIX_JSON_RPC_MAX_ARGS);
return ERROR;
}
void* ptr = NULL;
void* ptrToPtr = &ptr;
celix_auto(celix_rpc_args_t) rpcArgs = { dynArgs, {0} };
rpcArgs.args[0] = &serv->handle;
--nrOfArgs;
if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) {
const dyn_type *subType = dynType_typedPointer_getTypedType(dynType_realType(last->type));
rpcArgs.args[last->index] = &ptr;
if ((status = dynType_alloc(subType, &ptr)) != OK) {
celix_err_pushf("Error allocating memory for pre-allocated output argument of %s", sig);
return ERROR;
}
--nrOfArgs;
} else if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) {
rpcArgs.args[last->index] = &ptrToPtr;
--nrOfArgs;
}
if ((size_t)nrOfArgs != json_array_size(arguments)) {
celix_err_pushf("Wrong number of standard arguments for %s. Expected %d, got %zu",
sig, nrOfArgs, json_array_size(arguments));
return ERROR;
}
//setup and deserialize input
dyn_function_argument_type* entry = NULL;
TAILQ_FOREACH(entry, dynArgs, entries) {
if (entry->argumentMeta != DYN_FUNCTION_ARGUMENT_META__STD) {
continue;
}
status = jsonSerializer_deserializeJson(entry->type, json_array_get(arguments, entry->index-1), &(rpcArgs.args[entry->index]));
if (status != OK) {
celix_err_pushf("Error deserializing argument %d for %s", entry->index, sig);
return status;
}
}
ffi_sarg returnVal = 1;
(void)dynFunction_call(method->dynFunc, serv->methods[method->index], (void *) &returnVal, rpcArgs.args);
int funcCallStatus = (int)returnVal;
//serialize output
json_auto_t* jsonResult = NULL;
if (funcCallStatus == 0) {
const dyn_type* argType = dynType_realType(last->type);
if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__PRE_ALLOCATED_OUTPUT) {
status = jsonSerializer_serializeJson(argType, rpcArgs.args[last->index], &jsonResult);
} else if (last->argumentMeta == DYN_FUNCTION_ARGUMENT_META__OUTPUT) {
status = jsonSerializer_serializeJson(dynType_typedPointer_getTypedType(argType), (void*) &ptr, &jsonResult);
}
if (status != OK) {
celix_err_pushf("Error serializing result for %s", sig);
return status;
}
}
celix_rpcArgs_cleanup(&rpcArgs);
json_auto_t* payload = json_object();
if (funcCallStatus == 0) {
if (jsonResult != NULL) {
status = json_object_set_new_nocheck(payload, "r", celix_steal_ptr(jsonResult));
}
} else {
status = json_object_set_new_nocheck(payload, "e", json_integer(funcCallStatus));
}
if (status != 0) {
celix_err_pushf("Error generating response payload for %s", sig);
return ERROR;
}
//use JSON_COMPACT to reduce the size of the JSON string.
*out = json_dumps(payload, JSON_COMPACT | JSON_ENCODE_ANY);
return (*out != NULL) ? OK : ERROR;
}