void fail_request(int code, jsonrpc_request_t* req, char* err_str) { char* req_s; char* freeme = NULL; pv_value_t val; json_t* error; if(!req) { null_req: WARN("%s: (null)\n", err_str); goto end; } if(!(req->cmd) || (req->cmd->route.len <= 0)) { no_route: req_s = json_dumps(req->payload, JSON_COMPACT); if(req_s) { WARN("%s: \n%s\n", err_str, req_s); free(req_s); goto end; } goto null_req; } error = internal_error(code, req->payload); jsontoval(&val, &freeme, error); if(error) json_decref(error); if(send_to_script(&val, req->cmd)<0) { goto no_route; } end: if(freeme) free(freeme); if(req) { if(req->cmd) free_req_cmd(req->cmd); free_request(req); } }
int handle_response(json_t* response) { int retval = 0; jsonrpc_request_t* req = NULL; json_t* return_obj = NULL; json_t* internal = NULL; char* freeme = NULL; /* check if json object */ if(!json_is_object(response)){ WARN("jsonrpc response is not an object\n"); return -1; } /* check version */ json_t* version = json_object_get(response, "jsonrpc"); if(!version) { WARN("jsonrpc response does not have a version.\n"); retval = -1; goto end; } const char* version_s = json_string_value(version); if(!version_s){ WARN("jsonrpc response version is not a string.\n"); retval = -1; goto end; } if (strlen(version_s) != (sizeof(JSONRPC_VERSION)-1) || strncmp(version_s, JSONRPC_VERSION, sizeof(JSONRPC_VERSION)-1) != 0) { WARN("jsonrpc response version is not %s. version: %s\n", JSONRPC_VERSION, version_s); retval = -1; goto end; } /* check for an id */ json_t* _id = json_object_get(response, "id"); if(!_id) { WARN("jsonrpc response does not have an id.\n"); retval = -1; goto end; } int id = json_integer_value(_id); if (!(req = pop_request(id))) { /* don't fail the server for an unrecognized id */ retval = 0; goto end; } return_obj = json_object(); json_t* error = json_object_get(response, "error"); // if the error value is null, we don't care bool _error = error && (json_typeof(error) != JSON_NULL); json_t* result = json_object_get(response, "result"); if(_error) { json_object_set(return_obj, "error", error); } if(result) { json_object_set(return_obj, "result", result); } if ((!result && !_error) || (result && _error)) { WARN("bad response\n"); internal = internal_error(JRPC_ERR_BAD_RESP, req->payload); json_object_update(return_obj, internal); if(internal) json_decref(internal); } pv_value_t val; if(jsontoval(&val, &freeme, return_obj)<0) { fail_request( JRPC_ERR_TO_VAL, req, "Failed to convert response json to pv\n"); retval = -1; goto end; } char* error_s = NULL; if(send_to_script(&val, req->cmd)>=0) { goto free_and_end; } if(_error) { // get code from error json_t* _code = json_object_get(error, "code"); if(_code) { int code = json_integer_value(_code); // check if code is in global_retry_ranges retry_range_t* tmpr; for(tmpr = global_retry_ranges; tmpr != NULL; tmpr = tmpr->next) { if((tmpr->start < tmpr->end && tmpr->start <= code && code <= tmpr->end) || (tmpr->end < tmpr->start && tmpr->end <= code && code <= tmpr->start) || (tmpr->start == tmpr->end && tmpr->start == code)) { if(schedule_retry(req)==0) { goto end; } break; } } } error_s = json_dumps(error, JSON_COMPACT); if(error_s) { WARN("Request received an error: \n%s\n", error_s); free(error_s); } else { fail_request( JRPC_ERR_BAD_RESP, req, "Could not convert 'error' response to string"); retval = -1; goto end; } } free_and_end: free_req_cmd(req->cmd); free_request(req); end: if(freeme) free(freeme); if(return_obj) json_decref(return_obj); return retval; }
void cmd_pipe_cb(int fd, short event, void *arg) { struct jsonrpc_pipe_cmd *cmd; if (read(fd, &cmd, sizeof(cmd)) != sizeof(cmd)) { ERR("FATAL ERROR: failed to read from command pipe: %s\n", strerror(errno)); return; } switch(cmd->type) { case CMD_CLOSE: if(cmd->server) { wait_close(cmd->server); } goto end; break; case CMD_RECONNECT: if(cmd->server) { wait_reconnect(cmd->server); } goto end; break; case CMD_CONNECT: if(cmd->server) { bev_connect(cmd->server); } goto end; break; case CMD_UPDATE_SERVER_GROUP: if(cmd->new_grp) { jsonrpc_server_group_t* old_grp = *global_server_group; *global_server_group = cmd->new_grp; free_server_group(&old_grp); } lock_release(jsonrpc_server_group_lock); goto end; break; case CMD_SEND: break; default: ERR("Unrecognized pipe command: %d\n", cmd->type); goto end; break; } /* command is SEND */ jsonrpc_req_cmd_t* req_cmd = cmd->req_cmd; if(req_cmd == NULL) { ERR("req_cmd is NULL. Invalid send command\n"); goto end; } jsonrpc_request_t* req = NULL; req = create_request(req_cmd); if (!req || !req->payload) { json_t* error = internal_error(JRPC_ERR_REQ_BUILD, NULL); pv_value_t val; char* freeme = NULL; jsontoval(&val, &freeme, error); if(req_cmd->route.len <=0 && send_to_script(&val, req_cmd)<0) { ERR("Failed to build request (method: %.*s, params: %.*s)\n", STR(req_cmd->method), STR(req_cmd->params)); } if(freeme) free(freeme); if(error) json_decref(error); free_req_cmd(req_cmd); goto end; } int sent = jsonrpc_send(req_cmd->conn, req, req_cmd->notify_only); char* type; if (sent<0) { if (req_cmd->notify_only == false) { type = "Request"; } else { type = "Notification"; } WARN("%s could not be sent to connection group: %.*s\n", type, STR(req_cmd->conn)); fail_request(JRPC_ERR_SEND, req, "Failed to send request"); } end: free_pipe_cmd(cmd); }