Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 272 lines (214 sloc) 6.026 kb
2507fce @nicolasff Added JSON output.
authored
1 #include "cmd.h"
e2f2b36 @nicolasff Added RAW output.
authored
2 #include "server.h"
de5c283 @nicolasff Added IP range restriction.
authored
3 #include "conf.h"
a7bf6f7 @nicolasff ACL refactoring.
authored
4 #include "acl.h"
bbec5c6 @nicolasff Bugfix, restored forced content-type.
authored
5 #include "http.h"
e2f2b36 @nicolasff Added RAW output.
authored
6
7 #include "formats/json.h"
8 #include "formats/raw.h"
1ad059d @nicolasff Started adding support for a custom content-type in a second key.
authored
9 #include "formats/custom-type.h"
2507fce @nicolasff Added JSON output.
authored
10
11 #include <stdlib.h>
12 #include <string.h>
13 #include <hiredis/hiredis.h>
4448b0f @nicolasff Proper decoding of URL parameters.
authored
14 #include <ctype.h>
2507fce @nicolasff Added JSON output.
authored
15
16 struct cmd *
89bb00f @nicolasff Start sending HTTP replies.
authored
17 cmd_new(struct http_client *client, int count) {
2507fce @nicolasff Added JSON output.
authored
18
19 struct cmd *c = calloc(1, sizeof(struct cmd));
20
89bb00f @nicolasff Start sending HTTP replies.
authored
21 c->client = client;
2507fce @nicolasff Added JSON output.
authored
22 c->count = count;
23
1abb414 @nicolasff Final touch to content-type feature.
authored
24 c->argv = calloc(count, sizeof(char*));
25 c->argv_len = calloc(count, sizeof(size_t));
2507fce @nicolasff Added JSON output.
authored
26
27 return c;
28 }
29
30
31 void
32 cmd_free(struct cmd *c) {
33
34 free(c->argv);
35 free(c->argv_len);
36
102f9fc @nicolasff Started adding Accept support.
authored
37 if(c->mime_free) free(c->mime);
38
2507fce @nicolasff Added JSON output.
authored
39 free(c);
40 }
41
7aab776 @nicolasff Cleanup.
authored
42 /**
43 * Detect disconnection of a pub/sub client. We need to clean up the command.
44 */
794a76d @nicolasff Added debug info, trying to find a way to support SUBSCRIBE.
authored
45 void on_http_disconnect(struct evhttp_connection *evcon, void *ctx) {
d8f6460 @nicolasff Pub/sub progress...
authored
46 struct pubsub_client *ps = ctx;
47
48 (void)evcon;
49
bd9f723 @nicolasff Handle broken subscribe connections.
authored
50 /* clean up redis object */
240f05e @nicolasff PUB/SUB seems to work without crashing...
authored
51 redisAsyncFree(ps->s->ac);
bd9f723 @nicolasff Handle broken subscribe connections.
authored
52
53 /* clean up command object */
54 if(ps->cmd) {
55 cmd_free(ps->cmd);
56 }
d8f6460 @nicolasff Pub/sub progress...
authored
57 free(ps);
794a76d @nicolasff Added debug info, trying to find a way to support SUBSCRIBE.
authored
58 }
59
4448b0f @nicolasff Proper decoding of URL parameters.
authored
60 /* taken from libevent */
61 static char *
62 decode_uri(const char *uri, size_t length, size_t *out_len, int always_decode_plus) {
63 char c;
64 size_t i, j;
65 int in_query = always_decode_plus;
66
67 char *ret = malloc(length);
68
69 for (i = j = 0; i < length; i++) {
70 c = uri[i];
71 if (c == '?') {
72 in_query = 1;
73 } else if (c == '+' && in_query) {
74 c = ' ';
75 } else if (c == '%' && isxdigit((unsigned char)uri[i+1]) &&
76 isxdigit((unsigned char)uri[i+2])) {
77 char tmp[] = { uri[i+1], uri[i+2], '\0' };
78 c = (char)strtol(tmp, NULL, 16);
79 i += 2;
80 }
81 ret[j++] = c;
82 }
83 *out_len = (size_t)j;
84
85 return ret;
86 }
87
88
de5c283 @nicolasff Added IP range restriction.
authored
89 int
89bb00f @nicolasff Start sending HTTP replies.
authored
90 cmd_run(struct server *s, struct http_client *client,
91 const char *uri, size_t uri_len,
92 const char *body, size_t body_len) {
2507fce @nicolasff Added JSON output.
authored
93
89bb00f @nicolasff Start sending HTTP replies.
authored
94 char *qmark = memchr(uri, '?', uri_len);
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
95 char *slash;
e2f2b36 @nicolasff Added RAW output.
authored
96 const char *p;
2507fce @nicolasff Added JSON output.
authored
97 int cmd_len;
4448b0f @nicolasff Proper decoding of URL parameters.
authored
98 int param_count = 0, cur_param = 1, i;
2507fce @nicolasff Added JSON output.
authored
99
100 struct cmd *cmd;
1ea7cd0 @nicolasff Special GET formatter for key + content-type key.
authored
101
102 formatting_fun f_format;
2507fce @nicolasff Added JSON output.
authored
103
104 /* count arguments */
9d2beac @nicolasff Added JSONP.
authored
105 if(qmark) {
106 uri_len = qmark - uri;
107 }
2507fce @nicolasff Added JSON output.
authored
108 for(p = uri; p && p < uri + uri_len; param_count++) {
89bb00f @nicolasff Start sending HTTP replies.
authored
109 p = memchr(p+1, '/', uri_len - (p+1-uri));
2507fce @nicolasff Added JSON output.
authored
110 }
111
a3ee623 @nicolasff File uploads using HTTP PUT.
authored
112 if(body && body_len) { /* PUT request */
113 param_count++;
114 }
115
89bb00f @nicolasff Start sending HTTP replies.
authored
116 cmd = cmd_new(client, param_count);
2507fce @nicolasff Added JSON output.
authored
117
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
118 /* get output formatting function */
bbec5c6 @nicolasff Bugfix, restored forced content-type.
authored
119 uri_len = cmd_select_format(client, cmd, uri, uri_len, &f_format);
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
120
121 /* check if we only have one command or more. */
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
122 slash = memchr(uri, '/', uri_len);
2507fce @nicolasff Added JSON output.
authored
123 if(slash) {
124 cmd_len = slash - uri;
125 } else {
126 cmd_len = uri_len;
127 }
128
129 /* there is always a first parameter, it's the command name */
130 cmd->argv[0] = uri;
131 cmd->argv_len[0] = cmd_len;
132
2f5454c @nicolasff Merge branch 'master' into pubsub
authored
133
89bb00f @nicolasff Start sending HTTP replies.
authored
134 /* FIXME: check that the client is able to run this command */
135 /*
a7bf6f7 @nicolasff ACL refactoring.
authored
136 if(!acl_allow_command(cmd, s->cfg, rq)) {
de5c283 @nicolasff Added IP range restriction.
authored
137 return -1;
138 }
89bb00f @nicolasff Start sending HTTP replies.
authored
139 */
de5c283 @nicolasff Added IP range restriction.
authored
140
89bb00f @nicolasff Start sending HTTP replies.
authored
141 /* FIXME:check if we have to split the connection */
142 /*
4676cfe @nicolasff Refactoring.
authored
143 if(cmd_is_subscribe(cmd)) {
d8f6460 @nicolasff Pub/sub progress...
authored
144 struct pubsub_client *ps;
6e3c424 @nicolasff Cleanup.
authored
145
d8f6460 @nicolasff Pub/sub progress...
authored
146 ps = calloc(1, sizeof(struct pubsub_client));
147 ps->s = s = server_copy(s);
bd9f723 @nicolasff Handle broken subscribe connections.
authored
148 ps->cmd = cmd;
d8f6460 @nicolasff Pub/sub progress...
authored
149 ps->rq = rq;
150 evhttp_connection_set_closecb(rq->evcon, on_http_disconnect, ps);
2c980a2 @nicolasff First try.
authored
151 }
89bb00f @nicolasff Start sending HTTP replies.
authored
152 */
2c980a2 @nicolasff First try.
authored
153
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
154 /* no args (e.g. INFO command) */
2507fce @nicolasff Added JSON output.
authored
155 if(!slash) {
1ea7cd0 @nicolasff Special GET formatter for key + content-type key.
authored
156 redisAsyncCommandArgv(s->ac, f_format, cmd, 1, cmd->argv, cmd->argv_len);
de5c283 @nicolasff Added IP range restriction.
authored
157 return 0;
2507fce @nicolasff Added JSON output.
authored
158 }
159 p = slash + 1;
160 while(p < uri + uri_len) {
161
162 const char *arg = p;
163 int arg_len;
d9d85ca @nicolasff Fix realloc bug.
authored
164 char *next = memchr(arg, '/', uri_len - (arg-uri));
5ca45a5 @nicolasff Working custom handler.
authored
165 if(!next || next > uri + uri_len) { /* last argument */
166 p = uri + uri_len;
167 arg_len = p - arg;
168 } else { /* found a slash */
2507fce @nicolasff Added JSON output.
authored
169 arg_len = next - arg;
170 p = next + 1;
171 }
172
173 /* record argument */
4448b0f @nicolasff Proper decoding of URL parameters.
authored
174 cmd->argv[cur_param] = decode_uri(arg, arg_len, &cmd->argv_len[cur_param], 1);
2507fce @nicolasff Added JSON output.
authored
175 cur_param++;
176 }
177
a3ee623 @nicolasff File uploads using HTTP PUT.
authored
178 if(body && body_len) { /* PUT request */
179 cmd->argv[cur_param] = body;
180 cmd->argv_len[cur_param] = body_len;
181 }
182
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
183 /* push command to Redis. */
1ea7cd0 @nicolasff Special GET formatter for key + content-type key.
authored
184 redisAsyncCommandArgv(s->ac, f_format, cmd, cmd->count, cmd->argv, cmd->argv_len);
1ad059d @nicolasff Started adding support for a custom content-type in a second key.
authored
185
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
186 for(i = 1; i < cur_param; ++i) {
4448b0f @nicolasff Proper decoding of URL parameters.
authored
187 free((char*)cmd->argv[i]);
188 }
189
de5c283 @nicolasff Added IP range restriction.
authored
190 return 0;
2507fce @nicolasff Added JSON output.
authored
191 }
192
6e3c424 @nicolasff Cleanup.
authored
193
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
194 /**
6e3c424 @nicolasff Cleanup.
authored
195 * Select Content-Type and processing function.
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
196 */
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
197 int
bbec5c6 @nicolasff Bugfix, restored forced content-type.
authored
198 cmd_select_format(struct http_client *client, struct cmd *cmd,
199 const char *uri, size_t uri_len, formatting_fun *f_format) {
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
200
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
201 const char *ext;
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
202 int ext_len = -1;
203 unsigned int i;
e2f2b36 @nicolasff Added RAW output.
authored
204
6e3c424 @nicolasff Cleanup.
authored
205 /* those are the available reply formats */
206 struct reply_format {
207 const char *s;
208 size_t sz;
209 formatting_fun f;
210 const char *ct;
211 };
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
212 struct reply_format funs[] = {
213 {.s = "json", .sz = 4, .f = json_reply, .ct = "application/json"},
214 {.s = "raw", .sz = 3, .f = raw_reply, .ct = "binary/octet-stream"},
4abc4fc @nicolasff Support for .bin as binary/octet-stream
authored
215
216 {.s = "bin", .sz = 3, .f = custom_type_reply, .ct = "binary/octet-stream"},
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
217 {.s = "txt", .sz = 3, .f = custom_type_reply, .ct = "text/plain"},
218 {.s = "html", .sz = 4, .f = custom_type_reply, .ct = "text/html"},
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
219 {.s = "xhtml", .sz = 5, .f = custom_type_reply, .ct = "application/xhtml+xml"},
220 {.s = "xml", .sz = 3, .f = custom_type_reply, .ct = "text/xml"},
221
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
222 {.s = "png", .sz = 3, .f = custom_type_reply, .ct = "image/png"},
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
223 {.s = "jpg", .sz = 3, .f = custom_type_reply, .ct = "image/jpeg"},
224 {.s = "jpeg", .sz = 4, .f = custom_type_reply, .ct = "image/jpeg"},
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
225 };
e2f2b36 @nicolasff Added RAW output.
authored
226
6e3c424 @nicolasff Cleanup.
authored
227 /* default */
1ea7cd0 @nicolasff Special GET formatter for key + content-type key.
authored
228 *f_format = json_reply;
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
229
230 /* find extension */
231 for(ext = uri + uri_len - 1; ext != uri && *ext != '/'; --ext) {
232 if(*ext == '.') {
233 ext++;
234 ext_len = uri + uri_len - ext;
e2f2b36 @nicolasff Added RAW output.
authored
235 break;
236 }
237 }
6e3c424 @nicolasff Cleanup.
authored
238 if(!ext_len) return uri_len; /* nothing found */
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
239
240 /* find function for the given extension */
241 for(i = 0; i < sizeof(funs)/sizeof(funs[0]); ++i) {
242 if(ext_len == (int)funs[i].sz && strncmp(ext, funs[i].s, ext_len) == 0) {
102f9fc @nicolasff Started adding Accept support.
authored
243
244 if(cmd->mime_free) free(cmd->mime);
245 cmd->mime = (char*)funs[i].ct;
246 cmd->mime_free = 0;
247
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
248 *f_format = funs[i].f;
249 }
250 }
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
251
89bb00f @nicolasff Start sending HTTP replies.
authored
252 /* FIXME:the user can force it with ?type=some/thing */
bbec5c6 @nicolasff Bugfix, restored forced content-type.
authored
253 if(client->qs_type.s) {
254 *f_format = custom_type_reply;
255 cmd->mime = strdup(client->qs_type.s);
256 cmd->mime_free = 1;
71bc9e3 @nicolasff Added a few content types, added support for ?type.
authored
257 }
bbec5c6 @nicolasff Bugfix, restored forced content-type.
authored
258
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
259 return uri_len - ext_len - 1;
e2f2b36 @nicolasff Added RAW output.
authored
260 }
4676cfe @nicolasff Refactoring.
authored
261
262 int
263 cmd_is_subscribe(struct cmd *cmd) {
264
981fd54 @nicolasff Fix Keep-Alive.
authored
265 if(cmd->count >= 1 &&
266 (strncasecmp(cmd->argv[0], "SUBSCRIBE", cmd->argv_len[0]) == 0 ||
267 strncasecmp(cmd->argv[0], "PSUBSCRIBE", cmd->argv_len[0]) == 0)) {
4676cfe @nicolasff Refactoring.
authored
268 return 1;
269 }
270 return 0;
271 }
Something went wrong with that request. Please try again.