PageRenderTime 65ms CodeModel.GetById 50ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/formats/json.c

http://github.com/nicolasff/webdis
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