#include "raw.h" #include "common.h" #include "http.h" #include "client.h" #include "cmd.h" #include #include #include static char * raw_wrap(const redisReply *r, size_t *sz); void raw_reply(redisAsyncContext *c, void *r, void *privdata) { redisReply *reply = r; struct cmd *cmd = privdata; char *raw_out; size_t sz; (void)c; if (reply == NULL) { /* broken Redis link */ format_send_error(cmd, 503, "Service Unavailable"); return; } raw_out = raw_wrap(r, &sz); /* send reply */ format_send_reply(cmd, raw_out, sz, "binary/octet-stream"); /* cleanup */ free(raw_out); } static int integer_length(long long int i) { int sz = 0; int ci = abs(i); while (ci > 0) { ci = (ci/10); sz += 1; } if(i == 0) { /* log 0 doesn't make sense. */ sz = 1; } else if(i < 0) { /* allow for neg sign as well. */ sz++; } return sz; } /* extract Redis protocol string from WebSocket frame and fill struct cmd. */ struct cmd * raw_ws_extract(struct http_client *c, const char *p, size_t sz) { struct cmd *cmd = NULL; void *reader = NULL; redisReply *reply = NULL; unsigned int i; (void)c; /* create protocol reader */ reader = redisReplyReaderCreate(); /* add data */ redisReplyReaderFeed(reader, (char*)p, sz); /* parse data into reply object */ if(redisReplyReaderGetReply(reader, (void**)&reply) == REDIS_ERR) { goto end; } /* add data from reply object to cmd struct */ if(reply->type != REDIS_REPLY_ARRAY) { goto end; } /* create cmd object */ cmd = cmd_new(reply->elements); for(i = 0; i < reply->elements; ++i) { redisReply *ri = reply->element[i]; switch(ri->type) { case REDIS_REPLY_STRING: cmd->argv_len[i] = ri->len; cmd->argv[i] = calloc(cmd->argv_len[i] + 1, 1); memcpy(cmd->argv[i], ri->str, ri->len); break; case REDIS_REPLY_INTEGER: cmd->argv_len[i] = integer_length(ri->integer); cmd->argv[i] = calloc(cmd->argv_len[i] + 1, 1); sprintf(cmd->argv[i], "%lld", ri->integer); break; default: cmd_free(cmd); cmd = NULL; goto end; } } end: /* free reader */ if(reader) redisReplyReaderFree(reader); /* free reply */ if(reply) freeReplyObject(reply); return cmd; } static char * raw_array(const redisReply *r, size_t *sz) { unsigned int i; char *ret, *p; /* compute size */ *sz = 0; *sz += 1 + integer_length(r->elements) + 2; for(i = 0; i < r->elements; ++i) { redisReply *e = r->element[i]; switch(e->type) { case REDIS_REPLY_STRING: *sz += 1 + integer_length(e->len) + 2 + e->len + 2; break; case REDIS_REPLY_INTEGER: *sz += 1 + integer_length(integer_length(e->integer)) + 2 + integer_length(e->integer) + 2; break; } } /* allocate */ p = ret = malloc(1+*sz); p += sprintf(p, "*%zd\r\n", r->elements); /* copy */ for(i = 0; i < r->elements; ++i) { redisReply *e = r->element[i]; switch(e->type) { case REDIS_REPLY_STRING: p += sprintf(p, "$%d\r\n", e->len); memcpy(p, e->str, e->len); p += e->len; *p = '\r'; p++; *p = '\n'; p++; break; case REDIS_REPLY_INTEGER: p += sprintf(p, "$%d\r\n%lld\r\n", integer_length(e->integer), e->integer); break; } } return ret; } static char * raw_wrap(const redisReply *r, size_t *sz) { char *ret, *p; switch(r->type) { case REDIS_REPLY_STATUS: case REDIS_REPLY_ERROR: *sz = 3 + r->len; ret = malloc(*sz); ret[0] = (r->type == REDIS_REPLY_STATUS?'+':'-'); memcpy(ret+1, r->str, *sz-3); memcpy(ret+*sz - 2, "\r\n", 2); return ret; case REDIS_REPLY_STRING: *sz = 1 + integer_length(r->len) + 2 + r->len + 2; p = ret = malloc(*sz); p += sprintf(p, "$%d\r\n", r->len); memcpy(p, r->str, *sz - 2 - (p-ret)); memcpy(ret + *sz - 2, "\r\n", 2); return ret; case REDIS_REPLY_INTEGER: *sz = 3 + integer_length(r->integer); ret = malloc(4+*sz); sprintf(ret, ":%lld\r\n", r->integer); return ret; case REDIS_REPLY_ARRAY: return raw_array(r, sz); default: *sz = 5; ret = malloc(*sz); memcpy(ret, "$-1\r\n", 5); return ret; } }