PageRenderTime 51ms CodeModel.GetById 11ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 0ms

/client.c

http://github.com/nicolasff/webdis
C | 372 lines | 347 code | 20 blank | 5 comment | 3 complexity | 8fc02eb21d31b8bc0cc1a06bf86ab230 MD5 | raw file
  1#include "client.h"
  2#include "http_parser.h"
  3#include "http.h"
  4#include "server.h"
  5#include "worker.h"
  6#include "websocket.h"
  7#include "cmd.h"
  8#include "conf.h"
  9
 10#include <stdlib.h>
 11#include <string.h>
 12#include <unistd.h>
 13#include <errno.h>
 14#include <hiredis/hiredis.h>
 15#include <hiredis/async.h>
 16
 17#define CHECK_ALLOC(c, ptr) if(!(ptr)) { c->failed_alloc = 1; return -1;}
 18
 19static int
 20http_client_on_url(struct http_parser *p, const char *at, size_t sz) {
 21
 22	struct http_client *c = p->data;
 23
 24	CHECK_ALLOC(c, c->path = realloc(c->path, c->path_sz + sz + 1));
 25	memcpy(c->path + c->path_sz, at, sz);
 26	c->path_sz += sz;
 27	c->path[c->path_sz] = 0;
 28
 29	return 0;
 30}
 31
 32/*
 33 * Called when the body is parsed.
 34 */
 35static int
 36http_client_on_body(struct http_parser *p, const char *at, size_t sz) {
 37
 38	struct http_client *c = p->data;
 39	return http_client_add_to_body(c, at, sz);
 40}
 41
 42int
 43http_client_add_to_body(struct http_client *c, const char *at, size_t sz) {
 44
 45	CHECK_ALLOC(c, c->body = realloc(c->body, c->body_sz + sz + 1));
 46	memcpy(c->body + c->body_sz, at, sz);
 47	c->body_sz += sz;
 48	c->body[c->body_sz] = 0;
 49
 50	return 0;
 51}
 52
 53static int
 54http_client_on_header_name(struct http_parser *p, const char *at, size_t sz) {
 55
 56	struct http_client *c = p->data;
 57	size_t n = c->header_count;
 58
 59	/* if we're not adding to the same header name as last time, realloc to add one field. */
 60	if(c->last_cb != LAST_CB_KEY) {
 61		n = ++c->header_count;
 62		CHECK_ALLOC(c, c->headers = realloc(c->headers, n * sizeof(struct http_header)));
 63		memset(&c->headers[n-1], 0, sizeof(struct http_header));
 64	}
 65
 66	/* Add data to the current header name. */
 67	CHECK_ALLOC(c, c->headers[n-1].key = realloc(c->headers[n-1].key,
 68			c->headers[n-1].key_sz + sz + 1));
 69	memcpy(c->headers[n-1].key + c->headers[n-1].key_sz, at, sz);
 70	c->headers[n-1].key_sz += sz;
 71	c->headers[n-1].key[c->headers[n-1].key_sz] = 0;
 72
 73	c->last_cb = LAST_CB_KEY;
 74
 75	return 0;
 76}
 77
 78static char *
 79wrap_filename(const char *val, size_t val_len) {
 80
 81	char format[] = "attachment; filename=\"";
 82	size_t sz = sizeof(format) - 1 + val_len + 1;
 83	char *p = calloc(sz + 1, 1);
 84
 85	memcpy(p, format, sizeof(format)-1); /* copy format */
 86	memcpy(p + sizeof(format)-1, val, val_len); /* copy filename */
 87	p[sz-1] = '"';
 88
 89	return p;
 90}
 91
 92/*
 93 * Split query string into key/value pairs, process some of them.
 94 */
 95static int
 96http_client_on_query_string(struct http_parser *parser, const char *at, size_t sz) {
 97
 98	struct http_client *c = parser->data;
 99	const char *p = at;
100
101	while(p < at + sz) {
102
103		const char *key = p, *val;
104		int key_len, val_len;
105		char *eq = memchr(key, '=', sz - (p-at));
106		if(!eq || eq > at + sz) { /* last argument */
107			break;
108		} else { /* found an '=' */
109			char *amp;
110			val = eq + 1;
111			key_len = eq - key;
112			p = eq + 1;
113
114			amp = memchr(p, '&', sz - (p-at));
115			if(!amp || amp > at + sz) {
116				val_len = at + sz - p; /* last arg */
117			} else {
118				val_len = amp - val; /* cur arg */
119				p = amp + 1;
120			}
121
122			if(key_len == 4 && strncmp(key, "type", 4) == 0) {
123				c->type = calloc(1 + val_len, 1);
124				memcpy(c->type, val, val_len);
125			} else if((key_len == 5 && strncmp(key, "jsonp", 5) == 0)
126				|| (key_len == 8 && strncmp(key, "callback", 8) == 0)) {
127				c->jsonp = calloc(1 + val_len, 1);
128				memcpy(c->jsonp, val, val_len);
129			} else if(key_len == 3 && strncmp(key, "sep", 3) == 0) {
130				c->separator = calloc(1 + val_len, 1);
131				memcpy(c->separator, val, val_len);
132			} else if(key_len == 8 && strncmp(key, "filename", 8) == 0) {
133				c->filename = wrap_filename(val, val_len);
134			}
135
136			if(!amp) {
137				break;
138			}
139		}
140	}
141	return 0;
142}
143
144static int
145http_client_on_header_value(struct http_parser *p, const char *at, size_t sz) {
146
147	struct http_client *c = p->data;
148	size_t n = c->header_count;
149
150	/* Add data to the current header value. */
151	CHECK_ALLOC(c, c->headers[n-1].val = realloc(c->headers[n-1].val,
152			c->headers[n-1].val_sz + sz + 1));
153	memcpy(c->headers[n-1].val + c->headers[n-1].val_sz, at, sz);
154	c->headers[n-1].val_sz += sz;
155	c->headers[n-1].val[c->headers[n-1].val_sz] = 0;
156
157	c->last_cb = LAST_CB_VAL;
158
159
160	/* react to some values. */
161	if(strncmp("Expect", c->headers[n-1].key, c->headers[n-1].key_sz) == 0) {
162		if(sz == 12 && strncasecmp(at, "100-continue", sz) == 0) {
163			/* support HTTP file upload */
164			char http100[] = "HTTP/1.1 100 Continue\r\n\r\n";
165			int ret = write(c->fd, http100, sizeof(http100)-1);
166			(void)ret;
167		}
168	} else if(strncasecmp("Connection", c->headers[n-1].key, c->headers[n-1].key_sz) == 0) {
169		if(sz == 10 && strncasecmp(at, "Keep-Alive", sz) == 0) {
170			c->keep_alive = 1;
171		}
172	}
173
174	return 0;
175}
176
177static int
178http_client_on_message_complete(struct http_parser *p) {
179
180	struct http_client *c = p->data;
181
182	/* keep-alive detection */
183	if (c->parser.flags & F_CONNECTION_CLOSE) {
184		c->keep_alive = 0;
185	} else if(c->parser.http_major == 1 && c->parser.http_minor == 1) { /* 1.1 */
186		c->keep_alive = 1;
187	}
188	c->http_version = c->parser.http_minor;
189
190	if(p->upgrade && c->w->s->cfg->websockets) { /* WebSocket, don't execute just yet */
191		c->is_websocket = 1;
192		return 0;
193	}
194
195	/* handle default root object */
196	if(c->path_sz == 1 && *c->path == '/' && c->w->s->cfg->default_root) { /* replace */
197		free(c->path);
198		c->path = strdup(c->w->s->cfg->default_root);
199		c->path_sz = strlen(c->path);
200	}
201
202
203	worker_process_client(c);
204	http_client_reset(c);
205
206	return 0;
207}
208
209struct http_client *
210http_client_new(struct worker *w, int fd, in_addr_t addr) {
211
212	struct http_client *c = calloc(1, sizeof(struct http_client));
213
214	c->fd = fd;
215	c->w = w;
216	c->addr = addr;
217	c->s = w->s;
218
219	/* parser */
220	http_parser_init(&c->parser, HTTP_REQUEST);
221	c->parser.data = c;
222
223	/* callbacks */
224	c->settings.on_url = http_client_on_url;
225	c->settings.on_query_string = http_client_on_query_string;
226	c->settings.on_body = http_client_on_body;
227	c->settings.on_message_complete = http_client_on_message_complete;
228	c->settings.on_header_field = http_client_on_header_name;
229	c->settings.on_header_value = http_client_on_header_value;
230
231	c->last_cb = LAST_CB_NONE;
232
233	return c;
234}
235
236
237void
238http_client_reset(struct http_client *c) {
239
240	int i;
241
242	/* headers */
243	for(i = 0; i < c->header_count; ++i) {
244		free(c->headers[i].key);
245		free(c->headers[i].val);
246	}
247	free(c->headers);
248	c->headers = NULL;
249	c->header_count = 0;
250
251	/* other data */
252	free(c->body); c->body = NULL;
253	c->body_sz = 0;
254	free(c->path); c->path = NULL;
255	c->path_sz = 0;
256	free(c->type); c->type = NULL;
257	free(c->jsonp); c->jsonp = NULL;
258	free(c->filename); c->filename = NULL;
259	c->request_sz = 0;
260
261	/* no last known header callback */
262	c->last_cb = LAST_CB_NONE;
263
264	/* mark as broken if client doesn't support Keep-Alive. */
265	if(c->keep_alive == 0) {
266		c->broken = 1;
267	}
268}
269
270void
271http_client_free(struct http_client *c) {
272
273	http_client_reset(c);
274	free(c->buffer);
275	free(c);
276}
277
278int
279http_client_read(struct http_client *c) {
280
281	char buffer[4096];
282	int ret;
283
284	ret = read(c->fd, buffer, sizeof(buffer));
285	if(ret <= 0) {
286		/* broken link, free buffer and client object */
287
288		/* disconnect pub/sub client if there is one. */
289		if(c->pub_sub && c->pub_sub->ac) {
290			struct cmd *cmd = c->pub_sub;
291
292			/* disconnect from all channels */
293			redisAsyncDisconnect(c->pub_sub->ac);
294			if(c->pub_sub) c->pub_sub->ac = NULL;
295			c->pub_sub = NULL;
296
297			/* delete command object */
298			cmd_free(cmd);
299		}
300
301		close(c->fd);
302
303		http_client_free(c);
304		return (int)CLIENT_DISCONNECTED;
305	}
306
307	/* save what we've just read */
308	c->buffer = realloc(c->buffer, c->sz + ret);
309	if(!c->buffer) {
310		return (int)CLIENT_OOM;
311	}
312	memcpy(c->buffer + c->sz, buffer, ret);
313	c->sz += ret;
314
315	/* keep track of total sent */
316	c->request_sz += ret;
317
318	return ret;
319}
320
321int
322http_client_remove_data(struct http_client *c, size_t sz) {
323
324	char *buffer;
325	if(c->sz < sz)
326		return -1;
327
328	/* replace buffer */
329	CHECK_ALLOC(c, buffer = malloc(c->sz - sz));
330	memcpy(buffer, c->buffer + sz, c->sz - sz);
331	free(c->buffer);
332	c->buffer = buffer;
333	c->sz -= sz;
334
335	return 0;
336}
337
338int
339http_client_execute(struct http_client *c) {
340
341	int nparsed = http_parser_execute(&c->parser, &c->settings, c->buffer, c->sz);
342
343	if(!c->is_websocket) {
344		/* removed consumed data, all has been copied already. */
345		free(c->buffer);
346		c->buffer = NULL;
347		c->sz = 0;
348	}
349	return nparsed;
350}
351
352/*
353 * Find header value, returns NULL if not found.
354 */
355const char *
356client_get_header(struct http_client *c, const char *key) {
357
358	int i;
359	size_t sz = strlen(key);
360
361	for(i = 0; i < c->header_count; ++i) {
362
363		if(sz == c->headers[i].key_sz &&
364			strncasecmp(key, c->headers[i].key, sz) == 0) {
365			return c->headers[i].val;
366		}
367
368	}
369
370	return NULL;
371}
372