Skip to content

HTTPS clone URL

Subversion checkout URL

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