# HG changeset patch # User Adam Chlipala # Date 1385654771 18000 # Node ID 398298ca82b9aa8f508fd750c774c5d8585ad78e # Parent e2345c438f089617c2605ceb30120b000111d279 Add keepalive option to the http protocol diff -r e2345c438f08 -r 398298ca82b9 src/c/http.c --- a/src/c/http.c Wed Nov 27 15:42:24 2013 -0500 +++ b/src/c/http.c Thu Nov 28 11:06:11 2013 -0500 @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ extern uw_app uw_application; int uw_backlog = SOMAXCONN; +static int keepalive = 0; static char *get_header(void *data, const char *h) { char *s = data; @@ -70,18 +72,21 @@ int me = *(int *)data; uw_context ctx = uw_request_new_context(me, &uw_application, NULL, log_error, log_debug); size_t buf_size = 2; - char *buf = malloc(buf_size); + char *buf = malloc(buf_size), *back = buf; uw_request_context rc = uw_new_request_context(); + int sock = 0; while (1) { - char *back = buf; - int sock = uw_dequeue(); + if (sock == 0) { + back = buf; + sock = uw_dequeue(); + } printf("Handling connection with thread #%d.\n", me); while (1) { int r; - char *method, *path, *query_string, *headers, *body, *s, *s2; + char *method, *path, *query_string, *headers, *body, *after, *s, *s2; if (back - buf == buf_size - 1) { char *new_buf; @@ -95,11 +100,15 @@ if (r < 0) { fprintf(stderr, "Recv failed\n"); + close(sock); + sock = 0; break; } if (r == 0) { printf("Connection closed.\n"); + close(sock); + sock = 0; break; } @@ -108,6 +117,7 @@ if ((body = strstr(buf, "\r\n\r\n"))) { request_result rr; + int should_keepalive = 0; body[0] = body[1] = 0; body += 4; @@ -117,6 +127,8 @@ if (sscanf(s + 18, "%d\r\n", &clen) != 1) { fprintf(stderr, "Malformed Content-Length header\n"); + close(sock); + sock = 0; break; } @@ -138,19 +150,24 @@ if (r < 0) { fprintf(stderr, "Recv failed\n"); close(sock); + sock = 0; goto done; } if (r == 0) { fprintf(stderr, "Connection closed.\n"); close(sock); + sock = 0; goto done; } back += r; *back = 0; } - } + + after = body + clen; + } else + after = body; body[-4] = '\r'; body[-3] = '\n'; @@ -158,6 +175,7 @@ if (!(s = strstr(buf, "\r\n"))) { fprintf(stderr, "No newline in request\n"); close(sock); + sock = 0; goto done; } @@ -171,6 +189,7 @@ if (!s) { fprintf(stderr, "No first space in HTTP command\n"); close(sock); + sock = 0; goto done; } path = s; @@ -204,18 +223,36 @@ on_success, on_failure, NULL, log_error, log_debug, sock, uw_really_send, close); + if (rr != KEEP_OPEN) { char clen[100]; - uw_write_header(ctx, "Connection: close\r\n"); + if (keepalive) { + char *connection = uw_Basis_requestHeader(ctx, "Connection"); + + should_keepalive = !(connection && !strcmp(connection, "close")); + } + + if (!should_keepalive) + uw_write_header(ctx, "Connection: close\r\n"); + sprintf(clen, "Content-length: %d\r\n", uw_pagelen(ctx)); uw_write_header(ctx, clen); uw_send(ctx, sock); } - if (rr == SERVED || rr == FAILED) - close(sock); - else if (rr != KEEP_OPEN) + if (rr == SERVED || rr == FAILED) { + if (should_keepalive) { + // In case any other requests are queued up, shift + // unprocessed part of buffer to front. + int kept = back - after; + memmove(buf, after, kept); + back = buf + kept; + } else { + close(sock); + sock = 0; + } + } else if (rr != KEEP_OPEN) fprintf(stderr, "Illegal uw_request return code: %d\n", rr); break; @@ -230,7 +267,7 @@ } static void help(char *cmd) { - printf("Usage: %s [-p ] [-a ] [-t ]\n", cmd); + printf("Usage: %s [-p ] [-a ] [-t ] [-k]\nThe '-k' option turns on HTTP keepalive.\n", cmd); } static void sigint(int signum) { @@ -254,7 +291,7 @@ my_addr.sin_addr.s_addr = INADDR_ANY; // auto-fill with my IP memset(my_addr.sin_zero, '\0', sizeof my_addr.sin_zero); - while ((opt = getopt(argc, argv, "hp:a:t:")) != -1) { + while ((opt = getopt(argc, argv, "hp:a:t:k")) != -1) { switch (opt) { case '?': fprintf(stderr, "Unknown command-line option"); @@ -291,6 +328,10 @@ } break; + case 'k': + keepalive = 1; + break; + default: fprintf(stderr, "Unexpected getopt() behavior\n"); return 1; @@ -358,6 +399,11 @@ printf("Accepted connection.\n"); + if (keepalive) { + int flag = 1; + setsockopt(new_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); + } + uw_enqueue(new_fd); } }