Mercurial > urweb
comparison src/c/http.c @ 1917:398298ca82b9
Add keepalive option to the http protocol
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Thu, 28 Nov 2013 11:06:11 -0500 |
parents | 52e88e139b25 |
children | 5a7ae5acdcea |
comparison
equal
deleted
inserted
replaced
1916:e2345c438f08 | 1917:398298ca82b9 |
---|---|
4 #include <string.h> | 4 #include <string.h> |
5 #include <stdlib.h> | 5 #include <stdlib.h> |
6 #include <sys/types.h> | 6 #include <sys/types.h> |
7 #include <sys/socket.h> | 7 #include <sys/socket.h> |
8 #include <netinet/in.h> | 8 #include <netinet/in.h> |
9 #include <netinet/tcp.h> | |
9 #include <arpa/inet.h> | 10 #include <arpa/inet.h> |
10 #include <unistd.h> | 11 #include <unistd.h> |
11 #include <signal.h> | 12 #include <signal.h> |
12 #include <stdarg.h> | 13 #include <stdarg.h> |
13 | 14 |
18 #include "queue.h" | 19 #include "queue.h" |
19 | 20 |
20 extern uw_app uw_application; | 21 extern uw_app uw_application; |
21 | 22 |
22 int uw_backlog = SOMAXCONN; | 23 int uw_backlog = SOMAXCONN; |
24 static int keepalive = 0; | |
23 | 25 |
24 static char *get_header(void *data, const char *h) { | 26 static char *get_header(void *data, const char *h) { |
25 char *s = data; | 27 char *s = data; |
26 int len = strlen(h); | 28 int len = strlen(h); |
27 char *p; | 29 char *p; |
68 | 70 |
69 static void *worker(void *data) { | 71 static void *worker(void *data) { |
70 int me = *(int *)data; | 72 int me = *(int *)data; |
71 uw_context ctx = uw_request_new_context(me, &uw_application, NULL, log_error, log_debug); | 73 uw_context ctx = uw_request_new_context(me, &uw_application, NULL, log_error, log_debug); |
72 size_t buf_size = 2; | 74 size_t buf_size = 2; |
73 char *buf = malloc(buf_size); | 75 char *buf = malloc(buf_size), *back = buf; |
74 uw_request_context rc = uw_new_request_context(); | 76 uw_request_context rc = uw_new_request_context(); |
77 int sock = 0; | |
75 | 78 |
76 while (1) { | 79 while (1) { |
77 char *back = buf; | 80 if (sock == 0) { |
78 int sock = uw_dequeue(); | 81 back = buf; |
82 sock = uw_dequeue(); | |
83 } | |
79 | 84 |
80 printf("Handling connection with thread #%d.\n", me); | 85 printf("Handling connection with thread #%d.\n", me); |
81 | 86 |
82 while (1) { | 87 while (1) { |
83 int r; | 88 int r; |
84 char *method, *path, *query_string, *headers, *body, *s, *s2; | 89 char *method, *path, *query_string, *headers, *body, *after, *s, *s2; |
85 | 90 |
86 if (back - buf == buf_size - 1) { | 91 if (back - buf == buf_size - 1) { |
87 char *new_buf; | 92 char *new_buf; |
88 buf_size *= 2; | 93 buf_size *= 2; |
89 new_buf = realloc(buf, buf_size); | 94 new_buf = realloc(buf, buf_size); |
93 | 98 |
94 r = recv(sock, back, buf_size - 1 - (back - buf), 0); | 99 r = recv(sock, back, buf_size - 1 - (back - buf), 0); |
95 | 100 |
96 if (r < 0) { | 101 if (r < 0) { |
97 fprintf(stderr, "Recv failed\n"); | 102 fprintf(stderr, "Recv failed\n"); |
103 close(sock); | |
104 sock = 0; | |
98 break; | 105 break; |
99 } | 106 } |
100 | 107 |
101 if (r == 0) { | 108 if (r == 0) { |
102 printf("Connection closed.\n"); | 109 printf("Connection closed.\n"); |
110 close(sock); | |
111 sock = 0; | |
103 break; | 112 break; |
104 } | 113 } |
105 | 114 |
106 back += r; | 115 back += r; |
107 *back = 0; | 116 *back = 0; |
108 | 117 |
109 if ((body = strstr(buf, "\r\n\r\n"))) { | 118 if ((body = strstr(buf, "\r\n\r\n"))) { |
110 request_result rr; | 119 request_result rr; |
120 int should_keepalive = 0; | |
111 | 121 |
112 body[0] = body[1] = 0; | 122 body[0] = body[1] = 0; |
113 body += 4; | 123 body += 4; |
114 | 124 |
115 if ((s = strcasestr(buf, "\r\nContent-Length: ")) && s < body) { | 125 if ((s = strcasestr(buf, "\r\nContent-Length: ")) && s < body) { |
116 int clen; | 126 int clen; |
117 | 127 |
118 if (sscanf(s + 18, "%d\r\n", &clen) != 1) { | 128 if (sscanf(s + 18, "%d\r\n", &clen) != 1) { |
119 fprintf(stderr, "Malformed Content-Length header\n"); | 129 fprintf(stderr, "Malformed Content-Length header\n"); |
130 close(sock); | |
131 sock = 0; | |
120 break; | 132 break; |
121 } | 133 } |
122 | 134 |
123 while (back - body < clen) { | 135 while (back - body < clen) { |
124 if (back - buf == buf_size - 1) { | 136 if (back - buf == buf_size - 1) { |
136 r = recv(sock, back, buf_size - 1 - (back - buf), 0); | 148 r = recv(sock, back, buf_size - 1 - (back - buf), 0); |
137 | 149 |
138 if (r < 0) { | 150 if (r < 0) { |
139 fprintf(stderr, "Recv failed\n"); | 151 fprintf(stderr, "Recv failed\n"); |
140 close(sock); | 152 close(sock); |
153 sock = 0; | |
141 goto done; | 154 goto done; |
142 } | 155 } |
143 | 156 |
144 if (r == 0) { | 157 if (r == 0) { |
145 fprintf(stderr, "Connection closed.\n"); | 158 fprintf(stderr, "Connection closed.\n"); |
146 close(sock); | 159 close(sock); |
160 sock = 0; | |
147 goto done; | 161 goto done; |
148 } | 162 } |
149 | 163 |
150 back += r; | 164 back += r; |
151 *back = 0; | 165 *back = 0; |
152 } | 166 } |
153 } | 167 |
168 after = body + clen; | |
169 } else | |
170 after = body; | |
154 | 171 |
155 body[-4] = '\r'; | 172 body[-4] = '\r'; |
156 body[-3] = '\n'; | 173 body[-3] = '\n'; |
157 | 174 |
158 if (!(s = strstr(buf, "\r\n"))) { | 175 if (!(s = strstr(buf, "\r\n"))) { |
159 fprintf(stderr, "No newline in request\n"); | 176 fprintf(stderr, "No newline in request\n"); |
160 close(sock); | 177 close(sock); |
178 sock = 0; | |
161 goto done; | 179 goto done; |
162 } | 180 } |
163 | 181 |
164 body[-4] = body[-3] = 0; | 182 body[-4] = body[-3] = 0; |
165 | 183 |
169 | 187 |
170 strsep(&s, " "); | 188 strsep(&s, " "); |
171 if (!s) { | 189 if (!s) { |
172 fprintf(stderr, "No first space in HTTP command\n"); | 190 fprintf(stderr, "No first space in HTTP command\n"); |
173 close(sock); | 191 close(sock); |
192 sock = 0; | |
174 goto done; | 193 goto done; |
175 } | 194 } |
176 path = s; | 195 path = s; |
177 | 196 |
178 if ((s = strchr(path, ' '))) | 197 if ((s = strchr(path, ' '))) |
202 printf("Serving URI %s....\n", path); | 221 printf("Serving URI %s....\n", path); |
203 rr = uw_request(rc, ctx, method, path, query_string, body, back - body, | 222 rr = uw_request(rc, ctx, method, path, query_string, body, back - body, |
204 on_success, on_failure, | 223 on_success, on_failure, |
205 NULL, log_error, log_debug, | 224 NULL, log_error, log_debug, |
206 sock, uw_really_send, close); | 225 sock, uw_really_send, close); |
226 | |
207 if (rr != KEEP_OPEN) { | 227 if (rr != KEEP_OPEN) { |
208 char clen[100]; | 228 char clen[100]; |
209 | 229 |
210 uw_write_header(ctx, "Connection: close\r\n"); | 230 if (keepalive) { |
231 char *connection = uw_Basis_requestHeader(ctx, "Connection"); | |
232 | |
233 should_keepalive = !(connection && !strcmp(connection, "close")); | |
234 } | |
235 | |
236 if (!should_keepalive) | |
237 uw_write_header(ctx, "Connection: close\r\n"); | |
238 | |
211 sprintf(clen, "Content-length: %d\r\n", uw_pagelen(ctx)); | 239 sprintf(clen, "Content-length: %d\r\n", uw_pagelen(ctx)); |
212 uw_write_header(ctx, clen); | 240 uw_write_header(ctx, clen); |
213 uw_send(ctx, sock); | 241 uw_send(ctx, sock); |
214 } | 242 } |
215 | 243 |
216 if (rr == SERVED || rr == FAILED) | 244 if (rr == SERVED || rr == FAILED) { |
217 close(sock); | 245 if (should_keepalive) { |
218 else if (rr != KEEP_OPEN) | 246 // In case any other requests are queued up, shift |
247 // unprocessed part of buffer to front. | |
248 int kept = back - after; | |
249 memmove(buf, after, kept); | |
250 back = buf + kept; | |
251 } else { | |
252 close(sock); | |
253 sock = 0; | |
254 } | |
255 } else if (rr != KEEP_OPEN) | |
219 fprintf(stderr, "Illegal uw_request return code: %d\n", rr); | 256 fprintf(stderr, "Illegal uw_request return code: %d\n", rr); |
220 | 257 |
221 break; | 258 break; |
222 } | 259 } |
223 } | 260 } |
228 | 265 |
229 return NULL; | 266 return NULL; |
230 } | 267 } |
231 | 268 |
232 static void help(char *cmd) { | 269 static void help(char *cmd) { |
233 printf("Usage: %s [-p <port>] [-a <IP address>] [-t <thread count>]\n", cmd); | 270 printf("Usage: %s [-p <port>] [-a <IP address>] [-t <thread count>] [-k]\nThe '-k' option turns on HTTP keepalive.\n", cmd); |
234 } | 271 } |
235 | 272 |
236 static void sigint(int signum) { | 273 static void sigint(int signum) { |
237 printf("Exiting....\n"); | 274 printf("Exiting....\n"); |
238 exit(0); | 275 exit(0); |
252 signal(SIGPIPE, SIG_IGN); | 289 signal(SIGPIPE, SIG_IGN); |
253 | 290 |
254 my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP | 291 my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP |
255 memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero); | 292 memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero); |
256 | 293 |
257 while ((opt = getopt(argc, argv, "hp:a:t:")) != -1) { | 294 while ((opt = getopt(argc, argv, "hp:a:t:k")) != -1) { |
258 switch (opt) { | 295 switch (opt) { |
259 case '?': | 296 case '?': |
260 fprintf(stderr, "Unknown command-line option"); | 297 fprintf(stderr, "Unknown command-line option"); |
261 help(argv[0]); | 298 help(argv[0]); |
262 return 1; | 299 return 1; |
289 help(argv[0]); | 326 help(argv[0]); |
290 return 1; | 327 return 1; |
291 } | 328 } |
292 break; | 329 break; |
293 | 330 |
331 case 'k': | |
332 keepalive = 1; | |
333 break; | |
334 | |
294 default: | 335 default: |
295 fprintf(stderr, "Unexpected getopt() behavior\n"); | 336 fprintf(stderr, "Unexpected getopt() behavior\n"); |
296 return 1; | 337 return 1; |
297 } | 338 } |
298 } | 339 } |
356 return 1; | 397 return 1; |
357 } | 398 } |
358 | 399 |
359 printf("Accepted connection.\n"); | 400 printf("Accepted connection.\n"); |
360 | 401 |
402 if (keepalive) { | |
403 int flag = 1; | |
404 setsockopt(new_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); | |
405 } | |
406 | |
361 uw_enqueue(new_fd); | 407 uw_enqueue(new_fd); |
362 } | 408 } |
363 } | 409 } |
364 | 410 |
365 void *uw_init_client_data() { | 411 void *uw_init_client_data() { |