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