/formats/json.c
C | 293 lines | 216 code | 58 blank | 19 comment | 32 complexity | 37c396adc72bf8715c8912ad45b9a9d3 MD5 | raw file
1#include "json.h" 2#include "common.h" 3#include "cmd.h" 4#include "http.h" 5#include "client.h" 6 7#include <string.h> 8#include <hiredis/hiredis.h> 9#include <hiredis/async.h> 10 11static json_t * 12json_wrap_redis_reply(const struct cmd *cmd, const redisReply *r); 13 14void 15json_reply(redisAsyncContext *c, void *r, void *privdata) { 16 17 redisReply *reply = r; 18 struct cmd *cmd = privdata; 19 json_t *j; 20 char *jstr; 21 (void)c; 22 23 if(cmd == NULL) { 24 /* broken connection */ 25 return; 26 } 27 28 if (reply == NULL) { /* broken Redis link */ 29 format_send_error(cmd, 503, "Service Unavailable"); 30 return; 31 } 32 33 /* encode redis reply as JSON */ 34 j = json_wrap_redis_reply(cmd, r); 35 36 /* get JSON as string, possibly with JSONP wrapper */ 37 jstr = json_string_output(j, cmd->jsonp); 38 39 /* send reply */ 40 format_send_reply(cmd, jstr, strlen(jstr), "application/json"); 41 42 /* cleanup */ 43 json_decref(j); 44 free(jstr); 45} 46 47/** 48 * Parse info message and return object. 49 */ 50static json_t * 51json_info_reply(const char *s) { 52 const char *p = s; 53 size_t sz = strlen(s); 54 55 json_t *jroot = json_object(); 56 57 /* TODO: handle new format */ 58 59 while(p < s + sz) { 60 char *key, *val, *nl, *colon; 61 62 /* find key */ 63 colon = strchr(p, ':'); 64 if(!colon) { 65 break; 66 } 67 key = calloc(colon - p + 1, 1); 68 memcpy(key, p, colon - p); 69 p = colon + 1; 70 71 /* find value */ 72 nl = strchr(p, '\r'); 73 if(!nl) { 74 free(key); 75 break; 76 } 77 val = calloc(nl - p + 1, 1); 78 memcpy(val, p, nl - p); 79 p = nl + 1; 80 if(*p == '\n') p++; 81 82 /* add to object */ 83 json_object_set_new(jroot, key, json_string(val)); 84 free(key); 85 free(val); 86 } 87 88 return jroot; 89} 90 91static json_t * 92json_hgetall_reply(const redisReply *r) { 93 /* zip keys and values together in a json object */ 94 json_t *jroot; 95 unsigned int i; 96 97 if(r->elements % 2 != 0) { 98 return NULL; 99 } 100 101 jroot = json_object(); 102 for(i = 0; i < r->elements; i += 2) { 103 redisReply *k = r->element[i], *v = r->element[i+1]; 104 105 /* keys and values need to be strings */ 106 if(k->type != REDIS_REPLY_STRING || v->type != REDIS_REPLY_STRING) { 107 json_decref(jroot); 108 return NULL; 109 } 110 json_object_set_new(jroot, k->str, json_string(v->str)); 111 } 112 return jroot; 113} 114 115static json_t * 116json_wrap_redis_reply(const struct cmd *cmd, const redisReply *r) { 117 118 unsigned int i; 119 json_t *jlist, *jroot = json_object(); /* that's what we return */ 120 121 122 /* copy verb, as jansson only takes a char* but not its length. */ 123 char *verb; 124 if(cmd->count) { 125 verb = calloc(cmd->argv_len[0]+1, 1); 126 memcpy(verb, cmd->argv[0], cmd->argv_len[0]); 127 } else { 128 verb = strdup(""); 129 } 130 131 132 switch(r->type) { 133 case REDIS_REPLY_STATUS: 134 case REDIS_REPLY_ERROR: 135 jlist = json_array(); 136 json_array_append_new(jlist, 137 r->type == REDIS_REPLY_ERROR ? json_false() : json_true()); 138 json_array_append_new(jlist, json_string(r->str)); 139 json_object_set_new(jroot, verb, jlist); 140 break; 141 142 case REDIS_REPLY_STRING: 143 if(strcasecmp(verb, "INFO") == 0) { 144 json_object_set_new(jroot, verb, json_info_reply(r->str)); 145 } else { 146 json_object_set_new(jroot, verb, json_string(r->str)); 147 } 148 break; 149 150 case REDIS_REPLY_INTEGER: 151 json_object_set_new(jroot, verb, json_integer(r->integer)); 152 break; 153 154 case REDIS_REPLY_ARRAY: 155 if(strcasecmp(verb, "HGETALL") == 0) { 156 json_t *jobj = json_hgetall_reply(r); 157 if(jobj) { 158 json_object_set_new(jroot, verb, jobj); 159 break; 160 } 161 } 162 jlist = json_array(); 163 for(i = 0; i < r->elements; ++i) { 164 redisReply *e = r->element[i]; 165 switch(e->type) { 166 case REDIS_REPLY_STRING: 167 json_array_append_new(jlist, json_string(e->str)); 168 break; 169 case REDIS_REPLY_INTEGER: 170 json_array_append_new(jlist, json_integer(e->integer)); 171 break; 172 default: 173 json_array_append_new(jlist, json_null()); 174 break; 175 } 176 } 177 json_object_set_new(jroot, verb, jlist); 178 break; 179 180 default: 181 json_object_set_new(jroot, verb, json_null()); 182 break; 183 } 184 185 free(verb); 186 return jroot; 187} 188 189 190char * 191json_string_output(json_t *j, const char *jsonp) { 192 193 char *json_reply = json_dumps(j, JSON_COMPACT); 194 195 /* check for JSONP */ 196 if(jsonp) { 197 size_t jsonp_len = strlen(jsonp); 198 size_t json_len = strlen(json_reply); 199 size_t ret_len = jsonp_len + 1 + json_len + 3; 200 char *ret = calloc(1 + ret_len, 1); 201 202 memcpy(ret, jsonp, jsonp_len); 203 ret[jsonp_len]='('; 204 memcpy(ret + jsonp_len + 1, json_reply, json_len); 205 memcpy(ret + jsonp_len + 1 + json_len, ");\n", 3); 206 free(json_reply); 207 208 return ret; 209 } 210 211 return json_reply; 212} 213 214/* extract JSON from WebSocket frame and fill struct cmd. */ 215struct cmd * 216json_ws_extract(struct http_client *c, const char *p, size_t sz) { 217 218 struct cmd *cmd = NULL; 219 json_t *j; 220 char *jsonz; /* null-terminated */ 221 222 unsigned int i, cur; 223 int argc = 0; 224 json_error_t jerror; 225 226 (void)c; 227 228 jsonz = calloc(sz + 1, 1); 229 memcpy(jsonz, p, sz); 230 j = json_loads(jsonz, sz, &jerror); 231 free(jsonz); 232 233 if(!j) { 234 return NULL; 235 } 236 if(json_typeof(j) != JSON_ARRAY) { 237 json_decref(j); 238 return NULL; /* invalid JSON */ 239 } 240 241 /* count elements */ 242 for(i = 0; i < json_array_size(j); ++i) { 243 json_t *jelem = json_array_get(j, i); 244 245 switch(json_typeof(jelem)) { 246 case JSON_STRING: 247 case JSON_INTEGER: 248 argc++; 249 break; 250 251 default: 252 break; 253 } 254 } 255 256 if(!argc) { /* not a single item could be decoded */ 257 json_decref(j); 258 return NULL; 259 } 260 261 /* create command and add args */ 262 cmd = cmd_new(argc); 263 for(i = 0, cur = 0; i < json_array_size(j); ++i) { 264 json_t *jelem = json_array_get(j, i); 265 char *tmp; 266 267 switch(json_typeof(jelem)) { 268 case JSON_STRING: 269 tmp = strdup(json_string_value(jelem)); 270 271 cmd->argv[cur] = tmp; 272 cmd->argv_len[cur] = strlen(tmp); 273 cur++; 274 break; 275 276 case JSON_INTEGER: 277 tmp = malloc(40); 278 sprintf(tmp, "%d", (int)json_integer_value(jelem)); 279 280 cmd->argv[cur] = tmp; 281 cmd->argv_len[cur] = strlen(tmp); 282 cur++; 283 break; 284 285 default: 286 break; 287 } 288 } 289 290 json_decref(j); 291 return cmd; 292} 293