comparison src/c/http.c @ 855:28e42b22424d

Initial implementation of protocols in Settings
author Adam Chlipala <adamc@hcoop.net>
date Tue, 23 Jun 2009 15:56:04 -0400
parents src/c/driver.c@158d980889ac
children 86ec89baee01
comparison
equal deleted inserted replaced
854:158d980889ac 855:28e42b22424d
1 #define _GNU_SOURCE
2
3 #include <stdio.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <netinet/in.h>
9 #include <unistd.h>
10 #include <signal.h>
11
12 #include <pthread.h>
13
14 #include <mhash.h>
15
16 #include "urweb.h"
17 #include "request.h"
18
19 int uw_backlog = 10;
20
21 typedef struct node {
22 int fd;
23 struct node *next;
24 } *node;
25
26 static node front = NULL, back = NULL;
27
28 static int empty() {
29 return front == NULL;
30 }
31
32 static void enqueue(int fd) {
33 node n = malloc(sizeof(struct node));
34
35 n->fd = fd;
36 n->next = NULL;
37 if (back)
38 back->next = n;
39 else
40 front = n;
41 back = n;
42 }
43
44 static int dequeue() {
45 int ret = front->fd;
46
47 front = front->next;
48 if (!front)
49 back = NULL;
50
51 return ret;
52 }
53
54 static pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
55 static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
56
57 static char *get_header(void *data, const char *h) {
58 char *s = data;
59 int len = strlen(h);
60 char *p;
61
62 while (p = strchr(s, ':')) {
63 if (p - s == len && !strncasecmp(s, h, len)) {
64 return p + 2;
65 } else {
66 if ((s = strchr(p, 0)) && s[1] != 0)
67 s += 2;
68 else
69 return NULL;
70 }
71 }
72
73 return NULL;
74 }
75
76 static void *worker(void *data) {
77 int me = *(int *)data;
78 uw_context ctx = uw_request_new_context();
79 size_t buf_size = 2;
80 char *buf = malloc(buf_size);
81 uw_request_context rc = uw_new_request_context();
82
83 while (1) {
84 char *back = buf;
85 int sock;
86
87 pthread_mutex_lock(&queue_mutex);
88 while (empty())
89 pthread_cond_wait(&queue_cond, &queue_mutex);
90 sock = dequeue();
91 pthread_mutex_unlock(&queue_mutex);
92
93 printf("Handling connection with thread #%d.\n", me);
94
95 while (1) {
96 int r;
97 char *method, *path, *query_string, *headers, *body, *s, *s2;
98
99 if (back - buf == buf_size - 1) {
100 char *new_buf;
101 buf_size *= 2;
102 new_buf = realloc(buf, buf_size);
103 back = new_buf + (back - buf);
104 buf = new_buf;
105 }
106
107 r = recv(sock, back, buf_size - 1 - (back - buf), 0);
108
109 if (r < 0) {
110 fprintf(stderr, "Recv failed\n");
111 break;
112 }
113
114 if (r == 0) {
115 printf("Connection closed.\n");
116 break;
117 }
118
119 back += r;
120 *back = 0;
121
122 if ((body = strstr(buf, "\r\n\r\n"))) {
123 request_result rr;
124
125 body[0] = body[1] = 0;
126 body += 4;
127
128 if ((s = strcasestr(buf, "\r\nContent-Length: ")) && s < body) {
129 int clen;
130
131 if (sscanf(s + 18, "%d\r\n", &clen) != 1) {
132 fprintf(stderr, "Malformed Content-Length header\n");
133 break;
134 }
135
136 while (back - body < clen) {
137 if (back - buf == buf_size - 1) {
138 char *new_buf;
139 buf_size *= 2;
140 new_buf = realloc(buf, buf_size);
141
142 back = new_buf + (back - buf);
143 body = new_buf + (body - buf);
144 s = new_buf + (s - buf);
145
146 buf = new_buf;
147 }
148
149 r = recv(sock, back, buf_size - 1 - (back - buf), 0);
150
151 if (r < 0) {
152 fprintf(stderr, "Recv failed\n");
153 close(sock);
154 goto done;
155 }
156
157 if (r == 0) {
158 fprintf(stderr, "Connection closed.\n");
159 close(sock);
160 goto done;
161 }
162
163 back += r;
164 *back = 0;
165 }
166 }
167
168 if (!(s = strstr(buf, "\r\n"))) {
169 fprintf(stderr, "No newline in request\n");
170 close(sock);
171 goto done;
172 }
173
174 *s = 0;
175 headers = s + 2;
176 method = s = buf;
177
178 if (!strsep(&s, " ")) {
179 fprintf(stderr, "No first space in HTTP command\n");
180 close(sock);
181 goto done;
182 }
183 path = s;
184
185 if (s = strchr(path, ' '))
186 *s = 0;
187
188 if (s = strchr(path, '?')) {
189 *s = 0;
190 query_string = s+1;
191 }
192 else
193 query_string = NULL;
194
195 s = headers;
196 while (s2 = strchr(s, '\r')) {
197 s = s2;
198
199 if (s[1] == 0)
200 break;
201
202 *s = 0;
203 s += 2;
204 }
205
206 uw_set_headers(ctx, get_header, headers);
207
208 rr = uw_request(rc, ctx, method, path, query_string, body, back - body, sock);
209 uw_send(ctx, sock);
210
211 if (rr == SERVED || rr == FAILED)
212 close(sock);
213 else if (rr != KEEP_OPEN)
214 fprintf(stderr, "Illegal uw_request return code: %d\n", rr);
215
216 break;
217 }
218 }
219
220 done:
221 uw_reset(ctx);
222 }
223 }
224
225 static void help(char *cmd) {
226 printf("Usage: %s [-p <port>] [-t <thread-count>]\n", cmd);
227 }
228
229 static void sigint(int signum) {
230 printf("Exiting....\n");
231 exit(0);
232 }
233
234 int main(int argc, char *argv[]) {
235 // The skeleton for this function comes from Beej's sockets tutorial.
236 int sockfd; // listen on sock_fd
237 struct sockaddr_in my_addr;
238 struct sockaddr_in their_addr; // connector's address information
239 int sin_size, yes = 1;
240 int uw_port = 8080, nthreads = 1, i, *names, opt;
241
242 signal(SIGINT, sigint);
243 signal(SIGPIPE, SIG_IGN);
244
245 while ((opt = getopt(argc, argv, "hp:t:")) != -1) {
246 switch (opt) {
247 case '?':
248 fprintf(stderr, "Unknown command-line option");
249 help(argv[0]);
250 return 1;
251
252 case 'h':
253 help(argv[0]);
254 return 0;
255
256 case 'p':
257 uw_port = atoi(optarg);
258 if (uw_port <= 0) {
259 fprintf(stderr, "Invalid port number\n");
260 help(argv[0]);
261 return 1;
262 }
263 break;
264
265 case 't':
266 nthreads = atoi(optarg);
267 if (nthreads <= 0) {
268 fprintf(stderr, "Invalid thread count\n");
269 help(argv[0]);
270 return 1;
271 }
272 break;
273
274 default:
275 fprintf(stderr, "Unexpected getopt() behavior\n");
276 return 1;
277 }
278 }
279
280 uw_request_init();
281
282 names = calloc(nthreads, sizeof(int));
283
284 sockfd = socket(PF_INET, SOCK_STREAM, 0); // do some error checking!
285
286 if (sockfd < 0) {
287 fprintf(stderr, "Listener socket creation failed\n");
288 return 1;
289 }
290
291 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0) {
292 fprintf(stderr, "Listener socket option setting failed\n");
293 return 1;
294 }
295
296 my_addr.sin_family = AF_INET; // host byte order
297 my_addr.sin_port = htons(uw_port); // short, network byte order
298 my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP
299 memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero);
300
301 if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof my_addr) < 0) {
302 fprintf(stderr, "Listener socket bind failed\n");
303 return 1;
304 }
305
306 if (listen(sockfd, uw_backlog) < 0) {
307 fprintf(stderr, "Socket listen failed\n");
308 return 1;
309 }
310
311 sin_size = sizeof their_addr;
312
313 printf("Listening on port %d....\n", uw_port);
314
315 {
316 pthread_t thread;
317 int name;
318
319 if (pthread_create(&thread, NULL, client_pruner, &name)) {
320 fprintf(stderr, "Error creating pruner thread\n");
321 return 1;
322 }
323 }
324
325 for (i = 0; i < nthreads; ++i) {
326 pthread_t thread;
327 names[i] = i;
328 if (pthread_create(&thread, NULL, worker, &names[i])) {
329 fprintf(stderr, "Error creating worker thread #%d\n", i);
330 return 1;
331 }
332 }
333
334 while (1) {
335 int new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
336
337 if (new_fd < 0) {
338 fprintf(stderr, "Socket accept failed\n");
339 return 1;
340 }
341
342 printf("Accepted connection.\n");
343
344 pthread_mutex_lock(&queue_mutex);
345 enqueue(new_fd);
346 pthread_cond_broadcast(&queue_cond);
347 pthread_mutex_unlock(&queue_mutex);
348 }
349 }