about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--ext/unicorn_http/unicorn_http.rl42
1 files changed, 36 insertions, 6 deletions
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 70ba0a6..07a9881 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -22,6 +22,7 @@
 #define UH_FL_KAVERSION 0x80
 #define UH_FL_HASHEADER 0x100
 
+/* both of these flags need to be set for keepalive to be supported */
 #define UH_FL_KEEPALIVE (UH_FL_KAMETHOD | UH_FL_KAVERSION)
 
 struct http_parser {
@@ -57,11 +58,44 @@ static void finalize_header(struct http_parser *hp, VALUE req);
 #define HP_FL_UNSET(hp,fl) ((hp)->flags &= ~(UH_FL_##fl))
 #define HP_FL_ALL(hp,fl) (HP_FL_TEST(hp, fl) == (UH_FL_##fl))
 
+/*
+ * handles values of the "Connection:" header, keepalive is implied
+ * for HTTP/1.1 but needs to be explicitly enabled with HTTP/1.0
+ * Additionally, we require GET/HEAD requests to support keepalive.
+ */
+static void hp_keepalive_connection(struct http_parser *hp, VALUE val)
+{
+  /* REQUEST_METHOD is always set before any headers */
+  if (HP_FL_TEST(hp, KAMETHOD)) {
+    if (STR_CSTR_CASE_EQ(val, "keep-alive")) {
+      /* basically have HTTP/1.0 masquerade as HTTP/1.1+ */
+      HP_FL_SET(hp, KAVERSION);
+    } else if (STR_CSTR_CASE_EQ(val, "close")) {
+      /*
+       * it doesn't matter what HTTP version or request method we have,
+       * if a client says "Connection: close", we disable keepalive
+       */
+      HP_FL_UNSET(hp, KEEPALIVE);
+    } else {
+      /*
+       * client could've sent anything, ignore it for now.  Maybe
+       * "HP_FL_UNSET(hp, KEEPALIVE);" just in case?
+       * Raising an exception might be too mean...
+       */
+    }
+  }
+}
+
 static void
 request_method(struct http_parser *hp, VALUE req, const char *ptr, size_t len)
 {
   VALUE v;
 
+  /*
+   * we only support keepalive for GET and HEAD requests for now other
+   * methods are too rarely seen to be worth optimizing.  POST is unsafe
+   * since some clients send extra bytes after POST bodies.
+   */
   if (CONST_MEM_EQ("GET", ptr, len)) {
     HP_FL_SET(hp, KAMETHOD);
     v = g_GET;
@@ -82,6 +116,7 @@ http_version(struct http_parser *hp, VALUE req, const char *ptr, size_t len)
   HP_FL_SET(hp, HASHEADER);
 
   if (CONST_MEM_EQ("HTTP/1.1", ptr, len)) {
+    /* HTTP/1.1 implies keepalive unless "Connection: close" is set */
     HP_FL_SET(hp, KAVERSION);
     v = g_http_11;
   } else if (CONST_MEM_EQ("HTTP/1.0", ptr, len)) {
@@ -137,12 +172,7 @@ static void write_value(VALUE req, struct http_parser *hp,
     VALIDATE_MAX_LENGTH(hp->s.field_len, FIELD_NAME);
     f = uncommon_field(PTR_TO(start.field), hp->s.field_len);
   } else if (f == g_http_connection) {
-    if (HP_FL_TEST(hp, KAMETHOD)) {
-      if (STR_CSTR_CASE_EQ(v, "keep-alive"))
-        HP_FL_SET(hp, KAVERSION);
-      else if (STR_CSTR_CASE_EQ(v, "close"))
-        HP_FL_UNSET(hp, KEEPALIVE);
-    }
+    hp_keepalive_connection(hp, v);
   } else if (f == g_content_length) {
     hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v));
     if (hp->len.content < 0)