adamc@859
|
1 #define _GNU_SOURCE
|
adamc@859
|
2
|
adamc@859
|
3 #include <stdio.h>
|
adamc@859
|
4 #include <string.h>
|
adamc@859
|
5 #include <stdlib.h>
|
adamc@859
|
6 #include <sys/types.h>
|
adamc@859
|
7 #include <sys/socket.h>
|
adamc@859
|
8 #include <netdb.h>
|
adamc@859
|
9 #include <netinet/in.h>
|
adamc@859
|
10 #include <unistd.h>
|
adamc@859
|
11 #include <signal.h>
|
adamc@859
|
12 #include <stdarg.h>
|
adamc@859
|
13
|
adamc@859
|
14 #include <pthread.h>
|
adamc@859
|
15
|
adamc@859
|
16 #include "urweb.h"
|
adamc@859
|
17 #include "request.h"
|
adamc@859
|
18 #include "queue.h"
|
adamc@859
|
19
|
adamc@859
|
20 #include "fastcgi.h"
|
adamc@859
|
21
|
adamc@859
|
22 typedef struct {
|
adamc@859
|
23 unsigned char version;
|
adamc@859
|
24 unsigned char type;
|
adamc@859
|
25 unsigned char requestIdB1;
|
adamc@859
|
26 unsigned char requestIdB0;
|
adamc@859
|
27 unsigned char contentLengthB1;
|
adamc@859
|
28 unsigned char contentLengthB0;
|
adamc@859
|
29 unsigned char paddingLength;
|
adamc@859
|
30 unsigned char reserved;
|
adamc@859
|
31 unsigned char contentData[65535];
|
adamc@859
|
32 } FCGI_Record;
|
adamc@859
|
33
|
adamc@859
|
34 typedef struct {
|
adamc@859
|
35 FCGI_Record r;
|
adamc@859
|
36 int sock;
|
adamc@859
|
37 } FCGI_Output;
|
adamc@859
|
38
|
adamc@859
|
39 typedef struct {
|
adamc@859
|
40 char buf[sizeof(FCGI_Record) + 255];
|
adamc@859
|
41 int available, used, sock;
|
adamc@859
|
42 } FCGI_Input;
|
adamc@859
|
43
|
adamc@859
|
44 static FCGI_Output *fastcgi_output() {
|
adamc@859
|
45 FCGI_Output *o = malloc(sizeof(FCGI_Output));
|
adamc@859
|
46
|
adamc@859
|
47 o->r.version = FCGI_VERSION_1;
|
adamc@859
|
48 o->r.paddingLength = 0;
|
adamc@859
|
49 o->r.reserved = 0;
|
adamc@859
|
50
|
adamc@859
|
51 return o;
|
adamc@859
|
52 }
|
adamc@859
|
53
|
adamc@859
|
54 static FCGI_Input *fastcgi_input() {
|
adamc@859
|
55 FCGI_Input *i = malloc(sizeof(FCGI_Input));
|
adamc@859
|
56
|
adamc@859
|
57 i->available = i->used = 0;
|
adamc@859
|
58
|
adamc@859
|
59 return i;
|
adamc@859
|
60 }
|
adamc@859
|
61
|
adamc@859
|
62 static void fastcgi_input_reset(FCGI_Input *i) {
|
adamc@859
|
63 i->available = i->used = 0;
|
adamc@859
|
64 }
|
adamc@859
|
65
|
adamc@859
|
66 static int fastcgi_send(FCGI_Output *o,
|
adamc@859
|
67 unsigned char type,
|
adamc@859
|
68 unsigned short contentLength) {
|
adamc@859
|
69 o->r.type = type;
|
adamc@860
|
70 o->r.requestIdB1 = o->r.requestIdB0 = 0;
|
adamc@859
|
71 o->r.contentLengthB1 = contentLength >> 8;
|
adamc@859
|
72 o->r.contentLengthB0 = contentLength & 255;
|
adamc@861
|
73 return uw_really_send(o->sock, &o->r, sizeof(o->r) - 65535 + contentLength);
|
adamc@859
|
74 }
|
adamc@859
|
75
|
adamc@859
|
76 #define LATEST(i) ((FCGI_Record *)(i->buf + i->used))
|
adamc@859
|
77
|
adamc@859
|
78 static FCGI_Record *fastcgi_recv(FCGI_Input *i) {
|
adamc@859
|
79 while (1) {
|
adamc@859
|
80 ssize_t n;
|
adamc@859
|
81
|
adamc@859
|
82 if (i->available >= i->used + sizeof(FCGI_Record) - 65535
|
adamc@859
|
83 && i->available >= i->used + sizeof(FCGI_Record) - 65535
|
adamc@860
|
84 + ((LATEST(i)->contentLengthB1 << 8) | LATEST(i)->contentLengthB0)
|
adamc@859
|
85 + LATEST(i)->paddingLength) {
|
adamc@859
|
86 FCGI_Record *r = LATEST(i);
|
adamc@859
|
87
|
adamc@859
|
88 i->used += sizeof(FCGI_Record) - 65535
|
adamc@860
|
89 + ((LATEST(i)->contentLengthB1 << 8) | LATEST(i)->contentLengthB0)
|
adamc@859
|
90 + LATEST(i)->paddingLength;
|
adamc@859
|
91
|
adamc@859
|
92 return r;
|
adamc@859
|
93 }
|
adamc@859
|
94
|
adamc@859
|
95 if (i->used > 0) {
|
adamc@859
|
96 memmove(i->buf, i->buf + i->used, i->available - i->used);
|
adamc@859
|
97 i->available -= i->used;
|
adamc@859
|
98 i->used = 0;
|
adamc@859
|
99 }
|
adamc@859
|
100
|
adamc@859
|
101 n = recv(i->sock, i->buf + i->available, sizeof(i->buf) - i->available, 0);
|
adamc@859
|
102
|
adamc@859
|
103 if (n <= 0)
|
adamc@859
|
104 return NULL;
|
adamc@859
|
105
|
adamc@859
|
106 i->available += n;
|
adamc@859
|
107 }
|
adamc@859
|
108 }
|
adamc@859
|
109
|
adamc@859
|
110 static void on_success(uw_context ctx) { }
|
adamc@859
|
111
|
adamc@859
|
112 static void on_failure(uw_context ctx) {
|
adamc@859
|
113 uw_write_header(ctx, "Status: 500 Internal Server Error\r\n");
|
adamc@859
|
114 }
|
adamc@859
|
115
|
adamc@860
|
116 static int write_stdout(void *data, char *buf, size_t len) {
|
adamc@860
|
117 FCGI_Output *o = (FCGI_Output *)data;
|
adamc@860
|
118 while (len > 0) {
|
adamc@860
|
119 size_t len2 = len;
|
adamc@860
|
120 if (len2 > 65535)
|
adamc@860
|
121 len2 = 65535;
|
adamc@860
|
122 memcpy(o->r.contentData, buf, len2);
|
adamc@860
|
123 if (fastcgi_send(o, FCGI_STDOUT, len2)) {
|
adamc@860
|
124 fprintf(stderr, "fastcgi_send() failed in write_stdout().\n");
|
adamc@860
|
125 return -1;
|
adamc@860
|
126 }
|
adamc@860
|
127 buf += len2;
|
adamc@860
|
128 len -= len2;
|
adamc@860
|
129 }
|
adamc@860
|
130
|
adamc@860
|
131 return 0;
|
adamc@860
|
132 }
|
adamc@860
|
133
|
adamc@859
|
134 static void write_stderr(FCGI_Output *o, const char *fmt, ...) {
|
adamc@859
|
135 int len;
|
adamc@859
|
136 va_list ap;
|
adamc@859
|
137 va_start(ap, fmt);
|
adamc@859
|
138
|
adamc@859
|
139 len = vsnprintf(o->r.contentData, 65535, fmt, ap);
|
adamc@859
|
140 if (len < 0)
|
adamc@860
|
141 fprintf(stderr, "vsnprintf() failed in write_stderr().\n");
|
adamc@860
|
142 else if (fastcgi_send(o, FCGI_STDERR, len))
|
adamc@860
|
143 fprintf(stderr, "fastcgi_send() failed in write_stderr().\n");
|
adamc@860
|
144 }
|
adamc@860
|
145
|
adamc@860
|
146 static void close_stream(FCGI_Output *o, unsigned char type) {
|
adamc@860
|
147 if (fastcgi_send(o, type, 0))
|
adamc@860
|
148 fprintf(stderr, "fastcgi_send() failed in close_stream().\n");
|
adamc@859
|
149 }
|
adamc@859
|
150
|
adamc@859
|
151 static void log_error(void *data, const char *fmt, ...) {
|
adamc@859
|
152 FCGI_Output *o = (FCGI_Output *)data;
|
adamc@859
|
153 va_list ap;
|
adamc@859
|
154 va_start(ap, fmt);
|
adamc@859
|
155
|
adamc@859
|
156 if (o) {
|
adamc@859
|
157 int len = vsnprintf(o->r.contentData, 65535, fmt, ap);
|
adamc@859
|
158 if (len < 0)
|
adamc@859
|
159 fprintf(stderr, "vsnprintf() failed in log_error().\n");
|
adamc@860
|
160 else if (fastcgi_send(o, FCGI_STDERR, len))
|
adamc@859
|
161 fprintf(stderr, "fastcgi_send() failed in log_error().\n");
|
adamc@859
|
162 } else
|
adamc@859
|
163 vfprintf(stderr, fmt, ap);
|
adamc@859
|
164 }
|
adamc@859
|
165
|
adamc@859
|
166 static void log_debug(void *data, const char *fmt, ...) {
|
adamc@859
|
167 }
|
adamc@859
|
168
|
adamc@860
|
169 typedef struct {
|
adamc@860
|
170 char *name, *value;
|
adamc@860
|
171 unsigned name_len, value_len;
|
adamc@860
|
172 } nvp;
|
adamc@860
|
173
|
adamc@860
|
174 static char *search_nvps(nvp *nvps, const char *h) {
|
adamc@860
|
175 for (; nvps->name; ++nvps)
|
adamc@860
|
176 if (!strcmp(h, nvps->name))
|
adamc@860
|
177 return nvps->value;
|
adamc@860
|
178
|
adamc@860
|
179 return NULL;
|
adamc@860
|
180 }
|
adamc@860
|
181
|
adamc@860
|
182 typedef struct {
|
adamc@860
|
183 nvp *nvps;
|
adamc@860
|
184 char *uppercased;
|
adamc@860
|
185 int n_nvps, uppercased_len;
|
adamc@860
|
186 } headers;
|
adamc@860
|
187
|
adamc@860
|
188 static char *get_header(void *data, const char *h) {
|
adamc@860
|
189 headers *hs = (headers *)data;
|
adamc@860
|
190 size_t len = strlen(h);
|
adamc@860
|
191 char *s, *r;
|
adamc@860
|
192 const char *saved_h = h;
|
adamc@860
|
193
|
adamc@860
|
194 if (len > hs->uppercased_len) {
|
adamc@860
|
195 hs->uppercased_len = len;
|
adamc@860
|
196 hs->uppercased = realloc(hs->uppercased, len + 6);
|
adamc@860
|
197 }
|
adamc@860
|
198
|
adamc@860
|
199 strcpy(hs->uppercased, "HTTP_");
|
adamc@860
|
200 for (s = hs->uppercased+5; *h; ++h)
|
adamc@860
|
201 *s++ = *h == '-' ? '_' : toupper(*h);
|
adamc@860
|
202 *s = 0;
|
adamc@860
|
203
|
adamc@860
|
204 if (!strcasecmp(saved_h, "Content-length")
|
adamc@860
|
205 || !strcasecmp(saved_h, "Content-type"))
|
adamc@860
|
206 return search_nvps(hs->nvps, hs->uppercased + 5);
|
adamc@860
|
207 else
|
adamc@860
|
208 return search_nvps(hs->nvps, hs->uppercased);
|
adamc@860
|
209 }
|
adamc@860
|
210
|
adamc@861
|
211 static int read_funny_len(char **buf, int *len) {
|
adamc@861
|
212 if (*len <= 0)
|
adamc@861
|
213 return -1;
|
adamc@861
|
214
|
adamc@861
|
215 if ((*buf)[0] >> 7 == 0) {
|
adamc@861
|
216 int r = (*buf)[0];
|
adamc@861
|
217 ++*buf;
|
adamc@861
|
218 --*len;
|
adamc@861
|
219 return r;
|
adamc@861
|
220 }
|
adamc@861
|
221 else if (*len < 4)
|
adamc@861
|
222 return -1;
|
adamc@861
|
223 else {
|
adamc@861
|
224 int r = (((*buf)[3] & 0x7f) << 24) + ((*buf)[2] << 16) + ((*buf)[1] << 8) + (*buf)[0];
|
adamc@861
|
225 *buf += 4;
|
adamc@861
|
226 *len -= 4;
|
adamc@861
|
227 return r;
|
adamc@861
|
228 }
|
adamc@861
|
229 }
|
adamc@861
|
230
|
adamc@860
|
231 static int read_nvp(char *buf, int len, nvp *nv) {
|
adamc@860
|
232 int nameLength, valueLength;
|
adamc@860
|
233
|
adamc@860
|
234 if ((nameLength = read_funny_len(&buf, &len)) < 0)
|
adamc@860
|
235 return -1;
|
adamc@860
|
236 if ((valueLength = read_funny_len(&buf, &len)) < 0)
|
adamc@860
|
237 return -1;
|
adamc@860
|
238 if (len < nameLength + valueLength)
|
adamc@860
|
239 return -1;
|
adamc@860
|
240
|
adamc@861
|
241 if (!nv->name || nameLength+1 > nv->name_len) {
|
adamc@860
|
242 nv->name_len = nameLength+1;
|
adamc@861
|
243 nv->name = nv->name ? realloc(nv->name, nv->name_len) : malloc(nv->name_len);
|
adamc@860
|
244 }
|
adamc@861
|
245 if (!nv->value || valueLength+1 > nv->value_len) {
|
adamc@860
|
246 nv->value_len = valueLength+1;
|
adamc@861
|
247 nv->value = nv->value ? realloc(nv->value, nv->value_len) : malloc(nv->value_len);
|
adamc@860
|
248 }
|
adamc@860
|
249
|
adamc@860
|
250 memcpy(nv->name, buf, nameLength);
|
adamc@860
|
251 nv->name[nameLength] = 0;
|
adamc@860
|
252
|
adamc@860
|
253 memcpy(nv->value, buf + nameLength, valueLength);
|
adamc@860
|
254 nv->value[valueLength] = 0;
|
adamc@860
|
255
|
adamc@860
|
256 return 0;
|
adamc@860
|
257 }
|
adamc@860
|
258
|
adamc@859
|
259 static void *worker(void *data) {
|
adamc@859
|
260 int me = *(int *)data;
|
adamc@859
|
261 FCGI_Input *in = fastcgi_input();
|
adamc@859
|
262 FCGI_Output *out = fastcgi_output();
|
adamc@859
|
263 uw_context ctx = uw_request_new_context(out, log_error, log_debug);
|
adamc@859
|
264 uw_request_context rc = uw_new_request_context();
|
adamc@860
|
265 headers hs;
|
adamc@860
|
266 size_t body_size = 0;
|
adamc@860
|
267 char *body = malloc(0);
|
adamc@860
|
268 size_t path_size = 0;
|
adamc@860
|
269 char *path_buf = malloc(0);
|
adamc@861
|
270 int tries = 0;
|
adamc@860
|
271
|
adamc@860
|
272 hs.uppercased = malloc(0);
|
adamc@860
|
273 hs.uppercased_len = 0;
|
adamc@860
|
274 hs.nvps = malloc(sizeof(nvp));
|
adamc@860
|
275 hs.n_nvps = 1;
|
adamc@859
|
276
|
adamc@859
|
277 while (1) {
|
adamc@859
|
278 FCGI_Record *r;
|
adamc@860
|
279 size_t used_nvps = 0;
|
adamc@860
|
280 int body_len, body_read;
|
adamc@860
|
281 char *s;
|
adamc@860
|
282 char *method, *path, *path_info, *query_string;
|
adamc@859
|
283
|
adamc@859
|
284 in->sock = out->sock = uw_dequeue();
|
adamc@859
|
285
|
adamc@859
|
286 if (!(r = fastcgi_recv(in))) {
|
adamc@859
|
287 fprintf(stderr, "Error receiving initial message\n");
|
adamc@860
|
288 goto done;
|
adamc@859
|
289 }
|
adamc@861
|
290
|
adamc@859
|
291 if (r->type != FCGI_BEGIN_REQUEST) {
|
adamc@859
|
292 write_stderr(out, "First message is not BEGIN_REQUEST\n");
|
adamc@859
|
293 goto done;
|
adamc@859
|
294 } else if (((FCGI_BeginRequestBody *)&r->contentData)->roleB0 != FCGI_RESPONDER) {
|
adamc@859
|
295 write_stderr(out, "First message is not BEGIN_REQUEST\n");
|
adamc@859
|
296 goto done;
|
adamc@859
|
297 }
|
adamc@859
|
298
|
adamc@860
|
299 while (1) {
|
adamc@860
|
300 char *buf;
|
adamc@860
|
301
|
adamc@860
|
302 if (!(r = fastcgi_recv(in))) {
|
adamc@860
|
303 write_stderr(out, "Error receiving environment variables\n");
|
adamc@860
|
304 goto done;
|
adamc@860
|
305 }
|
adamc@860
|
306
|
adamc@860
|
307 if (r->type != FCGI_PARAMS) {
|
adamc@860
|
308 write_stderr(out, "Expected FCGI_PARAMS but got %d\n", r->type);
|
adamc@860
|
309 goto done;
|
adamc@860
|
310 }
|
adamc@860
|
311
|
adamc@860
|
312 if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0)
|
adamc@860
|
313 break;
|
adamc@860
|
314
|
adamc@860
|
315 if (used_nvps == hs.n_nvps-1) {
|
adamc@860
|
316 ++hs.n_nvps;
|
adamc@860
|
317 hs.nvps = realloc(hs.nvps, hs.n_nvps * sizeof(nvp));
|
adamc@860
|
318 hs.nvps[used_nvps].name = malloc(0);
|
adamc@860
|
319 hs.nvps[used_nvps].value = malloc(0);
|
adamc@860
|
320 hs.nvps[used_nvps].name_len = 0;
|
adamc@860
|
321 hs.nvps[used_nvps].value_len = 0;
|
adamc@860
|
322 }
|
adamc@860
|
323
|
adamc@860
|
324 if (read_nvp(r->contentData, (r->contentLengthB1 << 8) | r->contentLengthB0, &hs.nvps[used_nvps]) < 0) {
|
adamc@860
|
325 write_stderr(out, "Error reading FCGI_PARAMS name-value pair\n");
|
adamc@860
|
326 goto done;
|
adamc@860
|
327 }
|
adamc@860
|
328
|
adamc@860
|
329 ++used_nvps;
|
adamc@859
|
330 }
|
adamc@860
|
331
|
adamc@860
|
332 hs.nvps[used_nvps].name = NULL;
|
adamc@860
|
333
|
adamc@860
|
334 if (s = get_header(&hs, "Content-Length")) {
|
adamc@860
|
335 body_len = atoi(s);
|
adamc@860
|
336 if (body_len < 0) {
|
adamc@860
|
337 write_stderr(out, "Invalid Content-Length\n");
|
adamc@860
|
338 goto done;
|
adamc@860
|
339 }
|
adamc@860
|
340 } else
|
adamc@860
|
341 body_len = 0;
|
adamc@860
|
342
|
adamc@861
|
343 if (body_len+1 > body_size) {
|
adamc@861
|
344 body_size = body_len+1;
|
adamc@860
|
345 body = realloc(body, body_size);
|
adamc@860
|
346 }
|
adamc@860
|
347
|
adamc@860
|
348 for (body_read = 0; body_read < body_len; ) {
|
adamc@860
|
349 char *buf;
|
adamc@860
|
350 int this_len;
|
adamc@860
|
351
|
adamc@860
|
352 if (!(r = fastcgi_recv(in))) {
|
adamc@860
|
353 write_stderr(out, "Error receiving STDIN\n");
|
adamc@860
|
354 goto done;
|
adamc@860
|
355 }
|
adamc@860
|
356
|
adamc@860
|
357 if (r->type != FCGI_STDIN) {
|
adamc@860
|
358 write_stderr(out, "Expected FCGI_STDIN but got %d\n", r->type);
|
adamc@860
|
359 goto done;
|
adamc@860
|
360 }
|
adamc@860
|
361
|
adamc@860
|
362 if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0) {
|
adamc@860
|
363 write_stderr(out, "End of STDIN\n");
|
adamc@860
|
364 break;
|
adamc@860
|
365 }
|
adamc@860
|
366
|
adamc@860
|
367 this_len = (r->contentLengthB1 << 8) | r->contentLengthB0;
|
adamc@860
|
368
|
adamc@860
|
369 if (body_read + this_len > body_len) {
|
adamc@860
|
370 write_stderr(out, "Too much STDIN\n");
|
adamc@860
|
371 goto done;
|
adamc@860
|
372 }
|
adamc@860
|
373
|
adamc@860
|
374 memcpy(&body[body_read], r->contentData, this_len);
|
adamc@860
|
375 body_read += this_len;
|
adamc@860
|
376 }
|
adamc@860
|
377
|
adamc@861
|
378 body[body_read] = 0;
|
adamc@861
|
379
|
adamc@860
|
380 if (!(method = search_nvps(hs.nvps, "REQUEST_METHOD"))) {
|
adamc@860
|
381 write_stderr(out, "REQUEST_METHOD not set\n");
|
adamc@860
|
382 goto done;
|
adamc@860
|
383 }
|
adamc@860
|
384
|
adamc@860
|
385 if (!(path = search_nvps(hs.nvps, "SCRIPT_NAME"))) {
|
adamc@860
|
386 write_stderr(out, "SCRIPT_NAME not set\n");
|
adamc@860
|
387 goto done;
|
adamc@860
|
388 }
|
adamc@861
|
389
|
adamc@860
|
390 if (path_info = search_nvps(hs.nvps, "PATH_INFO")) {
|
adamc@860
|
391 int len1 = strlen(path), len2 = strlen(path_info);
|
adamc@860
|
392 int len = len1 + len2 + 1;
|
adamc@860
|
393
|
adamc@860
|
394 if (len > path_size) {
|
adamc@860
|
395 path_size = len;
|
adamc@860
|
396 path_buf = realloc(path_buf, path_size);
|
adamc@860
|
397 }
|
adamc@860
|
398
|
adamc@860
|
399 sprintf(path_buf, "%s%s", path, path_info);
|
adamc@860
|
400 path = path_buf;
|
adamc@860
|
401 }
|
adamc@860
|
402
|
adamc@860
|
403 if (!(query_string = search_nvps(hs.nvps, "QUERY_STRING")))
|
adamc@860
|
404 query_string = "";
|
adamc@860
|
405
|
adamc@860
|
406 uw_set_headers(ctx, get_header, &hs);
|
adamc@861
|
407
|
adamc@860
|
408 {
|
adamc@860
|
409 request_result rr;
|
adamc@860
|
410 FCGI_EndRequestBody *erb = (FCGI_EndRequestBody *)out->r.contentData;
|
adamc@860
|
411
|
adamc@860
|
412 rr = uw_request(rc, ctx, method, path, query_string, body, body_read,
|
adamc@860
|
413 on_success, on_failure,
|
adamc@860
|
414 out, log_error, log_debug,
|
adamc@860
|
415 in->sock);
|
adamc@860
|
416
|
adamc@860
|
417 if (rr == KEEP_OPEN)
|
adamc@860
|
418 goto done2;
|
adamc@860
|
419
|
adamc@860
|
420 uw_output(ctx, write_stdout, out);
|
adamc@860
|
421 close_stream(out, FCGI_STDOUT);
|
adamc@860
|
422 close_stream(out, FCGI_STDERR);
|
adamc@860
|
423
|
adamc@860
|
424 if (rr == SERVED)
|
adamc@860
|
425 erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0;
|
adamc@860
|
426 else
|
adamc@860
|
427 erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0xFF;
|
adamc@860
|
428
|
adamc@860
|
429 erb->protocolStatus = FCGI_REQUEST_COMPLETE;
|
adamc@860
|
430 fastcgi_send(out, FCGI_END_REQUEST, sizeof(FCGI_EndRequestBody));
|
adamc@860
|
431 }
|
adamc@859
|
432
|
adamc@859
|
433 done:
|
adamc@859
|
434 close(in->sock);
|
adamc@860
|
435 done2:
|
adamc@859
|
436 fastcgi_input_reset(in);
|
adamc@859
|
437 uw_reset(ctx);
|
adamc@859
|
438 }
|
adamc@859
|
439 }
|
adamc@859
|
440
|
adamc@859
|
441 static void help(char *cmd) {
|
adamc@859
|
442 printf("Usage: %s [-t <thread-count>]\n", cmd);
|
adamc@859
|
443 }
|
adamc@859
|
444
|
adamc@859
|
445 static void sigint(int signum) {
|
adamc@859
|
446 printf("Exiting....\n");
|
adamc@859
|
447 exit(0);
|
adamc@859
|
448 }
|
adamc@859
|
449
|
adamc@859
|
450 static loggers ls = {NULL, log_error, log_debug};
|
adamc@859
|
451
|
adamc@859
|
452 int main(int argc, char *argv[]) {
|
adamc@859
|
453 // The skeleton for this function comes from Beej's sockets tutorial.
|
adamc@859
|
454 struct sockaddr_in their_addr; // connector's address information
|
adamc@859
|
455 int sin_size, yes = 1;
|
adamc@859
|
456 int nthreads = 1, i, *names, opt;
|
adamc@859
|
457 char *fwsa = getenv("FCGI_WEB_SERVER_ADDRS"), *nthreads_s = getenv("URWEB_NUM_THREADS");
|
adamc@859
|
458
|
adamc@859
|
459 if (nthreads_s) {
|
adamc@859
|
460 nthreads = atoi(nthreads_s);
|
adamc@859
|
461 if (nthreads <= 0) {
|
adamc@859
|
462 fprintf(stderr, "Bad URWEB_NUM_THREADS value\n");
|
adamc@859
|
463 return 1;
|
adamc@859
|
464 }
|
adamc@859
|
465 }
|
adamc@859
|
466
|
adamc@859
|
467 signal(SIGINT, sigint);
|
adamc@859
|
468 signal(SIGPIPE, SIG_IGN);
|
adamc@859
|
469 signal(SIGUSR1, sigint);
|
adamc@859
|
470 signal(SIGTERM, sigint);
|
adamc@859
|
471
|
adamc@859
|
472 while ((opt = getopt(argc, argv, "ht:")) != -1) {
|
adamc@859
|
473 switch (opt) {
|
adamc@859
|
474 case '?':
|
adamc@859
|
475 fprintf(stderr, "Unknown command-line option");
|
adamc@859
|
476 help(argv[0]);
|
adamc@859
|
477 return 1;
|
adamc@859
|
478
|
adamc@859
|
479 case 'h':
|
adamc@859
|
480 help(argv[0]);
|
adamc@859
|
481 return 0;
|
adamc@859
|
482
|
adamc@859
|
483 case 't':
|
adamc@859
|
484 nthreads = atoi(optarg);
|
adamc@859
|
485 if (nthreads <= 0) {
|
adamc@859
|
486 fprintf(stderr, "Invalid thread count\n");
|
adamc@859
|
487 help(argv[0]);
|
adamc@859
|
488 return 1;
|
adamc@859
|
489 }
|
adamc@859
|
490 break;
|
adamc@859
|
491
|
adamc@859
|
492 default:
|
adamc@859
|
493 fprintf(stderr, "Unexpected getopt() behavior\n");
|
adamc@859
|
494 return 1;
|
adamc@859
|
495 }
|
adamc@859
|
496 }
|
adamc@859
|
497
|
adamc@860
|
498 uw_set_on_success("");
|
adamc@859
|
499 uw_request_init(NULL, log_error, log_debug);
|
adamc@859
|
500
|
adamc@859
|
501 names = calloc(nthreads, sizeof(int));
|
adamc@859
|
502
|
adamc@859
|
503 sin_size = sizeof their_addr;
|
adamc@859
|
504
|
adamc@859
|
505 {
|
adamc@859
|
506 pthread_t thread;
|
adamc@859
|
507 int name;
|
adamc@859
|
508
|
adamc@859
|
509 if (pthread_create(&thread, NULL, client_pruner, &ls)) {
|
adamc@859
|
510 fprintf(stderr, "Error creating pruner thread\n");
|
adamc@859
|
511 return 1;
|
adamc@859
|
512 }
|
adamc@859
|
513 }
|
adamc@859
|
514
|
adamc@859
|
515 for (i = 0; i < nthreads; ++i) {
|
adamc@859
|
516 pthread_t thread;
|
adamc@859
|
517 names[i] = i;
|
adamc@859
|
518 if (pthread_create(&thread, NULL, worker, &names[i])) {
|
adamc@859
|
519 fprintf(stderr, "Error creating worker thread #%d\n", i);
|
adamc@859
|
520 return 1;
|
adamc@859
|
521 }
|
adamc@859
|
522 }
|
adamc@859
|
523
|
adamc@859
|
524 while (1) {
|
adamc@859
|
525 int new_fd = accept(FCGI_LISTENSOCK_FILENO, (struct sockaddr *)&their_addr, &sin_size);
|
adamc@859
|
526
|
adamc@859
|
527 if (new_fd < 0) {
|
adamc@859
|
528 fprintf(stderr, "Socket accept failed\n");
|
adamc@859
|
529 return 1;
|
adamc@859
|
530 }
|
adamc@859
|
531
|
adamc@859
|
532 if (fwsa) {
|
adamc@859
|
533 char host[100], matched = 0;
|
adamc@859
|
534 char *ips, *sep;
|
adamc@859
|
535
|
adamc@859
|
536 if (getnameinfo((struct sockaddr *)&their_addr, sin_size, host, sizeof host, NULL, 0, NI_NUMERICHOST)) {
|
adamc@859
|
537 fprintf(stderr, "Remote IP determination failed\n");
|
adamc@859
|
538 return 1;
|
adamc@859
|
539 }
|
adamc@859
|
540
|
adamc@859
|
541 for (ips = fwsa; sep = strchr(ips, ','); ips = sep+1) {
|
adamc@859
|
542 if (!strncmp(ips, host, sep - ips)) {
|
adamc@859
|
543 matched = 1;
|
adamc@859
|
544 break;
|
adamc@859
|
545 }
|
adamc@859
|
546 }
|
adamc@859
|
547
|
adamc@859
|
548 if (!matched && strcmp(ips, host)) {
|
adamc@859
|
549 fprintf(stderr, "Remote address is not in FCGI_WEB_SERVER_ADDRS");
|
adamc@859
|
550 return 1;
|
adamc@859
|
551 }
|
adamc@859
|
552 }
|
adamc@859
|
553
|
adamc@859
|
554 uw_enqueue(new_fd);
|
adamc@859
|
555 }
|
adamc@859
|
556 }
|