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