Mercurial > urweb
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 } |