From c9f80eebc4ec8f717f667970d4c2763f96283ebd Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Tue, 18 Aug 2009 23:45:04 -0700 Subject: http: support for multi-line HTTP headers 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. --- ext/unicorn_http/unicorn_http.rl | 41 ++++++++++++++++++++++++++++++--- ext/unicorn_http/unicorn_http_common.rl | 5 +++- 2 files changed, 42 insertions(+), 4 deletions(-) (limited to 'ext') 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)? )*; -- cgit v1.2.3-24-ge0c7