about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-07-31 23:46:21 -0700
committerEric Wong <normalperson@yhbt.net>2009-08-09 01:18:49 -0700
commitfb7f6739c5bc041a28ae45fea80f840459523f8e (patch)
treef4afd31a6d96a36d8865ccf180aaf51fb54229b8
parente710d55dd171ab560d9cc1b1684f0e415b153c76 (diff)
downloadunicorn-fb7f6739c5bc041a28ae45fea80f840459523f8e.tar.gz
Use Data_Make_Struct instead of Data_Wrap_Struct to avoid extra
steps/code in object allocation.  The GC will also free()
implicitly if no free callback is passed to Data_Make_Struct,
and we don't need an extra method here...

Since we're more comfortable with object management nowadays,
just make the data_get() fail with an assertion failure if it
somehow (very unlikely) ends up getting a NULL parser object.
Unicorn itself has no way of recovering other than throwing
errors to clients anyways and we have bigger problems if there's
a GC bug causing this.

Then, finally, reduce the size of our http_parser struct even
more (we'll add an int back later) since we know it's safe to
do so...
-rw-r--r--ext/unicorn_http/ext_help.h9
-rw-r--r--ext/unicorn_http/unicorn_http.c99
-rw-r--r--ext/unicorn_http/unicorn_http.h304
-rw-r--r--ext/unicorn_http/unicorn_http.rl84
4 files changed, 205 insertions, 291 deletions
diff --git a/ext/unicorn_http/ext_help.h b/ext/unicorn_http/ext_help.h
index 17f7b01..8220600 100644
--- a/ext/unicorn_http/ext_help.h
+++ b/ext/unicorn_http/ext_help.h
@@ -3,10 +3,11 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 
-#ifdef DEBUG
-#define TRACE()  fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
-#else
-#define TRACE()
+#ifndef RSTRING_PTR
+#define RSTRING_PTR(s) (RSTRING(s)->ptr)
+#endif
+#ifndef RSTRING_LEN
+#define RSTRING_LEN(s) (RSTRING(s)->len)
 #endif
 
 #endif
diff --git a/ext/unicorn_http/unicorn_http.c b/ext/unicorn_http/unicorn_http.c
index 41936cd..51ddeaa 100644
--- a/ext/unicorn_http/unicorn_http.c
+++ b/ext/unicorn_http/unicorn_http.c
@@ -3,8 +3,6 @@
  * Copyright (c) 2005 Zed A. Shaw
  * You can redistribute it and/or modify it under the same terms as Ruby.
  */
-#include "ruby.h"
-#include "ext_help.h"
 #include <assert.h>
 #include <string.h>
 #include "unicorn_http.h"
@@ -14,18 +12,10 @@ static http_parser *data_get(VALUE self)
   http_parser *http;
 
   Data_Get_Struct(self, http_parser, http);
-  if (!http)
-    rb_raise(rb_eArgError, "NULL found for http when shouldn't be.");
+  assert(http);
   return http;
 }
 
-#ifndef RSTRING_PTR
-#define RSTRING_PTR(s) (RSTRING(s)->ptr)
-#endif
-#ifndef RSTRING_LEN
-#define RSTRING_LEN(s) (RSTRING(s)->len)
-#endif
-
 static VALUE mUnicorn;
 static VALUE cHttpParser;
 static VALUE eHttpParserError;
@@ -169,10 +159,9 @@ static VALUE find_common_field_value(const char *field, size_t flen)
   return Qnil;
 }
 
-static void http_field(void *data, const char *field,
+static void http_field(VALUE req, const char *field,
                        size_t flen, const char *value, size_t vlen)
 {
-  VALUE req = (VALUE)data;
   VALUE f = Qnil;
 
   VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
@@ -203,57 +192,44 @@ static void http_field(void *data, const char *field,
   rb_hash_aset(req, f, rb_str_new(value, vlen));
 }
 
-static void request_method(void *data, const char *at, size_t length)
+static void request_method(VALUE req, const char *at, size_t length)
 {
-  VALUE req = (VALUE)data;
-  VALUE val = Qnil;
-
-  val = rb_str_new(at, length);
-  rb_hash_aset(req, global_request_method, val);
+  rb_hash_aset(req, global_request_method, rb_str_new(at, length));
 }
 
-static void scheme(void *data, const char *at, size_t length)
+static void scheme(VALUE req, const char *at, size_t length)
 {
-  rb_hash_aset((VALUE)data, global_rack_url_scheme, rb_str_new(at, length));
+  rb_hash_aset(req, global_rack_url_scheme, rb_str_new(at, length));
 }
 
-static void host(void *data, const char *at, size_t length)
+static void host(VALUE req, const char *at, size_t length)
 {
-  rb_hash_aset((VALUE)data, global_http_host, rb_str_new(at, length));
+  rb_hash_aset(req, global_http_host, rb_str_new(at, length));
 }
 
-static void request_uri(void *data, const char *at, size_t length)
+static void request_uri(VALUE req, const char *at, size_t length)
 {
-  VALUE req = (VALUE)data;
-  VALUE val = Qnil;
-
   VALIDATE_MAX_LENGTH(length, REQUEST_URI);
 
-  val = rb_str_new(at, length);
-  rb_hash_aset(req, global_request_uri, val);
+  rb_hash_aset(req, global_request_uri, rb_str_new(at, length));
 
   /* "OPTIONS * HTTP/1.1\r\n" is a valid request */
   if (length == 1 && *at == '*') {
-    val = rb_str_new(NULL, 0);
+    VALUE val = rb_str_new(NULL, 0);
     rb_hash_aset(req, global_request_path, val);
     rb_hash_aset(req, global_path_info, val);
   }
 }
 
-static void fragment(void *data, const char *at, size_t length)
+static void fragment(VALUE req, const char *at, size_t length)
 {
-  VALUE req = (VALUE)data;
-  VALUE val = Qnil;
-
   VALIDATE_MAX_LENGTH(length, FRAGMENT);
 
-  val = rb_str_new(at, length);
-  rb_hash_aset(req, global_fragment, val);
+  rb_hash_aset(req, global_fragment, rb_str_new(at, length));
 }
 
-static void request_path(void *data, const char *at, size_t length)
+static void request_path(VALUE req, const char *at, size_t length)
 {
-  VALUE req = (VALUE)data;
   VALUE val = Qnil;
 
   VALIDATE_MAX_LENGTH(length, REQUEST_PATH);
@@ -266,28 +242,21 @@ static void request_path(void *data, const char *at, size_t length)
     rb_hash_aset(req, global_path_info, val);
 }
 
-static void query_string(void *data, const char *at, size_t length)
+static void query_string(VALUE req, const char *at, size_t length)
 {
-  VALUE req = (VALUE)data;
-  VALUE val = Qnil;
-
   VALIDATE_MAX_LENGTH(length, QUERY_STRING);
 
-  val = rb_str_new(at, length);
-  rb_hash_aset(req, global_query_string, val);
+  rb_hash_aset(req, global_query_string, rb_str_new(at, length));
 }
 
-static void http_version(void *data, const char *at, size_t length)
+static void http_version(VALUE req, const char *at, size_t length)
 {
-  VALUE req = (VALUE)data;
-  VALUE val = rb_str_new(at, length);
-  rb_hash_aset(req, global_http_version, val);
+  rb_hash_aset(req, global_http_version, rb_str_new(at, length));
 }
 
 /** Finalizes the request header to have a bunch of stuff that's needed. */
-static void header_done(void *data, const char *at, size_t length)
+static void header_done(VALUE req, const char *at, size_t length)
 {
-  VALUE req = (VALUE)data;
   VALUE server_name = global_localhost;
   VALUE server_port = global_port_80;
   VALUE temp;
@@ -337,25 +306,10 @@ static void header_done(void *data, const char *at, size_t length)
   }
 }
 
-static void HttpParser_free(void *data) {
-  TRACE();
-
-  if(data) {
-    free(data);
-  }
-}
-
-
 static VALUE HttpParser_alloc(VALUE klass)
 {
-  VALUE obj;
-  http_parser *hp = ALLOC_N(http_parser, 1);
-  TRACE();
-  http_parser_init(hp);
-
-  obj = Data_Wrap_Struct(klass, NULL, HttpParser_free, hp);
-
-  return obj;
+  http_parser *hp;
+  return Data_Make_Struct(klass, http_parser, NULL, NULL, hp);
 }
 
 
@@ -390,7 +344,7 @@ static VALUE HttpParser_reset(VALUE self)
 
 /**
  * call-seq:
- *    parser.execute(req_hash, data) -> true/false
+ *    parser.execute(req, data) -> true/false
  *
  * Takes a Hash and a String of data, parses the String of data filling
  * in the Hash returning a boolean to indicate whether or not parsing
@@ -401,17 +355,16 @@ static VALUE HttpParser_reset(VALUE self)
  * will need to wrap the parser with an exception handling block.
  */
 
-static VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data)
+static VALUE HttpParser_execute(VALUE self, VALUE req, VALUE data)
 {
   http_parser *http = data_get(self);
   char *dptr = RSTRING_PTR(data);
   long dlen = RSTRING_LEN(data);
 
-  if (http->nread < dlen) {
-    http->data = (void *)req_hash;
-    http_parser_execute(http, dptr, dlen);
+  if (http->start.offset < dlen) {
+    http_parser_execute(http, req, dptr, dlen);
 
-    VALIDATE_MAX_LENGTH(http->nread, HEADER);
+    VALIDATE_MAX_LENGTH(http->start.offset, HEADER);
 
     if (!http_parser_has_error(http))
       return http_parser_is_finished(http) ? Qtrue : Qfalse;
diff --git a/ext/unicorn_http/unicorn_http.h b/ext/unicorn_http/unicorn_http.h
index d39a22b..c651ed3 100644
--- a/ext/unicorn_http/unicorn_http.h
+++ b/ext/unicorn_http/unicorn_http.h
@@ -7,30 +7,32 @@
 #ifndef unicorn_http_h
 #define unicorn_http_h
 
+#include "ruby.h"
+#include "ext_help.h"
 #include <sys/types.h>
 
-static void http_field(void *data, const char *field,
+static void http_field(VALUE req, const char *field,
                        size_t flen, const char *value, size_t vlen);
-static void request_method(void *data, const char *at, size_t length);
-static void scheme(void *data, const char *at, size_t length);
-static void host(void *data, const char *at, size_t length);
-static void request_uri(void *data, const char *at, size_t length);
-static void fragment(void *data, const char *at, size_t length);
-static void request_path(void *data, const char *at, size_t length);
-static void query_string(void *data, const char *at, size_t length);
-static void http_version(void *data, const char *at, size_t length);
-static void header_done(void *data, const char *at, size_t length);
+static void request_method(VALUE req, const char *at, size_t length);
+static void scheme(VALUE req, const char *at, size_t length);
+static void host(VALUE req, const char *at, size_t length);
+static void request_uri(VALUE req, const char *at, size_t length);
+static void fragment(VALUE req, const char *at, size_t length);
+static void request_path(VALUE req, const char *at, size_t length);
+static void query_string(VALUE req, const char *at, size_t length);
+static void http_version(VALUE req, const char *at, size_t length);
+static void header_done(VALUE req, const char *at, size_t length);
 
 typedef struct http_parser {
   int cs;
-  size_t body_start;
-  size_t nread;
+  union {
+    size_t body;
+    size_t field;
+    size_t query;
+    size_t offset;
+  } start;
   size_t mark;
-  size_t field_start;
   size_t field_len;
-  size_t query_start;
-
-  void *data;
 } http_parser;
 
 static int http_parser_has_error(http_parser *parser);
@@ -61,12 +63,12 @@ static void downcase_char(char *c)
 /** Machine **/
 
 
-#line 109 "unicorn_http.rl"
+#line 101 "unicorn_http.rl"
 
 
 /** Data **/
 
-#line 70 "unicorn_http.h"
+#line 72 "unicorn_http.h"
 static const int http_parser_start = 1;
 static const int http_parser_first_final = 63;
 static const int http_parser_error = 0;
@@ -74,28 +76,28 @@ static const int http_parser_error = 0;
 static const int http_parser_en_main = 1;
 
 
-#line 113 "unicorn_http.rl"
+#line 105 "unicorn_http.rl"
 
 static void http_parser_init(http_parser *parser) {
   int cs = 0;
   memset(parser, 0, sizeof(*parser));
 
-#line 84 "unicorn_http.h"
+#line 86 "unicorn_http.h"
         {
         cs = http_parser_start;
         }
 
-#line 118 "unicorn_http.rl"
+#line 110 "unicorn_http.rl"
   parser->cs = cs;
 }
 
 /** exec **/
 static void http_parser_execute(
-  http_parser *parser, const char *buffer, size_t len)
+  http_parser *parser, VALUE req, const char *buffer, size_t len)
 {
   const char *p, *pe;
   int cs = parser->cs;
-  size_t off = parser->nread;
+  size_t off = parser->start.offset;
 
   assert(off <= len && "offset past end of buffer");
 
@@ -106,7 +108,7 @@ static void http_parser_execute(
   assert(pe - p == len - off && "pointers aren't same distance");
 
 
-#line 110 "unicorn_http.h"
+#line 112 "unicorn_http.h"
         {
         if ( p == pe )
                 goto _test_eof;
@@ -130,14 +132,14 @@ st0:
 cs = 0;
         goto _out;
 tr0:
-#line 64 "unicorn_http.rl"
+#line 66 "unicorn_http.rl"
         {MARK(mark, p); }
         goto st2;
 st2:
         if ( ++p == pe )
                 goto _test_eof2;
 case 2:
-#line 141 "unicorn_http.h"
+#line 143 "unicorn_http.h"
         switch( (*p) ) {
                 case 32: goto tr2;
                 case 36: goto st44;
@@ -153,10 +155,8 @@ case 2:
                 goto st44;
         goto st0;
 tr2:
-#line 77 "unicorn_http.rl"
-        {
-    request_method(parser->data, PTR_TO(mark), LEN(mark, p));
-  }
+#line 80 "unicorn_http.rl"
+        { request_method(req, PTR_TO(mark), LEN(mark, p)); }
         goto st3;
 st3:
         if ( ++p == pe )
@@ -171,7 +171,7 @@ case 3:
         }
         goto st0;
 tr4:
-#line 64 "unicorn_http.rl"
+#line 66 "unicorn_http.rl"
         {MARK(mark, p); }
         goto st4;
 st4:
@@ -185,74 +185,60 @@ case 4:
         }
         goto st0;
 tr7:
-#line 82 "unicorn_http.rl"
-        {
-    request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
-  }
+#line 83 "unicorn_http.rl"
+        { request_uri(req, PTR_TO(mark), LEN(mark, p)); }
         goto st5;
 tr30:
-#line 64 "unicorn_http.rl"
+#line 66 "unicorn_http.rl"
         {MARK(mark, p); }
-#line 85 "unicorn_http.rl"
-        {
-    fragment(parser->data, PTR_TO(mark), LEN(mark, p));
-  }
+#line 84 "unicorn_http.rl"
+        { fragment(req, PTR_TO(mark), LEN(mark, p)); }
         goto st5;
 tr33:
-#line 85 "unicorn_http.rl"
-        {
-    fragment(parser->data, PTR_TO(mark), LEN(mark, p));
-  }
+#line 84 "unicorn_http.rl"
+        { fragment(req, PTR_TO(mark), LEN(mark, p)); }
         goto st5;
 tr37:
-#line 98 "unicorn_http.rl"
-        {
-    request_path(parser->data, PTR_TO(mark), LEN(mark,p));
-  }
-#line 82 "unicorn_http.rl"
-        {
-    request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
-  }
+#line 92 "unicorn_http.rl"
+        { request_path(req, PTR_TO(mark), LEN(mark,p)); }
+#line 83 "unicorn_http.rl"
+        { request_uri(req, PTR_TO(mark), LEN(mark, p)); }
         goto st5;
 tr48:
-#line 89 "unicorn_http.rl"
-        {MARK(query_start, p); }
-#line 90 "unicorn_http.rl"
+#line 86 "unicorn_http.rl"
+        {MARK(start.query, p); }
+#line 87 "unicorn_http.rl"
         {
-    query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
-  }
-#line 82 "unicorn_http.rl"
-        {
-    request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    query_string(req, PTR_TO(start.query), LEN(start.query, p));
   }
+#line 83 "unicorn_http.rl"
+        { request_uri(req, PTR_TO(mark), LEN(mark, p)); }
         goto st5;
 tr52:
-#line 90 "unicorn_http.rl"
+#line 87 "unicorn_http.rl"
         {
-    query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
-  }
-#line 82 "unicorn_http.rl"
-        {
-    request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    query_string(req, PTR_TO(start.query), LEN(start.query, p));
   }
+#line 83 "unicorn_http.rl"
+        { request_uri(req, PTR_TO(mark), LEN(mark, p)); }
         goto st5;
 st5:
         if ( ++p == pe )
                 goto _test_eof5;
 case 5:
-#line 244 "unicorn_http.h"
+#line 230 "unicorn_http.h"
         if ( (*p) == 72 )
                 goto tr9;
         goto st0;
 tr9:
-#line 64 "unicorn_http.rl"
+#line 66 "unicorn_http.rl"
         {MARK(mark, p); }
         goto st6;
 st6:
         if ( ++p == pe )
                 goto _test_eof6;
 case 6:
-#line 256 "unicorn_http.h"
+#line 242 "unicorn_http.h"
         if ( (*p) == 84 )
                 goto st7;
         goto st0;
@@ -310,30 +296,30 @@ case 13:
                 goto st13;
         goto st0;
 tr17:
-#line 94 "unicorn_http.rl"
-        {
-    http_version(parser->data, PTR_TO(mark), LEN(mark, p));
-  }
+#line 91 "unicorn_http.rl"
+        { http_version(req, PTR_TO(mark), LEN(mark, p)); }
         goto st14;
 tr25:
-#line 73 "unicorn_http.rl"
+#line 75 "unicorn_http.rl"
         { MARK(mark, p); }
-#line 74 "unicorn_http.rl"
+#line 76 "unicorn_http.rl"
         {
-    http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
+    http_field(req, PTR_TO(start.field), parser->field_len,
+               PTR_TO(mark), LEN(mark, p));
   }
         goto st14;
 tr28:
-#line 74 "unicorn_http.rl"
+#line 76 "unicorn_http.rl"
         {
-    http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
+    http_field(req, PTR_TO(start.field), parser->field_len,
+               PTR_TO(mark), LEN(mark, p));
   }
         goto st14;
 st14:
         if ( ++p == pe )
                 goto _test_eof14;
 case 14:
-#line 337 "unicorn_http.h"
+#line 323 "unicorn_http.h"
         if ( (*p) == 10 )
                 goto st15;
         goto st0;
@@ -373,10 +359,10 @@ case 16:
                 goto tr21;
         goto st0;
 tr21:
-#line 102 "unicorn_http.rl"
+#line 94 "unicorn_http.rl"
         {
-    parser->body_start = p - buffer + 1;
-    header_done(parser->data, p + 1, pe - p - 1);
+    parser->start.body = p - buffer + 1;
+    header_done(req, p + 1, pe - p - 1);
     {p++; cs = 63; goto _out;}
   }
         goto st63;
@@ -384,23 +370,23 @@ st63:
         if ( ++p == pe )
                 goto _test_eof63;
 case 63:
-#line 388 "unicorn_http.h"
+#line 374 "unicorn_http.h"
         goto st0;
 tr20:
-#line 66 "unicorn_http.rl"
-        { MARK(field_start, p); }
-#line 67 "unicorn_http.rl"
+#line 68 "unicorn_http.rl"
+        { MARK(start.field, p); }
+#line 69 "unicorn_http.rl"
         { snake_upcase_char((char *)p); }
         goto st17;
 tr22:
-#line 67 "unicorn_http.rl"
+#line 69 "unicorn_http.rl"
         { snake_upcase_char((char *)p); }
         goto st17;
 st17:
         if ( ++p == pe )
                 goto _test_eof17;
 case 17:
-#line 404 "unicorn_http.h"
+#line 390 "unicorn_http.h"
         switch( (*p) ) {
                 case 33: goto tr22;
                 case 58: goto tr23;
@@ -426,80 +412,70 @@ case 17:
                 goto tr22;
         goto st0;
 tr23:
-#line 69 "unicorn_http.rl"
+#line 71 "unicorn_http.rl"
         {
-    parser->field_len = LEN(field_start, p);
+    parser->field_len = LEN(start.field, p);
   }
         goto st18;
 tr26:
-#line 73 "unicorn_http.rl"
+#line 75 "unicorn_http.rl"
         { MARK(mark, p); }
         goto st18;
 st18:
         if ( ++p == pe )
                 goto _test_eof18;
 case 18:
-#line 443 "unicorn_http.h"
+#line 429 "unicorn_http.h"
         switch( (*p) ) {
                 case 13: goto tr25;
                 case 32: goto tr26;
         }
         goto tr24;
 tr24:
-#line 73 "unicorn_http.rl"
+#line 75 "unicorn_http.rl"
         { MARK(mark, p); }
         goto st19;
 st19:
         if ( ++p == pe )
                 goto _test_eof19;
 case 19:
-#line 457 "unicorn_http.h"
+#line 443 "unicorn_http.h"
         if ( (*p) == 13 )
                 goto tr28;
         goto st19;
 tr8:
-#line 82 "unicorn_http.rl"
-        {
-    request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
-  }
+#line 83 "unicorn_http.rl"
+        { request_uri(req, PTR_TO(mark), LEN(mark, p)); }
         goto st20;
 tr38:
-#line 98 "unicorn_http.rl"
-        {
-    request_path(parser->data, PTR_TO(mark), LEN(mark,p));
-  }
-#line 82 "unicorn_http.rl"
-        {
-    request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
-  }
+#line 92 "unicorn_http.rl"
+        { request_path(req, PTR_TO(mark), LEN(mark,p)); }
+#line 83 "unicorn_http.rl"
+        { request_uri(req, PTR_TO(mark), LEN(mark, p)); }
         goto st20;
 tr49:
-#line 89 "unicorn_http.rl"
-        {MARK(query_start, p); }
-#line 90 "unicorn_http.rl"
+#line 86 "unicorn_http.rl"
+        {MARK(start.query, p); }
+#line 87 "unicorn_http.rl"
         {
-    query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
-  }
-#line 82 "unicorn_http.rl"
-        {
-    request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    query_string(req, PTR_TO(start.query), LEN(start.query, p));
   }
+#line 83 "unicorn_http.rl"
+        { request_uri(req, PTR_TO(mark), LEN(mark, p)); }
         goto st20;
 tr53:
-#line 90 "unicorn_http.rl"
+#line 87 "unicorn_http.rl"
         {
-    query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
-  }
-#line 82 "unicorn_http.rl"
-        {
-    request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
+    query_string(req, PTR_TO(start.query), LEN(start.query, p));
   }
+#line 83 "unicorn_http.rl"
+        { request_uri(req, PTR_TO(mark), LEN(mark, p)); }
         goto st20;
 st20:
         if ( ++p == pe )
                 goto _test_eof20;
 case 20:
-#line 503 "unicorn_http.h"
+#line 479 "unicorn_http.h"
         switch( (*p) ) {
                 case 32: goto tr30;
                 case 35: goto st0;
@@ -510,14 +486,14 @@ case 20:
                 goto st0;
         goto tr29;
 tr29:
-#line 64 "unicorn_http.rl"
+#line 66 "unicorn_http.rl"
         {MARK(mark, p); }
         goto st21;
 st21:
         if ( ++p == pe )
                 goto _test_eof21;
 case 21:
-#line 521 "unicorn_http.h"
+#line 497 "unicorn_http.h"
         switch( (*p) ) {
                 case 32: goto tr33;
                 case 35: goto st0;
@@ -528,14 +504,14 @@ case 21:
                 goto st0;
         goto st21;
 tr31:
-#line 64 "unicorn_http.rl"
+#line 66 "unicorn_http.rl"
         {MARK(mark, p); }
         goto st22;
 st22:
         if ( ++p == pe )
                 goto _test_eof22;
 case 22:
-#line 539 "unicorn_http.h"
+#line 515 "unicorn_http.h"
         if ( (*p) < 65 ) {
                 if ( 48 <= (*p) && (*p) <= 57 )
                         goto st23;
@@ -559,20 +535,20 @@ case 23:
                 goto st21;
         goto st0;
 tr5:
-#line 64 "unicorn_http.rl"
+#line 66 "unicorn_http.rl"
         {MARK(mark, p); }
         goto st24;
 tr65:
-#line 81 "unicorn_http.rl"
-        { host(parser->data, PTR_TO(mark), LEN(mark, p)); }
-#line 64 "unicorn_http.rl"
+#line 82 "unicorn_http.rl"
+        { host(req, PTR_TO(mark), LEN(mark, p)); }
+#line 66 "unicorn_http.rl"
         {MARK(mark, p); }
         goto st24;
 st24:
         if ( ++p == pe )
                 goto _test_eof24;
 case 24:
-#line 576 "unicorn_http.h"
+#line 552 "unicorn_http.h"
         switch( (*p) ) {
                 case 32: goto tr37;
                 case 35: goto tr38;
@@ -611,16 +587,14 @@ case 26:
                 goto st24;
         goto st0;
 tr40:
-#line 98 "unicorn_http.rl"
-        {
-    request_path(parser->data, PTR_TO(mark), LEN(mark,p));
-  }
+#line 92 "unicorn_http.rl"
+        { request_path(req, PTR_TO(mark), LEN(mark,p)); }
         goto st27;
 st27:
         if ( ++p == pe )
                 goto _test_eof27;
 case 27:
-#line 624 "unicorn_http.h"
+#line 598 "unicorn_http.h"
         switch( (*p) ) {
                 case 32: goto tr7;
                 case 35: goto tr8;
@@ -658,16 +632,14 @@ case 29:
                 goto st27;
         goto st0;
 tr41:
-#line 98 "unicorn_http.rl"
-        {
-    request_path(parser->data, PTR_TO(mark), LEN(mark,p));
-  }
+#line 92 "unicorn_http.rl"
+        { request_path(req, PTR_TO(mark), LEN(mark,p)); }
         goto st30;
 st30:
         if ( ++p == pe )
                 goto _test_eof30;
 case 30:
-#line 671 "unicorn_http.h"
+#line 643 "unicorn_http.h"
         switch( (*p) ) {
                 case 32: goto tr48;
                 case 35: goto tr49;
@@ -678,14 +650,14 @@ case 30:
                 goto st0;
         goto tr47;
 tr47:
-#line 89 "unicorn_http.rl"
-        {MARK(query_start, p); }
+#line 86 "unicorn_http.rl"
+        {MARK(start.query, p); }
         goto st31;
 st31:
         if ( ++p == pe )
                 goto _test_eof31;
 case 31:
-#line 689 "unicorn_http.h"
+#line 661 "unicorn_http.h"
         switch( (*p) ) {
                 case 32: goto tr52;
                 case 35: goto tr53;
@@ -696,14 +668,14 @@ case 31:
                 goto st0;
         goto st31;
 tr50:
-#line 89 "unicorn_http.rl"
-        {MARK(query_start, p); }
+#line 86 "unicorn_http.rl"
+        {MARK(start.query, p); }
         goto st32;
 st32:
         if ( ++p == pe )
                 goto _test_eof32;
 case 32:
-#line 707 "unicorn_http.h"
+#line 679 "unicorn_http.h"
         if ( (*p) < 65 ) {
                 if ( 48 <= (*p) && (*p) <= 57 )
                         goto st33;
@@ -727,58 +699,58 @@ case 33:
                 goto st31;
         goto st0;
 tr6:
-#line 64 "unicorn_http.rl"
+#line 66 "unicorn_http.rl"
         {MARK(mark, p); }
-#line 68 "unicorn_http.rl"
+#line 70 "unicorn_http.rl"
         { downcase_char((char *)p); }
         goto st34;
 st34:
         if ( ++p == pe )
                 goto _test_eof34;
 case 34:
-#line 740 "unicorn_http.h"
+#line 712 "unicorn_http.h"
         switch( (*p) ) {
                 case 84: goto tr56;
                 case 116: goto tr56;
         }
         goto st0;
 tr56:
-#line 68 "unicorn_http.rl"
+#line 70 "unicorn_http.rl"
         { downcase_char((char *)p); }
         goto st35;
 st35:
         if ( ++p == pe )
                 goto _test_eof35;
 case 35:
-#line 754 "unicorn_http.h"
+#line 726 "unicorn_http.h"
         switch( (*p) ) {
                 case 84: goto tr57;
                 case 116: goto tr57;
         }
         goto st0;
 tr57:
-#line 68 "unicorn_http.rl"
+#line 70 "unicorn_http.rl"
         { downcase_char((char *)p); }
         goto st36;
 st36:
         if ( ++p == pe )
                 goto _test_eof36;
 case 36:
-#line 768 "unicorn_http.h"
+#line 740 "unicorn_http.h"
         switch( (*p) ) {
                 case 80: goto tr58;
                 case 112: goto tr58;
         }
         goto st0;
 tr58:
-#line 68 "unicorn_http.rl"
+#line 70 "unicorn_http.rl"
         { downcase_char((char *)p); }
         goto st37;
 st37:
         if ( ++p == pe )
                 goto _test_eof37;
 case 37:
-#line 782 "unicorn_http.h"
+#line 754 "unicorn_http.h"
         switch( (*p) ) {
                 case 58: goto tr59;
                 case 83: goto tr60;
@@ -786,14 +758,14 @@ case 37:
         }
         goto st0;
 tr59:
-#line 80 "unicorn_http.rl"
-        { scheme(parser->data, PTR_TO(mark), LEN(mark, p)); }
+#line 81 "unicorn_http.rl"
+        { scheme(req, PTR_TO(mark), LEN(mark, p)); }
         goto st38;
 st38:
         if ( ++p == pe )
                 goto _test_eof38;
 case 38:
-#line 797 "unicorn_http.h"
+#line 769 "unicorn_http.h"
         if ( (*p) == 47 )
                 goto st39;
         goto st0;
@@ -823,14 +795,14 @@ case 40:
                 goto tr63;
         goto st0;
 tr63:
-#line 64 "unicorn_http.rl"
+#line 66 "unicorn_http.rl"
         {MARK(mark, p); }
         goto st41;
 st41:
         if ( ++p == pe )
                 goto _test_eof41;
 case 41:
-#line 834 "unicorn_http.h"
+#line 806 "unicorn_http.h"
         switch( (*p) ) {
                 case 47: goto tr65;
                 case 58: goto st42;
@@ -855,14 +827,14 @@ case 42:
                 goto st42;
         goto st0;
 tr60:
-#line 68 "unicorn_http.rl"
+#line 70 "unicorn_http.rl"
         { downcase_char((char *)p); }
         goto st43;
 st43:
         if ( ++p == pe )
                 goto _test_eof43;
 case 43:
-#line 866 "unicorn_http.h"
+#line 838 "unicorn_http.h"
         if ( (*p) == 58 )
                 goto tr59;
         goto st0;
@@ -1265,18 +1237,16 @@ case 62:
         _out: {}
         }
 
-#line 138 "unicorn_http.rl"
+#line 130 "unicorn_http.rl"
 
   if (!http_parser_has_error(parser))
     parser->cs = cs;
-  parser->nread += p - (buffer + off);
+  parser->start.offset = p - buffer;
 
   assert(p <= pe && "buffer overflow after parsing execute");
-  assert(parser->nread <= len && "nread longer than length");
-  assert(parser->body_start <= len && "body starts after buffer end");
+  assert(parser->start.offset <= len && "start.offset longer than length");
   assert(parser->mark < len && "mark is after buffer end");
   assert(parser->field_len <= len && "field has length longer than whole buffer");
-  assert(parser->field_start < len && "field starts after buffer end");
 }
 
 static int http_parser_has_error(http_parser *parser) {
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index d487a83..ceb4073 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -5,30 +5,32 @@
 #ifndef unicorn_http_h
 #define unicorn_http_h
 
+#include "ruby.h"
+#include "ext_help.h"
 #include <sys/types.h>
 
-static void http_field(void *data, const char *field,
+static void http_field(VALUE req, const char *field,
                        size_t flen, const char *value, size_t vlen);
-static void request_method(void *data, const char *at, size_t length);
-static void scheme(void *data, const char *at, size_t length);
-static void host(void *data, const char *at, size_t length);
-static void request_uri(void *data, const char *at, size_t length);
-static void fragment(void *data, const char *at, size_t length);
-static void request_path(void *data, const char *at, size_t length);
-static void query_string(void *data, const char *at, size_t length);
-static void http_version(void *data, const char *at, size_t length);
-static void header_done(void *data, const char *at, size_t length);
+static void request_method(VALUE req, const char *at, size_t length);
+static void scheme(VALUE req, const char *at, size_t length);
+static void host(VALUE req, const char *at, size_t length);
+static void request_uri(VALUE req, const char *at, size_t length);
+static void fragment(VALUE req, const char *at, size_t length);
+static void request_path(VALUE req, const char *at, size_t length);
+static void query_string(VALUE req, const char *at, size_t length);
+static void http_version(VALUE req, const char *at, size_t length);
+static void header_done(VALUE req, const char *at, size_t length);
 
 typedef struct http_parser {
   int cs;
-  size_t body_start;
-  size_t nread;
+  union {
+    size_t body;
+    size_t field;
+    size_t query;
+    size_t offset;
+  } start;
   size_t mark;
-  size_t field_start;
   size_t field_len;
-  size_t query_start;
-
-  void *data;
 } http_parser;
 
 static int http_parser_has_error(http_parser *parser);
@@ -63,45 +65,35 @@ static void downcase_char(char *c)
 
   action mark {MARK(mark, fpc); }
 
-  action start_field { MARK(field_start, fpc); }
+  action start_field { MARK(start.field, fpc); }
   action snake_upcase_field { snake_upcase_char((char *)fpc); }
   action downcase_char { downcase_char((char *)fpc); }
   action write_field {
-    parser->field_len = LEN(field_start, fpc);
+    parser->field_len = LEN(start.field, fpc);
   }
 
   action start_value { MARK(mark, fpc); }
   action write_value {
-    http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
-  }
-  action request_method {
-    request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
-  }
-  action scheme { scheme(parser->data, PTR_TO(mark), LEN(mark, fpc)); }
-  action host { host(parser->data, PTR_TO(mark), LEN(mark, fpc)); }
-  action request_uri {
-    request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
-  }
-  action fragment {
-    fragment(parser->data, PTR_TO(mark), LEN(mark, fpc));
+    http_field(req, PTR_TO(start.field), parser->field_len,
+               PTR_TO(mark), LEN(mark, fpc));
   }
+  action request_method { request_method(req, PTR_TO(mark), LEN(mark, fpc)); }
+  action scheme { scheme(req, PTR_TO(mark), LEN(mark, fpc)); }
+  action host { host(req, PTR_TO(mark), LEN(mark, fpc)); }
+  action request_uri { request_uri(req, PTR_TO(mark), LEN(mark, fpc)); }
+  action fragment { fragment(req, PTR_TO(mark), LEN(mark, fpc)); }
 
-  action start_query {MARK(query_start, fpc); }
+  action start_query {MARK(start.query, fpc); }
   action query_string {
-    query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
+    query_string(req, PTR_TO(start.query), LEN(start.query, fpc));
   }
 
-  action http_version {
-    http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
-  }
-
-  action request_path {
-    request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
-  }
+  action http_version { http_version(req, PTR_TO(mark), LEN(mark, fpc)); }
+  action request_path { request_path(req, PTR_TO(mark), LEN(mark,fpc)); }
 
   action done {
-    parser->body_start = fpc - buffer + 1;
-    header_done(parser->data, fpc + 1, pe - fpc - 1);
+    parser->start.body = fpc - buffer + 1;
+    header_done(req, fpc + 1, pe - fpc - 1);
     fbreak;
   }
 
@@ -120,11 +112,11 @@ static void http_parser_init(http_parser *parser) {
 
 /** exec **/
 static void http_parser_execute(
-  http_parser *parser, const char *buffer, size_t len)
+  http_parser *parser, VALUE req, const char *buffer, size_t len)
 {
   const char *p, *pe;
   int cs = parser->cs;
-  size_t off = parser->nread;
+  size_t off = parser->start.offset;
 
   assert(off <= len && "offset past end of buffer");
 
@@ -138,14 +130,12 @@ static void http_parser_execute(
 
   if (!http_parser_has_error(parser))
     parser->cs = cs;
-  parser->nread += p - (buffer + off);
+  parser->start.offset = p - buffer;
 
   assert(p <= pe && "buffer overflow after parsing execute");
-  assert(parser->nread <= len && "nread longer than length");
-  assert(parser->body_start <= len && "body starts after buffer end");
+  assert(parser->start.offset <= len && "start.offset longer than length");
   assert(parser->mark < len && "mark is after buffer end");
   assert(parser->field_len <= len && "field has length longer than whole buffer");
-  assert(parser->field_start < len && "field starts after buffer end");
 }
 
 static int http_parser_has_error(http_parser *parser) {