void cmd_free(struct cmd *c) { int i; if(!c) return; for(i = 0; i < c->count; ++i) { free((char*)c->argv[i]); } free(c->jsonp); free(c->separator); free(c->if_none_match); if(c->mime_free) free(c->mime); if (c->ac && /* we have a connection */ (c->database != c->w->s->cfg->database /* custom DB */ || cmd_is_subscribe(c))) { pool_free_context(c->ac); } free(c->argv); free(c->argv_len); free(c); }
static int ws_execute(struct http_client *c, const char *frame, size_t frame_len) { struct cmd*(*fun_extract)(struct http_client *, const char *, size_t) = NULL; formatting_fun fun_reply = NULL; if((c->path_sz == 1 && strncmp(c->path, "/", 1) == 0) || strncmp(c->path, "/.json", 6) == 0) { fun_extract = json_ws_extract; fun_reply = json_reply; } else if(strncmp(c->path, "/.raw", 5) == 0) { fun_extract = raw_ws_extract; fun_reply = raw_reply; } if(fun_extract) { /* Parse websocket frame into a cmd object. */ struct cmd *cmd = fun_extract(c, frame, frame_len); if(cmd) { /* copy client info into cmd. */ cmd_setup(cmd, c); cmd->is_websocket = 1; if (c->pub_sub != NULL) { /* This client already has its own connection * to Redis due to a subscription; use it from * now on. */ cmd->ac = c->pub_sub->ac; } else if (cmd_is_subscribe(cmd)) { /* New subscribe command; make new Redis context * for this client */ cmd->ac = pool_connect(c->w->pool, cmd->database, 0); c->pub_sub = cmd; cmd->pub_sub_client = c; } else { /* get Redis connection from pool */ cmd->ac = (redisAsyncContext*)pool_get_context(c->w->pool); } /* send it off */ cmd_send(cmd, fun_reply); return 0; } } return -1; }
void format_send_reply(struct cmd *cmd, const char *p, size_t sz, const char *content_type) { int free_cmd = 1; const char *ct = cmd->mime?cmd->mime:content_type; struct http_response *resp; if(cmd->is_websocket) { ws_reply(cmd, p, sz); /* If it's a subscribe command, there'll be more responses */ if(!cmd_is_subscribe(cmd)) cmd_free(cmd); return; } if(cmd_is_subscribe(cmd)) { free_cmd = 0; /* start streaming */ if(cmd->started_responding == 0) { cmd->started_responding = 1; resp = http_response_init(cmd->w, 200, "OK"); resp->http_version = cmd->http_version; if(cmd->filename) { http_response_set_header(resp, "Content-Disposition", cmd->filename); } http_response_set_header(resp, "Content-Type", ct); http_response_set_keep_alive(resp, 1); http_response_set_header(resp, "Transfer-Encoding", "chunked"); http_response_set_body(resp, p, sz); http_response_write(resp, cmd->fd); } else { /* Asynchronous chunk write. */ http_response_write_chunk(cmd->fd, cmd->w, p, sz); } } else { /* compute ETag */ char *etag = etag_new(p, sz); if(etag) { /* check If-None-Match */ if(cmd->if_none_match && strcmp(cmd->if_none_match, etag) == 0) { /* SAME! send 304. */ resp = http_response_init(cmd->w, 304, "Not Modified"); } else { resp = http_response_init(cmd->w, 200, "OK"); if(cmd->filename) { http_response_set_header(resp, "Content-Disposition", cmd->filename); } http_response_set_header(resp, "Content-Type", ct); http_response_set_header(resp, "ETag", etag); http_response_set_body(resp, p, sz); } resp->http_version = cmd->http_version; http_response_set_keep_alive(resp, cmd->keep_alive); http_response_write(resp, cmd->fd); free(etag); } else { format_send_error(cmd, 503, "Service Unavailable"); } } /* cleanup */ if(free_cmd) { cmd_free(cmd); } }
cmd_response_t cmd_run(struct worker *w, struct http_client *client, const char *uri, size_t uri_len, const char *body, size_t body_len) { char *qmark = memchr(uri, '?', uri_len); char *slash; const char *p, *cmd_name = uri; int cmd_len; int param_count = 0, cur_param = 1; struct cmd *cmd; formatting_fun f_format; /* count arguments */ if(qmark) { uri_len = qmark - uri; } for(p = uri; p && p < uri + uri_len; param_count++) { p = memchr(p+1, '/', uri_len - (p+1-uri)); } if(body && body_len) { /* PUT request */ param_count++; } if(param_count == 0) { return CMD_PARAM_ERROR; } cmd = cmd_new(param_count); cmd->fd = client->fd; cmd->database = w->s->cfg->database; /* get output formatting function */ uri_len = cmd_select_format(client, cmd, uri, uri_len, &f_format); /* add HTTP info */ cmd_setup(cmd, client); /* check if we only have one command or more. */ slash = memchr(uri, '/', uri_len); if(slash) { /* detect DB number by checking if first arg is only numbers */ int has_db = 1; int db_num = 0; for(p = uri; p < slash; ++p) { if(*p < '0' || *p > '9') { has_db = 0; break; } db_num = db_num * 10 + (*p - '0'); } /* shift to next arg if a db was set up */ if(has_db) { char *next; cmd->database = db_num; cmd->count--; /* overcounted earlier */ cmd_name = slash + 1; if((next = memchr(cmd_name, '/', uri_len - (slash - uri)))) { cmd_len = next - cmd_name; } else { cmd_len = uri_len - (slash - uri + 1); } } else { cmd_len = slash - uri; } } else { cmd_len = uri_len; } /* there is always a first parameter, it's the command name */ cmd->argv[0] = malloc(cmd_len); memcpy(cmd->argv[0], cmd_name, cmd_len); cmd->argv_len[0] = cmd_len; /* check that the client is able to run this command */ if(!acl_allow_command(cmd, w->s->cfg, client)) { cmd_free(cmd); return CMD_ACL_FAIL; } if(cmd_is_subscribe(cmd)) { /* create a new connection to Redis */ cmd->ac = (redisAsyncContext*)pool_connect(w->pool, cmd->database, 0); /* register with the client, used upon disconnection */ client->pub_sub = cmd; cmd->pub_sub_client = client; } else if(cmd->database != w->s->cfg->database) { /* create a new connection to Redis for custom DBs */ cmd->ac = (redisAsyncContext*)pool_connect(w->pool, cmd->database, 0); } else { /* get a connection from the pool */ cmd->ac = (redisAsyncContext*)pool_get_context(w->pool); } /* no args (e.g. INFO command) */ if(!slash) { if(!cmd->ac) { cmd_free(cmd); return CMD_REDIS_UNAVAIL; } redisAsyncCommandArgv(cmd->ac, f_format, cmd, 1, (const char **)cmd->argv, cmd->argv_len); return CMD_SENT; } p = cmd_name + cmd_len + 1; while(p < uri + uri_len) { const char *arg = p; int arg_len; char *next = memchr(arg, '/', uri_len - (arg-uri)); if(!next || next > uri + uri_len) { /* last argument */ p = uri + uri_len; arg_len = p - arg; } else { /* found a slash */ arg_len = next - arg; p = next + 1; } /* record argument */ cmd->argv[cur_param] = decode_uri(arg, arg_len, &cmd->argv_len[cur_param], 1); cur_param++; } if(body && body_len) { /* PUT request */ cmd->argv[cur_param] = malloc(body_len); memcpy(cmd->argv[cur_param], body, body_len); cmd->argv_len[cur_param] = body_len; } /* send it off! */ if(cmd->ac) { cmd_send(cmd, f_format); return CMD_SENT; } /* failed to find a suitable connection to Redis. */ cmd_free(cmd); client->pub_sub = NULL; return CMD_REDIS_UNAVAIL; }
int cmd_run(struct server *s, struct evhttp_request *rq, const char *uri, size_t uri_len, const char *body, size_t body_len) { char *qmark = strchr(uri, '?'); char *slash; const char *p; int cmd_len; int param_count = 0, cur_param = 1, i; struct cmd *cmd; formatting_fun f_format; /* count arguments */ if(qmark) { uri_len = qmark - uri; } for(p = uri; p && p < uri + uri_len; param_count++) { p = strchr(p+1, '/'); } if(body && body_len) { /* PUT request */ param_count++; } cmd = cmd_new(rq, param_count); /* parse URI parameters */ evhttp_parse_query(uri, &cmd->uri_params); /* get output formatting function */ uri_len = cmd_select_format(cmd, uri, uri_len, &f_format); /* check if we only have one command or more. */ slash = memchr(uri, '/', uri_len); if(slash) { cmd_len = slash - uri; } else { cmd_len = uri_len; } /* there is always a first parameter, it's the command name */ cmd->argv[0] = uri; cmd->argv_len[0] = cmd_len; /* check that the client is able to run this command */ if(!acl_allow_command(cmd, s->cfg, rq)) { return -1; } /* check if we have to split the connection */ if(cmd_is_subscribe(cmd)) { struct pubsub_client *ps; ps = calloc(1, sizeof(struct pubsub_client)); ps->s = s = server_copy(s); ps->cmd = cmd; ps->rq = rq; evhttp_connection_set_closecb(rq->evcon, on_http_disconnect, ps); } /* no args (e.g. INFO command) */ if(!slash) { redisAsyncCommandArgv(s->ac, f_format, cmd, 1, cmd->argv, cmd->argv_len); return 0; } p = slash + 1; while(p < uri + uri_len) { const char *arg = p; int arg_len; char *next = strchr(arg, '/'); if(!next || next > uri + uri_len) { /* last argument */ p = uri + uri_len; arg_len = p - arg; } else { /* found a slash */ arg_len = next - arg; p = next + 1; } /* record argument */ cmd->argv[cur_param] = decode_uri(arg, arg_len, &cmd->argv_len[cur_param], 1); cur_param++; } if(body && body_len) { /* PUT request */ cmd->argv[cur_param] = body; cmd->argv_len[cur_param] = body_len; } /* push command to Redis. */ redisAsyncCommandArgv(s->ac, f_format, cmd, cmd->count, cmd->argv, cmd->argv_len); for(i = 1; i < cur_param; ++i) { free((char*)cmd->argv[i]); } return 0; }