Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 346 lines (283 sloc) 8.192 kB
2507fce @nicolasff Added JSON output.
authored
1 #include "cmd.h"
de5c283 @nicolasff Added IP range restriction.
authored
2 #include "conf.h"
a7bf6f7 @nicolasff ACL refactoring.
authored
3 #include "acl.h"
7cfb80d @nicolasff Add client.{c,h}. Needs a lot more refactoring.
authored
4 #include "client.h"
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
5 #include "pool.h"
6 #include "worker.h"
7 #include "http.h"
8 #include "server.h"
e2f2b36 @nicolasff Added RAW output.
authored
9
10 #include "formats/json.h"
e4d2738 @nicolasff BSON support.
authored
11 #include "formats/bson.h"
e2f2b36 @nicolasff Added RAW output.
authored
12 #include "formats/raw.h"
525c63d @nicolasff Added more msgpack support
authored
13 #ifdef MSGPACK
14 #include "formats/msgpack.h"
15 #endif
1ad059d @nicolasff Started adding support for a custom content-type in a second key.
authored
16 #include "formats/custom-type.h"
2507fce @nicolasff Added JSON output.
authored
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <hiredis/hiredis.h>
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
21 #include <hiredis/async.h>
4448b0f @nicolasff Proper decoding of URL parameters.
authored
22 #include <ctype.h>
2507fce @nicolasff Added JSON output.
authored
23
24 struct cmd *
a298d3c @nicolasff Better client/cmd relationship.
authored
25 cmd_new(int count) {
2507fce @nicolasff Added JSON output.
authored
26
27 struct cmd *c = calloc(1, sizeof(struct cmd));
28
29 c->count = count;
30
1abb414 @nicolasff Final touch to content-type feature.
authored
31 c->argv = calloc(count, sizeof(char*));
32 c->argv_len = calloc(count, sizeof(size_t));
2507fce @nicolasff Added JSON output.
authored
33
34 return c;
35 }
36
37
38 void
39 cmd_free(struct cmd *c) {
40
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
41 int i;
a298d3c @nicolasff Better client/cmd relationship.
authored
42 if(!c) return;
43
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
44 for(i = 0; i < c->count; ++i) {
45 free((char*)c->argv[i]);
46 }
2507fce @nicolasff Added JSON output.
authored
47 free(c->argv);
48 free(c->argv_len);
49
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
50 free(c->jsonp);
006186f @nicolasff Added separator param for custom list responses.
authored
51 free(c->separator);
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
52 free(c->if_none_match);
102f9fc @nicolasff Started adding Accept support.
authored
53 if(c->mime_free) free(c->mime);
54
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
55
2507fce @nicolasff Added JSON output.
authored
56 free(c);
57 }
58
4448b0f @nicolasff Proper decoding of URL parameters.
authored
59 /* taken from libevent */
60 static char *
61 decode_uri(const char *uri, size_t length, size_t *out_len, int always_decode_plus) {
62 char c;
63 size_t i, j;
64 int in_query = always_decode_plus;
65
66 char *ret = malloc(length);
67
68 for (i = j = 0; i < length; i++) {
69 c = uri[i];
70 if (c == '?') {
71 in_query = 1;
72 } else if (c == '+' && in_query) {
73 c = ' ';
74 } else if (c == '%' && isxdigit((unsigned char)uri[i+1]) &&
75 isxdigit((unsigned char)uri[i+2])) {
76 char tmp[] = { uri[i+1], uri[i+2], '\0' };
77 c = (char)strtol(tmp, NULL, 16);
78 i += 2;
79 }
80 ret[j++] = c;
81 }
82 *out_len = (size_t)j;
83
84 return ret;
85 }
86
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
87 /* setup headers */
239c900 @nicolasff HTTP version in reply.
authored
88 void
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
89 cmd_setup(struct cmd *cmd, struct http_client *client) {
90
91 int i;
92 cmd->keep_alive = client->keep_alive;
0f7d057 @nicolasff Started making writes async.
authored
93 cmd->w = client->w; /* keep track of the worker */
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
94
95 for(i = 0; i < client->header_count; ++i) {
96 if(strcasecmp(client->headers[i].key, "If-None-Match") == 0) {
97 cmd->if_none_match = calloc(1+client->headers[i].val_sz, 1);
98 memcpy(cmd->if_none_match, client->headers[i].val,
99 client->headers[i].val_sz);
100 } else if(strcasecmp(client->headers[i].key, "Connection") == 0 &&
101 strcasecmp(client->headers[i].val, "Keep-Alive") == 0) {
102 cmd->keep_alive = 1;
103 }
104 }
105
106 if(client->type) { /* transfer pointer ownership */
107 cmd->mime = client->type;
108 cmd->mime_free = 1;
109 client->type = NULL;
110 }
111
112 if(client->jsonp) { /* transfer pointer ownership */
113 cmd->jsonp = client->jsonp;
114 client->jsonp = NULL;
115 }
239c900 @nicolasff HTTP version in reply.
authored
116
006186f @nicolasff Added separator param for custom list responses.
authored
117 if(client->separator) { /* transfer pointer ownership */
118 cmd->separator = client->separator;
119 client->separator = NULL;
120 }
121
683cd77 @nicolasff Added Content-Disposition.
authored
122 if(client->filename) { /* transfer pointer ownership */
123 cmd->filename = client->filename;
124 client->filename = NULL;
125 }
126
239c900 @nicolasff HTTP version in reply.
authored
127 cmd->fd = client->fd;
128 cmd->http_version = client->http_version;
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
129 }
130
4448b0f @nicolasff Proper decoding of URL parameters.
authored
131
bf2f584 @nicolasff Fixed 503 responses when Redis is down.
authored
132 cmd_response_t
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
133 cmd_run(struct worker *w, struct http_client *client,
89bb00f @nicolasff Start sending HTTP replies.
authored
134 const char *uri, size_t uri_len,
135 const char *body, size_t body_len) {
2507fce @nicolasff Added JSON output.
authored
136
89bb00f @nicolasff Start sending HTTP replies.
authored
137 char *qmark = memchr(uri, '?', uri_len);
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
138 char *slash;
e2f2b36 @nicolasff Added RAW output.
authored
139 const char *p;
2507fce @nicolasff Added JSON output.
authored
140 int cmd_len;
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
141 int param_count = 0, cur_param = 1;
2507fce @nicolasff Added JSON output.
authored
142
143 struct cmd *cmd;
1ea7cd0 @nicolasff Special GET formatter for key + content-type key.
authored
144 formatting_fun f_format;
2507fce @nicolasff Added JSON output.
authored
145
146 /* count arguments */
9d2beac @nicolasff Added JSONP.
authored
147 if(qmark) {
148 uri_len = qmark - uri;
149 }
2507fce @nicolasff Added JSON output.
authored
150 for(p = uri; p && p < uri + uri_len; param_count++) {
89bb00f @nicolasff Start sending HTTP replies.
authored
151 p = memchr(p+1, '/', uri_len - (p+1-uri));
2507fce @nicolasff Added JSON output.
authored
152 }
153
a3ee623 @nicolasff File uploads using HTTP PUT.
authored
154 if(body && body_len) { /* PUT request */
155 param_count++;
156 }
70d2e07 @nicolasff Fix memory access on empty commands.
authored
157 if(param_count == 0) {
bf2f584 @nicolasff Fixed 503 responses when Redis is down.
authored
158 return CMD_PARAM_ERROR;
70d2e07 @nicolasff Fix memory access on empty commands.
authored
159 }
a3ee623 @nicolasff File uploads using HTTP PUT.
authored
160
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
161 cmd = cmd_new(param_count);
162 cmd->fd = client->fd;
2507fce @nicolasff Added JSON output.
authored
163
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
164 /* get output formatting function */
bbec5c6 @nicolasff Bugfix, restored forced content-type.
authored
165 uri_len = cmd_select_format(client, cmd, uri, uri_len, &f_format);
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
166
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
167 /* add HTTP info */
168 cmd_setup(cmd, client);
169
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
170 /* check if we only have one command or more. */
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
171 slash = memchr(uri, '/', uri_len);
2507fce @nicolasff Added JSON output.
authored
172 if(slash) {
173 cmd_len = slash - uri;
174 } else {
175 cmd_len = uri_len;
176 }
177
178 /* there is always a first parameter, it's the command name */
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
179 cmd->argv[0] = malloc(cmd_len);
180 memcpy(cmd->argv[0], uri, cmd_len);
2507fce @nicolasff Added JSON output.
authored
181 cmd->argv_len[0] = cmd_len;
182
9ffc519 @nicolasff Fix ACLs
authored
183 /* check that the client is able to run this command */
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
184 if(!acl_allow_command(cmd, w->s->cfg, client)) {
513982d @nicolasff Added asynchronous reconnect, fixed memory leak.
authored
185 cmd_free(cmd);
bf2f584 @nicolasff Fixed 503 responses when Redis is down.
authored
186 return CMD_ACL_FAIL;
de5c283 @nicolasff Added IP range restriction.
authored
187 }
188
4676cfe @nicolasff Refactoring.
authored
189 if(cmd_is_subscribe(cmd)) {
bf2f584 @nicolasff Fixed 503 responses when Redis is down.
authored
190 /* create a new connection to Redis */
b01cb75 @nicolasff Disconnect broken SUBSCRIBE clients.
authored
191 cmd->ac = (redisAsyncContext*)pool_connect(w->pool, 0);
192
193 /* register with the client, used upon disconnection */
194 client->pub_sub = cmd;
9b2a761 @nicolasff Fix for pub/sub clients.
authored
195 cmd->pub_sub_client = client;
bf2f584 @nicolasff Fixed 503 responses when Redis is down.
authored
196 } else {
197 /* get a connection from the pool */
b01cb75 @nicolasff Disconnect broken SUBSCRIBE clients.
authored
198 cmd->ac = (redisAsyncContext*)pool_get_context(w->pool);
2c980a2 @nicolasff First try.
authored
199 }
200
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
201 /* no args (e.g. INFO command) */
2507fce @nicolasff Added JSON output.
authored
202 if(!slash) {
b01cb75 @nicolasff Disconnect broken SUBSCRIBE clients.
authored
203 if(!cmd->ac) {
513982d @nicolasff Added asynchronous reconnect, fixed memory leak.
authored
204 cmd_free(cmd);
a91b7b1 @nicolasff Unavailability bugfix.
authored
205 return CMD_REDIS_UNAVAIL;
206 }
b01cb75 @nicolasff Disconnect broken SUBSCRIBE clients.
authored
207 redisAsyncCommandArgv(cmd->ac, f_format, cmd, 1,
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
208 (const char **)cmd->argv, cmd->argv_len);
bf2f584 @nicolasff Fixed 503 responses when Redis is down.
authored
209 return CMD_SENT;
2507fce @nicolasff Added JSON output.
authored
210 }
211 p = slash + 1;
212 while(p < uri + uri_len) {
213
214 const char *arg = p;
215 int arg_len;
d9d85ca @nicolasff Fix realloc bug.
authored
216 char *next = memchr(arg, '/', uri_len - (arg-uri));
5ca45a5 @nicolasff Working custom handler.
authored
217 if(!next || next > uri + uri_len) { /* last argument */
218 p = uri + uri_len;
219 arg_len = p - arg;
220 } else { /* found a slash */
2507fce @nicolasff Added JSON output.
authored
221 arg_len = next - arg;
222 p = next + 1;
223 }
224
225 /* record argument */
4448b0f @nicolasff Proper decoding of URL parameters.
authored
226 cmd->argv[cur_param] = decode_uri(arg, arg_len, &cmd->argv_len[cur_param], 1);
2507fce @nicolasff Added JSON output.
authored
227 cur_param++;
228 }
229
a3ee623 @nicolasff File uploads using HTTP PUT.
authored
230 if(body && body_len) { /* PUT request */
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
231 cmd->argv[cur_param] = malloc(body_len);
232 memcpy(cmd->argv[cur_param], body, body_len);
a3ee623 @nicolasff File uploads using HTTP PUT.
authored
233 cmd->argv_len[cur_param] = body_len;
234 }
235
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
236 /* send it off! */
b01cb75 @nicolasff Disconnect broken SUBSCRIBE clients.
authored
237 if(cmd->ac) {
238 cmd_send(cmd, f_format);
bf2f584 @nicolasff Fixed 503 responses when Redis is down.
authored
239 return CMD_SENT;
4448b0f @nicolasff Proper decoding of URL parameters.
authored
240 }
bf2f584 @nicolasff Fixed 503 responses when Redis is down.
authored
241 /* failed to find a suitable connection to Redis. */
513982d @nicolasff Added asynchronous reconnect, fixed memory leak.
authored
242 cmd_free(cmd);
9b2a761 @nicolasff Fix for pub/sub clients.
authored
243 client->pub_sub = NULL;
bf2f584 @nicolasff Fixed 503 responses when Redis is down.
authored
244 return CMD_REDIS_UNAVAIL;
2507fce @nicolasff Added JSON output.
authored
245 }
246
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
247 void
b01cb75 @nicolasff Disconnect broken SUBSCRIBE clients.
authored
248 cmd_send(struct cmd *cmd, formatting_fun f_format) {
249 redisAsyncCommandArgv(cmd->ac, f_format, cmd, cmd->count,
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
250 (const char **)cmd->argv, cmd->argv_len);
251 }
6e3c424 @nicolasff Cleanup.
authored
252
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
253 /**
6e3c424 @nicolasff Cleanup.
authored
254 * Select Content-Type and processing function.
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
255 */
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
256 int
bbec5c6 @nicolasff Bugfix, restored forced content-type.
authored
257 cmd_select_format(struct http_client *client, struct cmd *cmd,
258 const char *uri, size_t uri_len, formatting_fun *f_format) {
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
259
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
260 const char *ext;
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
261 int ext_len = -1;
262 unsigned int i;
48b2658 @nicolasff Prevent truncate when the extension is unknown.
authored
263 int found = 0; /* did we match it to a predefined format? */
e2f2b36 @nicolasff Added RAW output.
authored
264
6e3c424 @nicolasff Cleanup.
authored
265 /* those are the available reply formats */
266 struct reply_format {
267 const char *s;
268 size_t sz;
269 formatting_fun f;
270 const char *ct;
271 };
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
272 struct reply_format funs[] = {
273 {.s = "json", .sz = 4, .f = json_reply, .ct = "application/json"},
274 {.s = "raw", .sz = 3, .f = raw_reply, .ct = "binary/octet-stream"},
e4d2738 @nicolasff BSON support.
authored
275 {.s = "bson", .sz = 4, .f = bson_reply, .ct = "application/bson"},
4abc4fc @nicolasff Support for .bin as binary/octet-stream
authored
276
525c63d @nicolasff Added more msgpack support
authored
277 #ifdef MSGPACK
278 {.s = "msg", .sz = 3, .f = msgpack_reply, .ct = "application/x-msgpack"},
279 #endif
280
4abc4fc @nicolasff Support for .bin as binary/octet-stream
authored
281 {.s = "bin", .sz = 3, .f = custom_type_reply, .ct = "binary/octet-stream"},
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
282 {.s = "txt", .sz = 3, .f = custom_type_reply, .ct = "text/plain"},
283 {.s = "html", .sz = 4, .f = custom_type_reply, .ct = "text/html"},
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
284 {.s = "xhtml", .sz = 5, .f = custom_type_reply, .ct = "application/xhtml+xml"},
285 {.s = "xml", .sz = 3, .f = custom_type_reply, .ct = "text/xml"},
286
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
287 {.s = "png", .sz = 3, .f = custom_type_reply, .ct = "image/png"},
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
288 {.s = "jpg", .sz = 3, .f = custom_type_reply, .ct = "image/jpeg"},
289 {.s = "jpeg", .sz = 4, .f = custom_type_reply, .ct = "image/jpeg"},
1b6f36b @nicolasff Added Content-Type for JS and CSS files.
authored
290
7286cfe @nicolasff Send JS in JSON format
authored
291 {.s = "js", .sz = 2, .f = json_reply, .ct = "application/javascript"},
1b6f36b @nicolasff Added Content-Type for JS and CSS files.
authored
292 {.s = "css", .sz = 3, .f = custom_type_reply, .ct = "text/css"},
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
293 };
e2f2b36 @nicolasff Added RAW output.
authored
294
6e3c424 @nicolasff Cleanup.
authored
295 /* default */
1ea7cd0 @nicolasff Special GET formatter for key + content-type key.
authored
296 *f_format = json_reply;
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
297
298 /* find extension */
299 for(ext = uri + uri_len - 1; ext != uri && *ext != '/'; --ext) {
300 if(*ext == '.') {
301 ext++;
302 ext_len = uri + uri_len - ext;
e2f2b36 @nicolasff Added RAW output.
authored
303 break;
304 }
305 }
6e3c424 @nicolasff Cleanup.
authored
306 if(!ext_len) return uri_len; /* nothing found */
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
307
308 /* find function for the given extension */
309 for(i = 0; i < sizeof(funs)/sizeof(funs[0]); ++i) {
310 if(ext_len == (int)funs[i].sz && strncmp(ext, funs[i].s, ext_len) == 0) {
102f9fc @nicolasff Started adding Accept support.
authored
311
312 if(cmd->mime_free) free(cmd->mime);
313 cmd->mime = (char*)funs[i].ct;
314 cmd->mime_free = 0;
315
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
316 *f_format = funs[i].f;
48b2658 @nicolasff Prevent truncate when the extension is unknown.
authored
317 found = 1;
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
318 }
319 }
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
320
6026811 @nicolasff Fix pub/sub. Valgrind is happy.
authored
321 /* the user can force it with ?type=some/thing */
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
322 if(client->type) {
bbec5c6 @nicolasff Bugfix, restored forced content-type.
authored
323 *f_format = custom_type_reply;
5b7aa50 @nicolasff Partial rewrite, adding WebSockets, threads, pool.
authored
324 cmd->mime = strdup(client->type);
bbec5c6 @nicolasff Bugfix, restored forced content-type.
authored
325 cmd->mime_free = 1;
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
326 }
bbec5c6 @nicolasff Bugfix, restored forced content-type.
authored
327
48b2658 @nicolasff Prevent truncate when the extension is unknown.
authored
328 if(found) {
329 return uri_len - ext_len - 1;
330 } else {
331 /* no matching format, use default output with the full argument, extension included. */
332 return uri_len;
333 }
e2f2b36 @nicolasff Added RAW output.
authored
334 }
4676cfe @nicolasff Refactoring.
authored
335
336 int
337 cmd_is_subscribe(struct cmd *cmd) {
338
9378544 @nicolasff Fix subscribe leak.
authored
339 if(cmd->count >= 1 &&
981fd54 @nicolasff Fix Keep-Alive.
authored
340 (strncasecmp(cmd->argv[0], "SUBSCRIBE", cmd->argv_len[0]) == 0 ||
341 strncasecmp(cmd->argv[0], "PSUBSCRIBE", cmd->argv_len[0]) == 0)) {
4676cfe @nicolasff Refactoring.
authored
342 return 1;
343 }
344 return 0;
345 }
Something went wrong with that request. Please try again.