You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

233 lines
4.6 KiB

#include "wsfs/jsonrpc.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include "wsfs/response_parser.h"
#define DEFAULT_TIMEOUT_SECS 10
struct wsfs_jsonrpc_response
{
int id;
wsfs_status status;
json_t * result;
};
struct wsfs_jsonrpc
{
pthread_mutex_t lock;
pthread_cond_t finished;
pthread_condattr_t finished_attributes;
wsfs_create_message_fn * create_message;
wsfs_send_message_fn * send_message;
void * user_data;
bool is_finished;
struct wsfs_jsonrpc_response response;
};
static json_t * wsfs_request_create(
char const * method,
int id,
char const * param_info,
va_list args)
{
json_t * request = json_object();
json_object_set_new(request, "method", json_string(method));
json_t * params = json_array();
for (char const * param_type = param_info; '\0' != *param_type; param_type++)
{
switch(*param_type)
{
case 's':
{
char const * const value = va_arg(args, char const *);
json_array_append_new(params, json_string(value));
}
break;
case 'i':
{
int const value = va_arg(args, int);
json_array_append_new(params, json_integer(value));
}
break;
default:
fprintf(stderr, "fatal: unknown param_type '%c'\n", *param_type);
exit(EXIT_FAILURE);
break;
}
}
json_object_set_new(request, "params", params);
json_object_set_new(request, "id", json_integer(id));
return request;
}
wsfs_status wsfs_jsonrpc_invoke(
struct wsfs_jsonrpc * rpc,
json_t * * result,
char const * method,
char const * param_info,
...
)
{
// enqueue message
pthread_mutex_lock(&rpc->lock);
wsfs_status status = WSFS_BAD;
char * message = NULL;
size_t length = 0;
if (-1 == rpc->response.id) {
va_list args;
va_start(args, param_info);
json_t * request = wsfs_request_create(method, 42, param_info, args);
va_end(args);
length = json_dumpb(request, NULL, 0, JSON_COMPACT);
if (0 < length)
{
rpc->is_finished = false;
rpc->response.id = 42;
rpc->response.result = NULL;
rpc->response.status = WSFS_GOOD;
message = rpc->create_message(length);
json_dumpb(request, message, length, JSON_COMPACT);
}
json_decref(request);
}
pthread_mutex_unlock(&rpc->lock);
if (NULL != message)
{
bool const success = rpc->send_message(message, length, rpc->user_data);
// wait for answer
pthread_mutex_lock(&rpc->lock);
if (success)
{
struct timespec timeout;
clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec += DEFAULT_TIMEOUT_SECS;
int rc = 0;
while ((0 == rc) && (!rpc->is_finished)) {
rc = pthread_cond_timedwait(&rpc->finished, &rpc->lock, &timeout);
}
if (rpc->is_finished)
{
status = rpc->response.status;
*result = rpc->response.result;
}
else
{
status = WSFS_BAD_TIMEOUT;
}
}
rpc->response.id = -1;
rpc->response.result = NULL;
rpc->response.status = WSFS_GOOD;
pthread_mutex_unlock(&rpc->lock);
}
return status;
}
struct wsfs_jsonrpc * wsfs_jsonrpc_create(
wsfs_create_message_fn * create_message,
wsfs_send_message_fn * send_message,
void * user_data)
{
struct wsfs_jsonrpc * rpc = malloc(sizeof(struct wsfs_jsonrpc));
if (NULL != rpc)
{
pthread_mutex_init(&rpc->lock, NULL);
pthread_condattr_init(&rpc->finished_attributes);
pthread_condattr_setclock(&rpc->finished_attributes, CLOCK_MONOTONIC);
pthread_cond_init(&rpc->finished, &rpc->finished_attributes);
rpc->create_message = create_message;
rpc->send_message = send_message;
rpc->user_data = user_data;
rpc->is_finished = true;
rpc->response.id = -1;
rpc->response.status = WSFS_GOOD;
rpc->response.result = NULL;
}
return rpc;
}
void wsfs_jsonrpc_set_user_data(
struct wsfs_jsonrpc * rpc,
void * user_data)
{
rpc->user_data = user_data;
}
void wsfs_jsonrpc_dispose(
struct wsfs_jsonrpc * rpc)
{
if (NULL != rpc->response.result)
{
json_decref(rpc->response.result);
}
pthread_cond_destroy(&rpc->finished);
pthread_condattr_destroy(&rpc->finished_attributes);
pthread_mutex_destroy(&rpc->lock);
free(rpc);
}
void wsfs_jsonrpc_on_message(
char const * message,
size_t length,
void * user_data)
{
struct wsfs_jsonrpc * rpc = user_data;
struct wsfs_response response;
wsfs_response_parse(message, length, &response);
if (-1 != response.id)
{
pthread_mutex_lock(&rpc->lock);
if (response.id == rpc->response.id)
{
rpc->is_finished = true;
rpc->response.status = response.status;
rpc->response.result = response.result;
}
else
{
if (NULL != response.result)
{
json_decref(response.result);
}
}
pthread_cond_signal(&rpc->finished);
pthread_mutex_unlock(&rpc->lock);
}
}