From c54df54f6633f3467b64fdfc9cbff278d02397ac Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Mon, 26 Apr 2010 15:02:46 -0700 Subject: http: pedantic fix for trailer-less chunked requests HTTP requests without trailers still need a CRLF after the last chunk, that is: it must end as: "0\r\n\r\n", not "0\r\n". So we'll always pretend there are trailers to parse for the sake of TeeInput. This is mostly a pedantic fix, as the two bytes in the socket buffer are unlikely to trigger protocol errors. --- ext/unicorn_http/unicorn_http.rl | 8 ++------ test/unit/test_http_parser_ng.rb | 4 ++++ test/unit/test_tee_input.rb | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl index f0b602b..264db68 100644 --- a/ext/unicorn_http/unicorn_http.rl +++ b/ext/unicorn_http/unicorn_http.rl @@ -299,12 +299,8 @@ static void write_value(VALUE req, struct http_parser *hp, } action end_chunked_body { - if (HP_FL_TEST(hp, HASTRAILER)) { - HP_FL_SET(hp, INTRAILER); - cs = http_parser_en_Trailers; - } else { - cs = http_parser_first_final; - } + HP_FL_SET(hp, INTRAILER); + cs = http_parser_en_Trailers; ++p; assert(p <= pe && "buffer overflow after chunked body"); goto post_exec; diff --git a/test/unit/test_http_parser_ng.rb b/test/unit/test_http_parser_ng.rb index 3b9111f..7267ea0 100644 --- a/test/unit/test_http_parser_ng.rb +++ b/test/unit/test_http_parser_ng.rb @@ -172,6 +172,10 @@ class HttpParserNgTest < Test::Unit::TestCase assert_equal '', str assert ! @parser.body_eof? assert_equal "", @parser.filter_body(tmp, "\r\n0\r\n") + assert_equal "", tmp + assert @parser.body_eof? + assert_equal req, @parser.trailers(req, moo = "\r\n") + assert_equal "", moo assert @parser.body_eof? assert ! @parser.keepalive? end diff --git a/test/unit/test_tee_input.rb b/test/unit/test_tee_input.rb index ee81a87..a127882 100644 --- a/test/unit/test_tee_input.rb +++ b/test/unit/test_tee_input.rb @@ -160,7 +160,7 @@ class TestTeeInput < Test::Unit::TestCase pid = fork { @rd.close 5.times { @wr.write("5\r\nabcde\r\n") } - @wr.write("0\r\n") + @wr.write("0\r\n\r\n") } @wr.close ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf) @@ -199,7 +199,7 @@ class TestTeeInput < Test::Unit::TestCase rd.read(1) == "." and @wr.write("#{'%x' % [ chunk.size]}\r\n#{chunk}\r\n") end - @wr.write("0\r\n") + @wr.write("0\r\n\r\n") } ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf) assert_nil @parser.content_length @@ -213,6 +213,34 @@ class TestTeeInput < Test::Unit::TestCase assert status.success? end + def test_chunked_with_trailer + @parser = Unicorn::HttpParser.new + @buf = "POST / HTTP/1.1\r\n" \ + "Host: localhost\r\n" \ + "Trailer: Hello\r\n" \ + "Transfer-Encoding: chunked\r\n" \ + "\r\n" + assert_equal @env, @parser.headers(@env, @buf) + assert_equal "", @buf + + pid = fork { + @rd.close + 5.times { @wr.write("5\r\nabcde\r\n") } + @wr.write("0\r\n") + @wr.write("Hello: World\r\n\r\n") + } + @wr.close + ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf) + assert_nil @parser.content_length + assert_nil ti.len + assert ! @parser.body_eof? + assert_equal 25, ti.size + assert_equal "World", @env['HTTP_HELLO'] + status = nil + assert_nothing_raised { pid, status = Process.waitpid2(pid) } + assert status.success? + end + private def init_parser(body, size = nil) -- cgit v1.2.3-24-ge0c7