diff --git a/cmd.c b/cmd.c index 01cc479..f769dbf 100644 --- a/cmd.c +++ b/cmd.c @@ -33,9 +33,6 @@ cmd_free(struct cmd *c) { free(c->argv); free(c->argv_len); - free(c->mime); - free(c->mimeKey); - free(c); } @@ -100,7 +97,6 @@ cmd_run(struct server *s, struct evhttp_request *rq, struct cmd *cmd; formatting_fun f_format; - transform_fun f_transform = NULL; /* count arguments */ if(qmark) { @@ -112,6 +108,13 @@ cmd_run(struct server *s, struct evhttp_request *rq, cmd = cmd_new(rq, param_count); + /* parse URI parameters */ + evhttp_parse_query(uri, &cmd->uri_params); + + /* get output formatting function */ + uri_len = cmd_read_params(cmd, uri, uri_len, &f_format); + + /* check if we only have one command or more. */ slash = memchr(uri, '/', uri_len); if(slash) { cmd_len = slash - uri; @@ -119,12 +122,6 @@ cmd_run(struct server *s, struct evhttp_request *rq, cmd_len = uri_len; } - /* parse URI parameters */ - evhttp_parse_query(uri, &cmd->uri_params); - - /* get output formatting function */ - cmd_read_params(cmd, &f_format, &f_transform); - /* there is always a first parameter, it's the command name */ cmd->argv[0] = uri; cmd->argv_len[0] = cmd_len; @@ -170,9 +167,6 @@ cmd_run(struct server *s, struct evhttp_request *rq, cur_param++; } - /* transform command if we need to. */ - if(f_transform) f_transform(cmd); - /* push command to Redis. */ redisAsyncCommandArgv(s->ac, f_format, cmd, cmd->count, cmd->argv, cmd->argv_len); @@ -187,33 +181,42 @@ cmd_run(struct server *s, struct evhttp_request *rq, * Return 2 functions, one to format the reply and * one to transform the command before processing it. */ -void -cmd_read_params(struct cmd *cmd, formatting_fun *f_format, transform_fun *f_transform) { +int +cmd_read_params(struct cmd *cmd, const char *uri, size_t uri_len, formatting_fun *f_format) { + + const char *ext; + int ext_len = -1; + unsigned int i; - struct evkeyval *kv; + struct reply_format funs[] = { + {.s = "json", .sz = 4, .f = json_reply, .ct = "application/json"}, + {.s = "raw", .sz = 3, .f = raw_reply, .ct = "binary/octet-stream"}, + {.s = "txt", .sz = 3, .f = custom_type_reply, .ct = "text/plain"}, + {.s = "html", .sz = 4, .f = custom_type_reply, .ct = "text/html"}, + {.s = "png", .sz = 3, .f = custom_type_reply, .ct = "image/png"}, + }; /* defaults */ *f_format = json_reply; - *f_transform = NULL; - - /* loop over the query string */ - TAILQ_FOREACH(kv, &cmd->uri_params, next) { - if(strcmp(kv->key, "format") == 0) { /* output format */ - if(strcmp(kv->value, "raw") == 0) { - *f_format = raw_reply; - } else if(strcmp(kv->value, "json") == 0) { - *f_format = json_reply; - } + + /* find extension */ + for(ext = uri + uri_len - 1; ext != uri && *ext != '/'; --ext) { + if(*ext == '.') { + ext++; + ext_len = uri + uri_len - ext; break; - } else if(strcmp(kv->key, "typeKey") == 0) { /* MIME type in a key. */ - cmd->mimeKey = strdup(kv->value); - *f_transform = custom_type_process_cmd; - *f_format = custom_type_reply; - } else if(strcmp(kv->key, "type") == 0) { /* MIME type directly in parameter */ - cmd->mime = strdup(kv->value); - *f_format = custom_type_reply; } } + if(!ext_len) return uri_len; + + /* find function for the given extension */ + for(i = 0; i < sizeof(funs)/sizeof(funs[0]); ++i) { + if(ext_len == (int)funs[i].sz && strncmp(ext, funs[i].s, ext_len) == 0) { + *f_format = funs[i].f; + cmd->mime = funs[i].ct; + } + } + return uri_len - ext_len - 1; } int diff --git a/cmd.h b/cmd.h index 3d8aab7..cd94b2a 100644 --- a/cmd.h +++ b/cmd.h @@ -12,7 +12,6 @@ struct server; struct cmd; typedef void (*formatting_fun)(redisAsyncContext *, void *, void *); -typedef void (*transform_fun)(struct cmd *); struct cmd { @@ -26,8 +25,7 @@ struct cmd { int started_responding; /* HTTP data */ - char *mime; - char *mimeKey; + const char *mime; char *if_none_match; }; @@ -37,6 +35,13 @@ struct pubsub_client { struct evhttp_request *rq; }; +struct reply_format { + const char *s; + size_t sz; + formatting_fun f; + const char *ct; +}; + struct cmd * cmd_new(struct evhttp_request *rq, int count); @@ -47,8 +52,8 @@ int cmd_run(struct server *s, struct evhttp_request *rq, const char *uri, size_t uri_len); -void -cmd_read_params(struct cmd *cmd, formatting_fun *f_format, transform_fun *f_transform); +int +cmd_read_params(struct cmd *cmd, const char *uri, size_t uri_len, formatting_fun *f_format); int cmd_is_subscribe(struct cmd *cmd); diff --git a/formats/custom-type.c b/formats/custom-type.c index 39baff9..c378440 100644 --- a/formats/custom-type.c +++ b/formats/custom-type.c @@ -28,16 +28,24 @@ custom_type_reply(redisAsyncContext *c, void *r, void *privdata) { } if(cmd->mime) { /* use the given content-type */ - if(reply->type != REDIS_REPLY_STRING) { - custom_400(cmd); - return; + switch(reply->type) { + + case REDIS_REPLY_NIL: + format_send_reply(cmd, "", 0, cmd->mime); + return; + + case REDIS_REPLY_STRING: + format_send_reply(cmd, reply->str, reply->len, cmd->mime); + return; + + default: + custom_400(cmd); + return; } - format_send_reply(cmd, reply->str, reply->len, cmd->mime); - return; } /* we expect array(string, string) */ - if(!cmd->mimeKey || reply->type != REDIS_REPLY_ARRAY || reply->elements != 2 || reply->element[0]->type != REDIS_REPLY_STRING) { + if(reply->type != REDIS_REPLY_ARRAY || reply->elements != 2 || reply->element[0]->type != REDIS_REPLY_STRING) { custom_400(cmd); return; } @@ -54,23 +62,3 @@ custom_type_reply(redisAsyncContext *c, void *r, void *privdata) { return; } -/* This will change a GET command into MGET if a key is provided to get the response MIME-type from. */ -void -custom_type_process_cmd(struct cmd *cmd) { - /* MGET if mode is “custom” */ - if(cmd->count == 2 && cmd->argv_len[0] == 3 && - strncasecmp(cmd->argv[0], "GET", 3) == 0 && cmd->mimeKey) { - - cmd->count++; /* space for content-type key */ - cmd->argv = realloc(cmd->argv, cmd->count * sizeof(char*)); - cmd->argv_len = realloc(cmd->argv_len, cmd->count * sizeof(size_t)); - - /* replace command with MGET */ - cmd->argv[0] = "MGET"; - cmd->argv_len[0] = 4; - - /* add mime key after the key. */ - cmd->argv[2] = strdup(cmd->mimeKey); - cmd->argv_len[2] = strlen(cmd->mimeKey); - } -} diff --git a/formats/custom-type.h b/formats/custom-type.h index 959e0be..cb5b7be 100644 --- a/formats/custom-type.h +++ b/formats/custom-type.h @@ -9,7 +9,4 @@ struct cmd; void custom_type_reply(redisAsyncContext *c, void *r, void *privdata); -void -custom_type_process_cmd(struct cmd *cmd); - #endif diff --git a/formats/raw.c b/formats/raw.c index 33ce8a6..b6fdad7 100644 --- a/formats/raw.c +++ b/formats/raw.c @@ -28,7 +28,7 @@ raw_reply(redisAsyncContext *c, void *r, void *privdata) { raw_out = raw_wrap(r, &sz); /* send reply */ - format_send_reply(cmd, raw_out, sz, "binary/octet-stream"); + format_send_reply(cmd, raw_out, sz, cmd->mime?cmd->mime:"binary/octet-stream"); /* cleanup */ free(raw_out);