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 requestId,
|
adamc@859
|
69 unsigned short contentLength) {
|
adamc@859
|
70 o->r.type = type;
|
adamc@859
|
71 o->r.requestIdB1 = requestId >> 8;
|
adamc@859
|
72 o->r.requestIdB0 = requestId & 255;
|
adamc@859
|
73 o->r.contentLengthB1 = contentLength >> 8;
|
adamc@859
|
74 o->r.contentLengthB0 = contentLength & 255;
|
adamc@859
|
75 return uw_really_send(o->sock, &o->r, sizeof(o->r) - (65535 - contentLength));
|
adamc@859
|
76 }
|
adamc@859
|
77
|
adamc@859
|
78 #define LATEST(i) ((FCGI_Record *)(i->buf + i->used))
|
adamc@859
|
79
|
adamc@859
|
80 static FCGI_Record *fastcgi_recv(FCGI_Input *i) {
|
adamc@859
|
81 while (1) {
|
adamc@859
|
82 ssize_t n;
|
adamc@859
|
83
|
adamc@859
|
84 if (i->available >= i->used + sizeof(FCGI_Record) - 65535
|
adamc@859
|
85 && i->available >= i->used + sizeof(FCGI_Record) - 65535
|
adamc@859
|
86 + ((LATEST(i)->contentLengthB1 << 8) & LATEST(i)->contentLengthB0)
|
adamc@859
|
87 + LATEST(i)->paddingLength) {
|
adamc@859
|
88 FCGI_Record *r = LATEST(i);
|
adamc@859
|
89
|
adamc@859
|
90 i->used += sizeof(FCGI_Record) - 65535
|
adamc@859
|
91 + ((LATEST(i)->contentLengthB1 << 8) & LATEST(i)->contentLengthB0)
|
adamc@859
|
92 + LATEST(i)->paddingLength;
|
adamc@859
|
93
|
adamc@859
|
94 return r;
|
adamc@859
|
95 }
|
adamc@859
|
96
|
adamc@859
|
97 if (i->used > 0) {
|
adamc@859
|
98 memmove(i->buf, i->buf + i->used, i->available - i->used);
|
adamc@859
|
99 i->available -= i->used;
|
adamc@859
|
100 i->used = 0;
|
adamc@859
|
101 }
|
adamc@859
|
102
|
adamc@859
|
103 n = recv(i->sock, i->buf + i->available, sizeof(i->buf) - i->available, 0);
|
adamc@859
|
104
|
adamc@859
|
105 if (n <= 0)
|
adamc@859
|
106 return NULL;
|
adamc@859
|
107
|
adamc@859
|
108 i->available += n;
|
adamc@859
|
109 }
|
adamc@859
|
110 }
|
adamc@859
|
111
|
adamc@859
|
112 static char *get_header(void *data, const char *h) {
|
adamc@859
|
113 return NULL;
|
adamc@859
|
114 }
|
adamc@859
|
115
|
adamc@859
|
116 static void on_success(uw_context ctx) { }
|
adamc@859
|
117
|
adamc@859
|
118 static void on_failure(uw_context ctx) {
|
adamc@859
|
119 uw_write_header(ctx, "Status: 500 Internal Server Error\r\n");
|
adamc@859
|
120 }
|
adamc@859
|
121
|
adamc@859
|
122 static void write_stderr(FCGI_Output *o, const char *fmt, ...) {
|
adamc@859
|
123 int len;
|
adamc@859
|
124 va_list ap;
|
adamc@859
|
125 va_start(ap, fmt);
|
adamc@859
|
126
|
adamc@859
|
127 len = vsnprintf(o->r.contentData, 65535, fmt, ap);
|
adamc@859
|
128 if (len < 0)
|
adamc@859
|
129 fprintf(stderr, "vsnprintf() failed in log_error().\n");
|
adamc@859
|
130 else if (fastcgi_send(o, FCGI_STDERR, FCGI_NULL_REQUEST_ID, len))
|
adamc@859
|
131 fprintf(stderr, "fastcgi_send() failed in log_error().\n");
|
adamc@859
|
132 }
|
adamc@859
|
133
|
adamc@859
|
134 static void log_error(void *data, const char *fmt, ...) {
|
adamc@859
|
135 FCGI_Output *o = (FCGI_Output *)data;
|
adamc@859
|
136 va_list ap;
|
adamc@859
|
137 va_start(ap, fmt);
|
adamc@859
|
138
|
adamc@859
|
139 if (o) {
|
adamc@859
|
140 int len = vsnprintf(o->r.contentData, 65535, fmt, ap);
|
adamc@859
|
141 if (len < 0)
|
adamc@859
|
142 fprintf(stderr, "vsnprintf() failed in log_error().\n");
|
adamc@859
|
143 else if (fastcgi_send(o, FCGI_STDERR, FCGI_NULL_REQUEST_ID, len))
|
adamc@859
|
144 fprintf(stderr, "fastcgi_send() failed in log_error().\n");
|
adamc@859
|
145 } else
|
adamc@859
|
146 vfprintf(stderr, fmt, ap);
|
adamc@859
|
147 }
|
adamc@859
|
148
|
adamc@859
|
149 static void log_debug(void *data, const char *fmt, ...) {
|
adamc@859
|
150 }
|
adamc@859
|
151
|
adamc@859
|
152 static void *worker(void *data) {
|
adamc@859
|
153 int me = *(int *)data;
|
adamc@859
|
154 FCGI_Input *in = fastcgi_input();
|
adamc@859
|
155 FCGI_Output *out = fastcgi_output();
|
adamc@859
|
156 uw_context ctx = uw_request_new_context(out, log_error, log_debug);
|
adamc@859
|
157 uw_request_context rc = uw_new_request_context();
|
adamc@859
|
158
|
adamc@859
|
159 while (1) {
|
adamc@859
|
160 FCGI_Record *r;
|
adamc@859
|
161
|
adamc@859
|
162 in->sock = out->sock = uw_dequeue();
|
adamc@859
|
163
|
adamc@859
|
164 if (!(r = fastcgi_recv(in))) {
|
adamc@859
|
165 fprintf(stderr, "Error receiving initial message\n");
|
adamc@859
|
166 return NULL;
|
adamc@859
|
167 }
|
adamc@859
|
168
|
adamc@859
|
169 if (r->type != FCGI_BEGIN_REQUEST) {
|
adamc@859
|
170 write_stderr(out, "First message is not BEGIN_REQUEST\n");
|
adamc@859
|
171 goto done;
|
adamc@859
|
172 } else if (((FCGI_BeginRequestBody *)&r->contentData)->roleB0 != FCGI_RESPONDER) {
|
adamc@859
|
173 write_stderr(out, "First message is not BEGIN_REQUEST\n");
|
adamc@859
|
174 goto done;
|
adamc@859
|
175 }
|
adamc@859
|
176
|
adamc@859
|
177 if (!(r = fastcgi_recv(in))) {
|
adamc@859
|
178 fprintf(stderr, "Error receiving second message\n");
|
adamc@859
|
179 return NULL;
|
adamc@859
|
180 }
|
adamc@859
|
181 write_stderr(out, "Next message code: %d\n", r->type);
|
adamc@859
|
182
|
adamc@859
|
183 done:
|
adamc@859
|
184 close(in->sock);
|
adamc@859
|
185 fastcgi_input_reset(in);
|
adamc@859
|
186 uw_reset(ctx);
|
adamc@859
|
187 }
|
adamc@859
|
188 }
|
adamc@859
|
189
|
adamc@859
|
190 static void help(char *cmd) {
|
adamc@859
|
191 printf("Usage: %s [-t <thread-count>]\n", cmd);
|
adamc@859
|
192 }
|
adamc@859
|
193
|
adamc@859
|
194 static void sigint(int signum) {
|
adamc@859
|
195 printf("Exiting....\n");
|
adamc@859
|
196 exit(0);
|
adamc@859
|
197 }
|
adamc@859
|
198
|
adamc@859
|
199 static loggers ls = {NULL, log_error, log_debug};
|
adamc@859
|
200
|
adamc@859
|
201 int main(int argc, char *argv[]) {
|
adamc@859
|
202 // The skeleton for this function comes from Beej's sockets tutorial.
|
adamc@859
|
203 struct sockaddr_in their_addr; // connector's address information
|
adamc@859
|
204 int sin_size, yes = 1;
|
adamc@859
|
205 int nthreads = 1, i, *names, opt;
|
adamc@859
|
206 char *fwsa = getenv("FCGI_WEB_SERVER_ADDRS"), *nthreads_s = getenv("URWEB_NUM_THREADS");
|
adamc@859
|
207
|
adamc@859
|
208 if (nthreads_s) {
|
adamc@859
|
209 nthreads = atoi(nthreads_s);
|
adamc@859
|
210 if (nthreads <= 0) {
|
adamc@859
|
211 fprintf(stderr, "Bad URWEB_NUM_THREADS value\n");
|
adamc@859
|
212 return 1;
|
adamc@859
|
213 }
|
adamc@859
|
214 }
|
adamc@859
|
215
|
adamc@859
|
216 signal(SIGINT, sigint);
|
adamc@859
|
217 signal(SIGPIPE, SIG_IGN);
|
adamc@859
|
218 signal(SIGUSR1, sigint);
|
adamc@859
|
219 signal(SIGTERM, sigint);
|
adamc@859
|
220
|
adamc@859
|
221 while ((opt = getopt(argc, argv, "ht:")) != -1) {
|
adamc@859
|
222 switch (opt) {
|
adamc@859
|
223 case '?':
|
adamc@859
|
224 fprintf(stderr, "Unknown command-line option");
|
adamc@859
|
225 help(argv[0]);
|
adamc@859
|
226 return 1;
|
adamc@859
|
227
|
adamc@859
|
228 case 'h':
|
adamc@859
|
229 help(argv[0]);
|
adamc@859
|
230 return 0;
|
adamc@859
|
231
|
adamc@859
|
232 case 't':
|
adamc@859
|
233 nthreads = atoi(optarg);
|
adamc@859
|
234 if (nthreads <= 0) {
|
adamc@859
|
235 fprintf(stderr, "Invalid thread count\n");
|
adamc@859
|
236 help(argv[0]);
|
adamc@859
|
237 return 1;
|
adamc@859
|
238 }
|
adamc@859
|
239 break;
|
adamc@859
|
240
|
adamc@859
|
241 default:
|
adamc@859
|
242 fprintf(stderr, "Unexpected getopt() behavior\n");
|
adamc@859
|
243 return 1;
|
adamc@859
|
244 }
|
adamc@859
|
245 }
|
adamc@859
|
246
|
adamc@859
|
247 uw_request_init(NULL, log_error, log_debug);
|
adamc@859
|
248
|
adamc@859
|
249 names = calloc(nthreads, sizeof(int));
|
adamc@859
|
250
|
adamc@859
|
251 sin_size = sizeof their_addr;
|
adamc@859
|
252
|
adamc@859
|
253 {
|
adamc@859
|
254 pthread_t thread;
|
adamc@859
|
255 int name;
|
adamc@859
|
256
|
adamc@859
|
257 if (pthread_create(&thread, NULL, client_pruner, &ls)) {
|
adamc@859
|
258 fprintf(stderr, "Error creating pruner thread\n");
|
adamc@859
|
259 return 1;
|
adamc@859
|
260 }
|
adamc@859
|
261 }
|
adamc@859
|
262
|
adamc@859
|
263 for (i = 0; i < nthreads; ++i) {
|
adamc@859
|
264 pthread_t thread;
|
adamc@859
|
265 names[i] = i;
|
adamc@859
|
266 if (pthread_create(&thread, NULL, worker, &names[i])) {
|
adamc@859
|
267 fprintf(stderr, "Error creating worker thread #%d\n", i);
|
adamc@859
|
268 return 1;
|
adamc@859
|
269 }
|
adamc@859
|
270 }
|
adamc@859
|
271
|
adamc@859
|
272 while (1) {
|
adamc@859
|
273 int new_fd = accept(FCGI_LISTENSOCK_FILENO, (struct sockaddr *)&their_addr, &sin_size);
|
adamc@859
|
274
|
adamc@859
|
275 if (new_fd < 0) {
|
adamc@859
|
276 fprintf(stderr, "Socket accept failed\n");
|
adamc@859
|
277 return 1;
|
adamc@859
|
278 }
|
adamc@859
|
279
|
adamc@859
|
280 if (fwsa) {
|
adamc@859
|
281 char host[100], matched = 0;
|
adamc@859
|
282 char *ips, *sep;
|
adamc@859
|
283
|
adamc@859
|
284 if (getnameinfo((struct sockaddr *)&their_addr, sin_size, host, sizeof host, NULL, 0, NI_NUMERICHOST)) {
|
adamc@859
|
285 fprintf(stderr, "Remote IP determination failed\n");
|
adamc@859
|
286 return 1;
|
adamc@859
|
287 }
|
adamc@859
|
288
|
adamc@859
|
289 for (ips = fwsa; sep = strchr(ips, ','); ips = sep+1) {
|
adamc@859
|
290 if (!strncmp(ips, host, sep - ips)) {
|
adamc@859
|
291 matched = 1;
|
adamc@859
|
292 break;
|
adamc@859
|
293 }
|
adamc@859
|
294 }
|
adamc@859
|
295
|
adamc@859
|
296 if (!matched && strcmp(ips, host)) {
|
adamc@859
|
297 fprintf(stderr, "Remote address is not in FCGI_WEB_SERVER_ADDRS");
|
adamc@859
|
298 return 1;
|
adamc@859
|
299 }
|
adamc@859
|
300 }
|
adamc@859
|
301
|
adamc@859
|
302 uw_enqueue(new_fd);
|
adamc@859
|
303 }
|
adamc@859
|
304 }
|