Include separating ws when folding header values

The support for folding of multi-line header values does not conform
to the specs.  Given a request containing

    Multi-Line-Header: foo<CRLF>
     bar<CRLF>

http-parser will eliminate the whitespace breaking the header value to
yield a header value of "foobar".  This is confirmed by the
LINE_FOLDING_IN_HEADER case in tests.c.

But from rfc2616, section 2.2:

   A CRLF is allowed in the definition of TEXT only as part of a header
   field continuation. It is expected that the folding LWS will be
   replaced with a single SP before interpretation of the TEXT value.

And from draft-ietf-httpbis-p1-messaging-25, section 3.2.4:

   A server that receives an obs-fold in a request message that is not
   within a message/http container MUST either reject the message by
   sending a 400 (Bad Request), preferably with a representation
   explaining that obsolete line folding is unacceptable, or replace
   each received obs-fold with one or more SP octets prior to
   interpreting the field value or forwarding the message downstream.

So in the example above, the header value should be interpreted as
"foo bar", possibly with multiple spaces.  The current http-parser
behaviour of eliminating the LWS altogether clearly deviates from the
specs.

For http-parser itself to confirm exactly would involve significant
changes in order to synthesize replacement SP octets.  Such changes
are unlikely to be worth it to support what is an obscure and
deprecated feature.  But http-parser should at least preserve some
separating whitespace when folding multi-line header values, so that
applications using http-parser can conform to the specs.

This commit is a minimal change to preserve whitespace when folding
lines.  It eliminates the CRLF, but retains any trailing and leading
whitespace in the header value.
make-http-max-header-size-gyp-configurable
David Wragg 11 years ago committed by Fedor Indutny
parent cba704cb2d
commit 5d9c382172

@ -280,6 +280,7 @@ enum state
, s_header_field_start , s_header_field_start
, s_header_field , s_header_field
, s_header_value_discard_ws
, s_header_value_start , s_header_value_start
, s_header_value , s_header_value
, s_header_value_lws , s_header_value_lws
@ -1380,7 +1381,7 @@ size_t http_parser_execute (http_parser *parser,
} }
if (ch == ':') { if (ch == ':') {
parser->state = s_header_value_start; parser->state = s_header_value_discard_ws;
CALLBACK_DATA(header_field); CALLBACK_DATA(header_field);
break; break;
} }
@ -1401,10 +1402,12 @@ size_t http_parser_execute (http_parser *parser,
goto error; goto error;
} }
case s_header_value_start: case s_header_value_discard_ws:
{
if (ch == ' ' || ch == '\t') break; if (ch == ' ' || ch == '\t') break;
/* FALLTHROUGH */
case s_header_value_start:
{
MARK(header_value); MARK(header_value);
parser->state = s_header_value; parser->state = s_header_value;
@ -1593,12 +1596,10 @@ size_t http_parser_execute (http_parser *parser,
if (ch == ' ' || ch == '\t') if (ch == ' ' || ch == '\t')
parser->state = s_header_value_start; parser->state = s_header_value_start;
else else
{
parser->state = s_header_field_start; parser->state = s_header_field_start;
goto reexecute_byte; goto reexecute_byte;
} }
break;
}
case s_headers_almost_done: case s_headers_almost_done:
{ {

@ -619,7 +619,7 @@ const struct message requests[] =
,.request_path= "/" ,.request_path= "/"
,.request_url= "/" ,.request_url= "/"
,.num_headers= 2 ,.num_headers= 2
,.headers= { { "Line1", "abcdefghijklmno qrs" } ,.headers= { { "Line1", "abc\tdef ghi\t\tjkl mno \t \tqrs" }
, { "Line2", "line2\t" } , { "Line2", "line2\t" }
} }
,.body= "" ,.body= ""

Loading…
Cancel
Save