/* This will immediately close a server socket and clean out any pending * requests that are waiting on that socket. * */ void force_disconnect(jsonrpc_server_t* server) { if(!server) { ERR("Trying to disconnect a NULL server.\n"); return; } // clear the netstring buffer when disconnecting free_netstring(server->buffer); server->buffer = NULL; server->status = JSONRPC_SERVER_DISCONNECTED; // close bufferevent bev_disconnect(server->bev); INFO("Disconnected from server %.*s:%d for conn %.*s.\n", STR(server->addr), server->port, STR(server->conn)); /* clean out requests */ jsonrpc_request_t* req = NULL; int key = 0; for (key=0; key < JSONRPC_DEFAULT_HTABLE_SIZE; key++) { for (req = request_table[key]; req != NULL; req = req->next) { if(req->server != NULL && req->server == server) { fail_request(JRPC_ERR_SERVER_DISCONNECT, req, "Failing request for server shutdown"); } } } }
void timeout_cb(int fd, short event, void *arg) { jsonrpc_request_t* req = (jsonrpc_request_t*)arg; if(!req) return; if(!(req->server)) { ERR("No server defined for request\n"); return; } if(schedule_retry(req)<0) { fail_request(JRPC_ERR_TIMEOUT, req, "Request timeout"); } }
void retry_cb(int fd, short event, void* arg) { if(!arg) return; jsonrpc_request_t* req = (jsonrpc_request_t*)arg; if(!(req->cmd)) { ERR("request has no cmd\n"); goto error; } DEBUG("retrying request: id=%d\n", req->id); if(jsonrpc_send(req->cmd->conn, req, 0)<0) { goto error; } CHECK_AND_FREE_EV(req->retry_ev); return; error: fail_request(JRPC_ERR_SEND, req, "Retry failed to send request"); }
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); }
int jsonrpc_send(str conn, jsonrpc_request_t* req, bool notify_only) { char* json = (char*)json_dumps(req->payload, JSON_COMPACT); char* ns; size_t bytes; bytes = netstring_encode_new(&ns, json, (size_t)strlen(json)); bool sent = false; jsonrpc_server_group_t* c_grp = NULL; if(global_server_group != NULL) c_grp = *global_server_group; jsonrpc_server_group_t* p_grp = NULL; jsonrpc_server_group_t* w_grp = NULL; jsonrpc_server_t* s = NULL; server_list_t* tried_servers = NULL; DEBUG("SENDING DATA\n"); for(; c_grp != NULL; c_grp = c_grp->next) { if(strncmp(conn.s, c_grp->conn.s, c_grp->conn.len) != 0) continue; for(p_grp = c_grp->sub_group; p_grp != NULL; p_grp = p_grp->next) { w_grp = p_grp->sub_group; while(!sent) { loadbalance_by_weight(&s, w_grp, tried_servers); if (s == NULL || s->status != JSONRPC_SERVER_CONNECTED) { break; } if(bufferevent_write(s->bev, ns, bytes) == 0) { sent = true; if(!notify_only) { s->req_count++; if (s->hwm > 0 && s->req_count >= s->hwm) { WARN("%.*s:%d in connection group %.*s has exceeded its high water mark (%d)\n", STR(s->addr), s->port, STR(s->conn), s->hwm); } } req->server = s; break; } else { addto_server_list(s, &tried_servers); } } if (sent) { break; } WARN("Failed to send to priority group, %d\n", p_grp->priority); if(p_grp->next != NULL) { INFO("Proceeding to next priority group, %d\n", p_grp->next->priority); } } if (sent) { break; } } if(!sent) { WARN("Failed to send to connection group, \"%.*s\"\n", STR(conn)); if(schedule_retry(req)<0) { fail_request(JRPC_ERR_RETRY, req, "Failed to schedule retry"); } } free_server_list(tried_servers); if(ns) pkg_free(ns); if(json) free(json); if (sent && notify_only == false) { const struct timeval tv = ms_to_tv(req->timeout); req->timeout_ev = evtimer_new(global_ev_base, timeout_cb, (void*)req); if(event_add(req->timeout_ev, &tv)<0) { ERR("event_add failed while setting request timer (%s).", strerror(errno)); return -1; } } return sent; }