Beispiel #1
0
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);
}
Beispiel #2
0
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;
}
Beispiel #3
0
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);
	}
}
Beispiel #4
0
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;
}
Beispiel #5
0
Datei: cmd.c Projekt: orls/webdis
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;
}