about summary refs log tree commit homepage
path: root/ext
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-08-18 23:45:04 -0700
committerEric Wong <normalperson@yhbt.net>2009-08-18 23:45:04 -0700
commitc9f80eebc4ec8f717f667970d4c2763f96283ebd (patch)
treeff498218314d8825f71b807563083422258f349d /ext
parent79bba4abefb57f12b54ddd338ed8f9ec828b5e89 (diff)
downloadunicorn-c9f80eebc4ec8f717f667970d4c2763f96283ebd.tar.gz
While I still consider pound to be irrelevant, but I still
sometimes get hand-crafted HTTP requests that come in with
multiline headers.  Since these are part of the HTTP specs and
not difficult to support, we might as well support them for
the sake of completeness.
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)? )*;