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() {