Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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