about summary refs log tree commit homepage
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/unicorn_http/unicorn_http.rl41
-rw-r--r--ext/unicorn_http/unicorn_http_common.rl5
2 files changed, 42 insertions, 4 deletions
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index ed9c359..2d829e9 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -36,6 +36,7 @@ struct http_parser {
     size_t field_len; /* only used during header processing */
     size_t dest_offset; /* only used during body processing */
   } s;
+  VALUE cont;
   union {
     off_t content;
     off_t chunk;
@@ -87,6 +88,31 @@ static void invalid_if_trailer(int flags)
     rb_raise(eHttpParserError, "invalid Trailer");
 }
 
+static void write_cont_value(struct http_parser *hp,
+                             const char *buffer, const char *p)
+{
+  char *vptr;
+
+  if (!hp->cont)
+    rb_raise(eHttpParserError, "invalid continuation line");
+
+  assert(hp->mark > 0);
+
+  if (LEN(mark, p) == 0)
+    return;
+
+  if (RSTRING_LEN(hp->cont) > 0)
+    --hp->mark;
+
+  vptr = PTR_TO(mark);
+
+  if (RSTRING_LEN(hp->cont) > 0) {
+    assert(' ' == *vptr || '\t' == *vptr);
+    *vptr = ' ';
+  }
+  rb_str_buf_cat(hp->cont, vptr, LEN(mark, p));
+}
+
 static void write_value(VALUE req, struct http_parser *hp,
                         const char *buffer, const char *p)
 {
@@ -123,11 +149,11 @@ static void write_value(VALUE req, struct http_parser *hp,
 
   e = rb_hash_aref(req, f);
   if (e == Qnil) {
-    rb_hash_aset(req, f, v);
+    hp->cont = rb_hash_aset(req, f, v);
   } else if (f != g_http_host) {
     /* full URLs in REQUEST_URI take precedence for the Host: header */
     rb_str_buf_cat(e, ",", 1);
-    rb_str_buf_append(e, v);
+    hp->cont = rb_str_buf_append(e, v);
   }
 }
 
@@ -144,6 +170,7 @@ static void write_value(VALUE req, struct http_parser *hp,
   action write_field { hp->s.field_len = LEN(start.field, fpc); }
   action start_value { MARK(mark, fpc); }
   action write_value { write_value(req, hp, buffer, fpc); }
+  action write_cont_value { write_cont_value(hp, buffer, fpc); }
   action request_method {
     request_method(hp, req, PTR_TO(mark), LEN(mark, fpc));
   }
@@ -342,10 +369,18 @@ static void finalize_header(VALUE req)
     rb_hash_aset(req, g_query_string, rb_str_new(NULL, 0));
 }
 
+static void hp_mark(void *ptr)
+{
+  struct http_parser *hp = ptr;
+
+  if (hp->cont)
+    rb_gc_mark(hp->cont);
+}
+
 static VALUE HttpParser_alloc(VALUE klass)
 {
   struct http_parser *hp;
-  return Data_Make_Struct(klass, struct http_parser, NULL, NULL, hp);
+  return Data_Make_Struct(klass, struct http_parser, hp_mark, NULL, hp);
 }
 
 
diff --git a/ext/unicorn_http/unicorn_http_common.rl b/ext/unicorn_http/unicorn_http_common.rl
index f1ed138..9b51ba1 100644
--- a/ext/unicorn_http/unicorn_http_common.rl
+++ b/ext/unicorn_http/unicorn_http_common.rl
@@ -19,6 +19,7 @@
   uchar = (unreserved | escape | sorta_safe);
   pchar = (uchar | ":" | "@" | "&" | "=" | "+");
   tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
+  lws = (" " | "\t");
 
 # elements
   token = (ascii -- (CTL | tspecials));
@@ -49,7 +50,9 @@
 
   field_value = any* >start_value %write_value;
 
-  message_header = field_name ":" " "* field_value :> CRLF;
+  value_cont = lws+ any* >start_value %write_cont_value;
+
+  message_header = ((field_name ":" " "* field_value)|value_cont) :> CRLF;
   chunk_ext_val = token*;
   chunk_ext_name = token*;
   chunk_extension = ( ";" " "* chunk_ext_name ("=" chunk_ext_val)? )*;