Skip to content
Newer
Older
100644 251 lines (197 sloc) 5.31 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"
e2f2b36 @nicolasff Added RAW output.
authored
5
6 #include "formats/json.h"
7 #include "formats/raw.h"
1ad059d @nicolasff Started adding support for a custom content-type in a second key.
authored
8 #include "formats/custom-type.h"
2507fce @nicolasff Added JSON output.
authored
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <hiredis/hiredis.h>
4448b0f @nicolasff Proper decoding of URL parameters.
authored
13 #include <ctype.h>
2507fce @nicolasff Added JSON output.
authored
14
15 struct cmd *
16 cmd_new(struct evhttp_request *rq, int count) {
17
18 struct cmd *c = calloc(1, sizeof(struct cmd));
19
20 c->rq = rq;
21 c->count = count;
22
1abb414 @nicolasff Final touch to content-type feature.
authored
23 c->argv = calloc(count, sizeof(char*));
24 c->argv_len = calloc(count, sizeof(size_t));
2507fce @nicolasff Added JSON output.
authored
25
26 return c;
27 }
28
29
30 void
31 cmd_free(struct cmd *c) {
32
33 free(c->argv);
34 free(c->argv_len);
35
102f9fc @nicolasff Started adding Accept support.
authored
36 if(c->mime_free) free(c->mime);
37
2507fce @nicolasff Added JSON output.
authored
38 free(c);
39 }
40
7aab776 @nicolasff Cleanup.
authored
41 /**
42 * Detect disconnection of a pub/sub client. We need to clean up the command.
43 */
794a76d @nicolasff Added debug info, trying to find a way to support SUBSCRIBE.
authored
44 void on_http_disconnect(struct evhttp_connection *evcon, void *ctx) {
d8f6460 @nicolasff Pub/sub progress...
authored
45 struct pubsub_client *ps = ctx;
46
47 (void)evcon;
48
117fe91 @nicolasff More HiRedis updates, and JSONP change.
authored
49 if(ps->s->ac->replies.head) {
50 struct cmd *cmd = ps->s->ac->replies.head->privdata;
51 if(cmd) {
52 cmd_free(cmd);
114d6c9 @nicolasff Cleaned up PUB/SUB leaks.
authored
53 }
d8f6460 @nicolasff Pub/sub progress...
authored
54 ps->s->ac->replies.head->privdata = NULL;
55 }
240f05e @nicolasff PUB/SUB seems to work without crashing...
authored
56 redisAsyncFree(ps->s->ac);
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
e2f2b36 @nicolasff Added RAW output.
authored
90 cmd_run(struct server *s, struct evhttp_request *rq,
9d2beac @nicolasff Added JSONP.
authored
91 const char *uri, size_t uri_len) {
2507fce @nicolasff Added JSON output.
authored
92
9d2beac @nicolasff Added JSONP.
authored
93 char *qmark = strchr(uri, '?');
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
94 char *slash;
e2f2b36 @nicolasff Added RAW output.
authored
95 const char *p;
2507fce @nicolasff Added JSON output.
authored
96 int cmd_len;
4448b0f @nicolasff Proper decoding of URL parameters.
authored
97 int param_count = 0, cur_param = 1, i;
2507fce @nicolasff Added JSON output.
authored
98
99 struct cmd *cmd;
1ea7cd0 @nicolasff Special GET formatter for key + content-type key.
authored
100
101 formatting_fun f_format;
2507fce @nicolasff Added JSON output.
authored
102
103 /* count arguments */
9d2beac @nicolasff Added JSONP.
authored
104 if(qmark) {
105 uri_len = qmark - uri;
106 }
2507fce @nicolasff Added JSON output.
authored
107 for(p = uri; p && p < uri + uri_len; param_count++) {
108 p = strchr(p+1, '/');
109 }
110
111 cmd = cmd_new(rq, param_count);
112
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
113 /* parse URI parameters */
114 evhttp_parse_query(uri, &cmd->uri_params);
115
116 /* get output formatting function */
6e3c424 @nicolasff Cleanup.
authored
117 uri_len = cmd_select_format(cmd, uri, uri_len, &f_format);
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
118
119 /* check if we only have one command or more. */
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
120 slash = memchr(uri, '/', uri_len);
2507fce @nicolasff Added JSON output.
authored
121 if(slash) {
122 cmd_len = slash - uri;
123 } else {
124 cmd_len = uri_len;
125 }
126
127 /* there is always a first parameter, it's the command name */
128 cmd->argv[0] = uri;
129 cmd->argv_len[0] = cmd_len;
130
2f5454c @nicolasff Merge branch 'master' into pubsub
authored
131
de5c283 @nicolasff Added IP range restriction.
authored
132 /* check that the client is able to run this command */
a7bf6f7 @nicolasff ACL refactoring.
authored
133 if(!acl_allow_command(cmd, s->cfg, rq)) {
de5c283 @nicolasff Added IP range restriction.
authored
134 return -1;
135 }
136
2c980a2 @nicolasff First try.
authored
137 /* check if we have to split the connection */
4676cfe @nicolasff Refactoring.
authored
138 if(cmd_is_subscribe(cmd)) {
d8f6460 @nicolasff Pub/sub progress...
authored
139 struct pubsub_client *ps;
6e3c424 @nicolasff Cleanup.
authored
140
d8f6460 @nicolasff Pub/sub progress...
authored
141 ps = calloc(1, sizeof(struct pubsub_client));
142 ps->s = s = server_copy(s);
143 ps->rq = rq;
144
145 evhttp_connection_set_closecb(rq->evcon, on_http_disconnect, ps);
2c980a2 @nicolasff First try.
authored
146 }
147
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
148 /* no args (e.g. INFO command) */
2507fce @nicolasff Added JSON output.
authored
149 if(!slash) {
1ea7cd0 @nicolasff Special GET formatter for key + content-type key.
authored
150 redisAsyncCommandArgv(s->ac, f_format, cmd, 1, cmd->argv, cmd->argv_len);
de5c283 @nicolasff Added IP range restriction.
authored
151 return 0;
2507fce @nicolasff Added JSON output.
authored
152 }
153 p = slash + 1;
154 while(p < uri + uri_len) {
155
156 const char *arg = p;
157 int arg_len;
158 char *next = strchr(arg, '/');
5ca45a5 @nicolasff Working custom handler.
authored
159 if(!next || next > uri + uri_len) { /* last argument */
160 p = uri + uri_len;
161 arg_len = p - arg;
162 } else { /* found a slash */
2507fce @nicolasff Added JSON output.
authored
163 arg_len = next - arg;
164 p = next + 1;
165 }
166
167 /* record argument */
4448b0f @nicolasff Proper decoding of URL parameters.
authored
168 cmd->argv[cur_param] = decode_uri(arg, arg_len, &cmd->argv_len[cur_param], 1);
2507fce @nicolasff Added JSON output.
authored
169 cur_param++;
170 }
171
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
172 /* push command to Redis. */
1ea7cd0 @nicolasff Special GET formatter for key + content-type key.
authored
173 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
174
1abb414 @nicolasff Final touch to content-type feature.
authored
175 for(i = 1; i < cmd->count; ++i) {
4448b0f @nicolasff Proper decoding of URL parameters.
authored
176 free((char*)cmd->argv[i]);
177 }
178
de5c283 @nicolasff Added IP range restriction.
authored
179 return 0;
2507fce @nicolasff Added JSON output.
authored
180 }
181
6e3c424 @nicolasff Cleanup.
authored
182
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
183 /**
6e3c424 @nicolasff Cleanup.
authored
184 * Select Content-Type and processing function.
a3aa1a9 @nicolasff Bugfix in RAW mode, more code doc.
authored
185 */
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
186 int
6e3c424 @nicolasff Cleanup.
authored
187 cmd_select_format(struct cmd *cmd, const char *uri, size_t uri_len, formatting_fun *f_format) {
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
188
102f9fc @nicolasff Started adding Accept support.
authored
189 const char *ext, *accept_ct;
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
190 int ext_len = -1;
191 unsigned int i;
e2f2b36 @nicolasff Added RAW output.
authored
192
6e3c424 @nicolasff Cleanup.
authored
193 /* those are the available reply formats */
194 struct reply_format {
195 const char *s;
196 size_t sz;
197 formatting_fun f;
198 const char *ct;
199 };
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
200 struct reply_format funs[] = {
201 {.s = "json", .sz = 4, .f = json_reply, .ct = "application/json"},
202 {.s = "raw", .sz = 3, .f = raw_reply, .ct = "binary/octet-stream"},
203 {.s = "txt", .sz = 3, .f = custom_type_reply, .ct = "text/plain"},
204 {.s = "html", .sz = 4, .f = custom_type_reply, .ct = "text/html"},
205 {.s = "png", .sz = 3, .f = custom_type_reply, .ct = "image/png"},
206 };
e2f2b36 @nicolasff Added RAW output.
authored
207
6e3c424 @nicolasff Cleanup.
authored
208 /* default */
1ea7cd0 @nicolasff Special GET formatter for key + content-type key.
authored
209 *f_format = json_reply;
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
210
102f9fc @nicolasff Started adding Accept support.
authored
211 /* if there is an Accept header, use it */
212 if((accept_ct = evhttp_find_header(cmd->rq->input_headers, "Accept"))) {
213 cmd->mime = strdup(accept_ct);
214 cmd->mime_free = 1;
215 // printf("Accept: [%s]\n", cmd->mime);
216 }
217
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
218 /* find extension */
219 for(ext = uri + uri_len - 1; ext != uri && *ext != '/'; --ext) {
220 if(*ext == '.') {
221 ext++;
222 ext_len = uri + uri_len - ext;
e2f2b36 @nicolasff Added RAW output.
authored
223 break;
224 }
225 }
6e3c424 @nicolasff Cleanup.
authored
226 if(!ext_len) return uri_len; /* nothing found */
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
227
228 /* find function for the given extension */
229 for(i = 0; i < sizeof(funs)/sizeof(funs[0]); ++i) {
230 if(ext_len == (int)funs[i].sz && strncmp(ext, funs[i].s, ext_len) == 0) {
102f9fc @nicolasff Started adding Accept support.
authored
231
232 if(cmd->mime_free) free(cmd->mime);
233 cmd->mime = (char*)funs[i].ct;
234 cmd->mime_free = 0;
235
469e516 @nicolasff Removed content-type in another key, added suffixes instead.
authored
236 *f_format = funs[i].f;
237 }
238 }
239 return uri_len - ext_len - 1;
e2f2b36 @nicolasff Added RAW output.
authored
240 }
4676cfe @nicolasff Refactoring.
authored
241
242 int
243 cmd_is_subscribe(struct cmd *cmd) {
244
245 if(strncasecmp(cmd->argv[0], "SUBSCRIBE", cmd->argv_len[0]) == 0 ||
246 strncasecmp(cmd->argv[0], "PSUBSCRIBE", cmd->argv_len[0]) == 0) {
247 return 1;
248 }
249 return 0;
250 }
Something went wrong with that request. Please try again.