about summary refs log tree commit homepage
path: root/ext
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-08-10 23:55:21 -0700
committerEric Wong <normalperson@yhbt.net>2009-08-11 00:51:10 -0700
commitb1adc4738b5fd9a42a5d686d67efae300d1c55ea (patch)
tree8027e31e9ebb0d0fe64cbaf85a704136c56f212d /ext
parent4d1121dbe7244537fc76757552345ea0e8f87a03 (diff)
downloadunicorn-b1adc4738b5fd9a42a5d686d67efae300d1c55ea.tar.gz
This should be used to detect if a request can really handle
keepalives and pipelining.  Currently, the rules are:

  1. MUST be a GET or HEAD request
  2. MUST be HTTP/1.1
  3. MUST NOT have "Connection: close" set

This also reduces the amount of garbage we create by
globalizing constants and using them whenever possible.
Diffstat (limited to 'ext')
-rw-r--r--ext/unicorn_http/global_variables.h9
-rw-r--r--ext/unicorn_http/unicorn_http.rl55
2 files changed, 57 insertions, 7 deletions
diff --git a/ext/unicorn_http/global_variables.h b/ext/unicorn_http/global_variables.h
index 1383ed4..b8b6221 100644
--- a/ext/unicorn_http/global_variables.h
+++ b/ext/unicorn_http/global_variables.h
@@ -15,16 +15,19 @@ static VALUE g_path_info;
 static VALUE g_server_name;
 static VALUE g_server_port;
 static VALUE g_server_protocol;
-static VALUE g_server_protocol_value;
 static VALUE g_http_host;
 static VALUE g_http_x_forwarded_proto;
 static VALUE g_http_transfer_encoding;
 static VALUE g_content_length;
 static VALUE g_http_trailer;
+static VALUE g_http_connection;
 static VALUE g_port_80;
 static VALUE g_port_443;
 static VALUE g_localhost;
 static VALUE g_http;
+static VALUE g_http_11;
+static VALUE g_GET;
+static VALUE g_HEAD;
 
 /** Defines common length and error messages for input length validation. */
 #define DEF_MAX_LENGTH(N, length) \
@@ -71,12 +74,14 @@ void init_globals(void)
   DEF_GLOBAL(server_name, "SERVER_NAME");
   DEF_GLOBAL(server_port, "SERVER_PORT");
   DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
-  DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
   DEF_GLOBAL(http_x_forwarded_proto, "HTTP_X_FORWARDED_PROTO");
   DEF_GLOBAL(port_80, "80");
   DEF_GLOBAL(port_443, "443");
   DEF_GLOBAL(localhost, "localhost");
   DEF_GLOBAL(http, "http");
+  DEF_GLOBAL(http_11, "HTTP/1.1");
+  DEF_GLOBAL(GET, "GET");
+  DEF_GLOBAL(HEAD, "HEAD");
 
   eHttpParserError =
          rb_define_class_under(mUnicorn, "HttpParserError", rb_eIOError);
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index ac74990..220069b 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -18,6 +18,10 @@
 #define UH_FL_HASTRAILER 0x8
 #define UH_FL_INTRAILER 0x10
 #define UH_FL_INCHUNK  0x20
+#define UH_FL_KAMETHOD 0x40
+#define UH_FL_KAVERSION 0x80
+
+#define UH_FL_KEEPALIVE (UH_FL_KAMETHOD | UH_FL_KAVERSION)
 
 struct http_parser {
   int cs; /* Ragel internal state */
@@ -46,6 +50,37 @@ static void finalize_header(VALUE req);
 #define PTR_TO(F) (buffer + hp->F)
 #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
 
+static void
+request_method(struct http_parser *hp, VALUE req, const char *ptr, size_t len)
+{
+  VALUE v;
+
+  if (CONST_MEM_EQ("GET", ptr, len)) {
+    hp->flags |= UH_FL_KAMETHOD;
+    v = g_GET;
+  } else if (CONST_MEM_EQ("HEAD", ptr, len)) {
+    hp->flags |= UH_FL_KAMETHOD;
+    v = g_HEAD;
+  } else {
+    v = rb_str_new(ptr, len);
+  }
+  rb_hash_aset(req, g_request_method, v);
+}
+
+static void
+http_version(struct http_parser *hp, VALUE req, const char *ptr, size_t len)
+{
+  VALUE v;
+
+  if (CONST_MEM_EQ("HTTP/1.1", ptr, len)) {
+    hp->flags |= UH_FL_KAVERSION;
+    v = g_http_11;
+  } else {
+    v = rb_str_new(ptr, len);
+  }
+  rb_hash_aset(req, g_http_version, v);
+}
+
 static void invalid_if_trailer(int flags)
 {
   if (flags & UH_FL_INTRAILER)
@@ -64,6 +99,9 @@ static void write_value(VALUE req, struct http_parser *hp,
   if (f == Qnil) {
     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 (STR_CSTR_CASE_EQ(v, "close"))
+      hp->flags &= ~UH_FL_KEEPALIVE;
   } else if (f == g_content_length) {
     hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v));
     if (hp->len.content < 0)
@@ -103,7 +141,7 @@ static void write_value(VALUE req, struct http_parser *hp,
   action start_value { MARK(mark, fpc); }
   action write_value { write_value(req, hp, buffer, fpc); }
   action request_method {
-    rb_hash_aset(req, g_request_method, STR_NEW(mark, fpc));
+    request_method(hp, req, PTR_TO(mark), LEN(mark, fpc));
   }
   action scheme {
     rb_hash_aset(req, g_rack_url_scheme, STR_NEW(mark, fpc));
@@ -136,9 +174,7 @@ static void write_value(VALUE req, struct http_parser *hp,
     VALIDATE_MAX_LENGTH(LEN(start.query, fpc), QUERY_STRING);
     rb_hash_aset(req, g_query_string, STR_NEW(start.query, fpc));
   }
-  action http_version {
-    rb_hash_aset(req, g_http_version, STR_NEW(mark, fpc));
-  }
+  action http_version { http_version(hp, req, PTR_TO(mark), LEN(mark, fpc)); }
   action request_path {
     VALUE val;
     size_t len = LEN(mark, fpc);
@@ -295,7 +331,7 @@ static void finalize_header(VALUE req)
   }
   rb_hash_aset(req, g_server_name, server_name);
   rb_hash_aset(req, g_server_port, server_port);
-  rb_hash_aset(req, g_server_protocol, g_server_protocol_value);
+  rb_hash_aset(req, g_server_protocol, g_http_11);
 
   /* rack requires QUERY_STRING */
   if (rb_hash_aref(req, g_query_string) == Qnil)
@@ -407,6 +443,13 @@ static VALUE HttpParser_body_eof(VALUE self)
   return hp->len.content == 0 ? Qtrue : Qfalse;
 }
 
+static VALUE HttpParser_keepalive(VALUE self)
+{
+  struct http_parser *hp = data_get(self);
+
+  return (hp->flags & UH_FL_KEEPALIVE) == UH_FL_KEEPALIVE ? Qtrue : Qfalse;
+}
+
 /**
  * call-seq:
  *    parser.filter_body(buf, data) -> nil/data
@@ -485,6 +528,7 @@ void Init_unicorn_http(void)
   rb_define_method(cHttpParser, "trailers", HttpParser_headers, 2);
   rb_define_method(cHttpParser, "content_length", HttpParser_content_length, 0);
   rb_define_method(cHttpParser, "body_eof?", HttpParser_body_eof, 0);
+  rb_define_method(cHttpParser, "keepalive?", HttpParser_keepalive, 0);
 
   /*
    * The maximum size a single chunk when using chunked transfer encoding.
@@ -506,5 +550,6 @@ void Init_unicorn_http(void)
   SET_GLOBAL(g_http_trailer, "TRAILER");
   SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
   SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
+  SET_GLOBAL(g_http_connection, "CONNECTION");
 }
 #undef SET_GLOBAL