PageRenderTime 355ms CodeModel.GetById 161ms app.highlight 16ms RepoModel.GetById 175ms app.codeStats 1ms

/worker.c

http://github.com/nicolasff/webdis
C | 237 lines | 164 code | 46 blank | 27 comment | 30 complexity | ef10ba6f022dddf4543f15f36b59ec48 MD5 | raw file
  1#include "worker.h"
  2#include "client.h"
  3#include "http.h"
  4#include "cmd.h"
  5#include "pool.h"
  6#include "slog.h"
  7#include "websocket.h"
  8#include "conf.h"
  9#include "server.h"
 10
 11#include <stdlib.h>
 12#include <stdio.h>
 13#include <unistd.h>
 14#include <event.h>
 15#include <string.h>
 16
 17
 18struct worker *
 19worker_new(struct server *s) {
 20
 21	int ret;
 22	struct worker *w = calloc(1, sizeof(struct worker));
 23	w->s = s;
 24
 25	/* setup communication link */
 26	ret = pipe(w->link);
 27	(void)ret;
 28
 29	/* Redis connection pool */
 30	w->pool = pool_new(w, s->cfg->pool_size_per_thread);
 31
 32	return w;
 33
 34}
 35
 36void
 37worker_can_read(int fd, short event, void *p) {
 38
 39	struct http_client *c = p;
 40	int ret, nparsed;
 41
 42	(void)fd;
 43	(void)event;
 44
 45	ret = http_client_read(c);
 46	if(ret <= 0) {
 47		if((client_error_t)ret == CLIENT_DISCONNECTED) {
 48			return;
 49		} else if (c->failed_alloc || (client_error_t)ret == CLIENT_OOM) {
 50			slog(c->w->s, WEBDIS_DEBUG, "503", 3);
 51			http_send_error(c, 503, "Service Unavailable");
 52			return;
 53		}
 54	}
 55
 56	if(c->is_websocket) {
 57		/* Got websocket data */
 58		ws_add_data(c);
 59	} else {
 60		/* run parser */
 61		nparsed = http_client_execute(c);
 62
 63		if(c->failed_alloc) {
 64			slog(c->w->s, WEBDIS_DEBUG, "503", 3);
 65			http_send_error(c, 503, "Service Unavailable");
 66		} else if (c->parser.flags & F_CONNECTION_CLOSE) {
 67			c->broken = 1;
 68		} else if(c->is_websocket) {
 69			/* we need to use the remaining (unparsed) data as the body. */
 70			if(nparsed < ret) {
 71				http_client_add_to_body(c, c->buffer + nparsed + 1, c->sz - nparsed - 1);
 72				ws_handshake_reply(c);
 73			} else {
 74				c->broken = 1;
 75			}
 76			free(c->buffer);
 77			c->buffer = NULL;
 78			c->sz = 0;
 79		} else if(nparsed != ret) {
 80			slog(c->w->s, WEBDIS_DEBUG, "400", 3);
 81			http_send_error(c, 400, "Bad Request");
 82		} else if(c->request_sz > c->s->cfg->http_max_request_size) {
 83			slog(c->w->s, WEBDIS_DEBUG, "413", 3);
 84			http_send_error(c, 413, "Request Entity Too Large");
 85		}
 86	}
 87
 88	if(c->broken) { /* terminate client */
 89		http_client_free(c);
 90	} else {
 91		/* start monitoring input again */
 92		worker_monitor_input(c);
 93	}
 94}
 95
 96/**
 97 * Monitor client FD for possible reads.
 98 */
 99void
100worker_monitor_input(struct http_client *c) {
101
102	event_set(&c->ev, c->fd, EV_READ, worker_can_read, c);
103	event_base_set(c->w->base, &c->ev);
104	event_add(&c->ev, NULL);
105}
106
107/**
108 * Called when a client is sent to this worker.
109 */
110static void
111worker_on_new_client(int pipefd, short event, void *ptr) {
112
113	struct http_client *c;
114	unsigned long addr;
115
116	(void)event;
117	(void)ptr;
118
119	/* Get client from messaging pipe */
120	int ret = read(pipefd, &addr, sizeof(addr));
121	if(ret == sizeof(addr)) {
122		c = (struct http_client*)addr;
123
124		/* monitor client for input */
125		worker_monitor_input(c);
126	}
127}
128
129static void
130worker_pool_connect(struct worker *w) {
131
132	int i;
133	/* create connections */
134	for(i = 0; i < w->pool->count; ++i) {
135		pool_connect(w->pool, w->s->cfg->database, 1);
136	}
137
138}
139
140static void*
141worker_main(void *p) {
142
143	struct worker *w = p;
144	struct event ev;
145
146	/* setup libevent */
147	w->base = event_base_new();
148
149	/* monitor pipe link */
150	event_set(&ev, w->link[0], EV_READ | EV_PERSIST, worker_on_new_client, w);
151	event_base_set(w->base, &ev);
152	event_add(&ev, NULL);
153
154	/* connect to Redis */
155	worker_pool_connect(w);
156
157	/* loop */
158	event_base_dispatch(w->base);
159
160	return NULL;
161}
162
163void
164worker_start(struct worker *w) {
165
166	pthread_create(&w->thread, NULL, worker_main, w);
167}
168
169/**
170 * Queue new client to process
171 */
172void
173worker_add_client(struct worker *w, struct http_client *c) {
174
175	/* write into pipe link */
176	unsigned long addr = (unsigned long)c;
177	int ret = write(w->link[1], &addr, sizeof(addr));
178	(void)ret;
179}
180
181/**
182 * Called when a client has finished reading input and can create a cmd
183 */
184void
185worker_process_client(struct http_client *c) {
186
187	/* check that the command can be executed */
188	struct worker *w = c->w;
189	cmd_response_t ret = CMD_PARAM_ERROR;
190	switch(c->parser.method) {
191		case HTTP_GET:
192			if(c->path_sz == 16 && memcmp(c->path, "/crossdomain.xml", 16) == 0) {
193				http_crossdomain(c);
194				return;
195			}
196			slog(w->s, WEBDIS_DEBUG, c->path, c->path_sz);
197			ret = cmd_run(c->w, c, 1+c->path, c->path_sz-1, NULL, 0);
198			break;
199
200		case HTTP_POST:
201			slog(w->s, WEBDIS_DEBUG, c->path, c->path_sz);
202			ret = cmd_run(c->w, c, c->body, c->body_sz, NULL, 0);
203			break;
204
205		case HTTP_PUT:
206			slog(w->s, WEBDIS_DEBUG, c->path, c->path_sz);
207			ret = cmd_run(c->w, c, 1+c->path, c->path_sz-1,
208					c->body, c->body_sz);
209			break;
210
211		case HTTP_OPTIONS:
212			http_send_options(c);
213			return;
214
215		default:
216			slog(w->s, WEBDIS_DEBUG, "405", 3);
217			http_send_error(c, 405, "Method Not Allowed");
218			return;
219	}
220
221	switch(ret) {
222		case CMD_ACL_FAIL:
223		case CMD_PARAM_ERROR:
224			slog(w->s, WEBDIS_DEBUG, "403", 3);
225			http_send_error(c, 403, "Forbidden");
226			break;
227
228		case CMD_REDIS_UNAVAIL:
229			slog(w->s, WEBDIS_DEBUG, "503", 3);
230			http_send_error(c, 503, "Service Unavailable");
231			break;
232		default:
233			break;
234	}
235
236}
237