From 61a43460e3b9f8bef85b2cd9bf9b5348667d50f9 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Sun, 26 Nov 2017 17:41:29 +0100 Subject: [PATCH 1/3] Add fragmented request unittest --- tests/test_parser.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/test_parser.py b/tests/test_parser.py index 2794372..aca0182 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -419,6 +419,41 @@ def test_parser_request_4(self): with self.assertRaisesRegex(TypeError, 'a bytes-like object'): p.feed_data('POST HTTP/1.2') + def test_parser_request_fragmented(self): + m = mock.Mock() + headers = {} + m.on_header.side_effect = headers.__setitem__ + p = httptools.HttpRequestParser(m) + + REQUEST = ( + b'PUT / HTTP/1.1\r\nHost: localhost:1234\r\nContent-Type: text/pl', + b'ain; charset=utf-8\r\nX-Empty-Header: \r\nConnection: close\r\n', + b'Content-Length: 10\r\n\r\n1234567890', + ) + + p.feed_data(REQUEST[0]) + + m.on_message_begin.assert_called_once_with() + m.on_url.assert_called_once_with(b'/') + self.assertEqual(headers, {b'Host': b'localhost:1234'}) + + p.feed_data(REQUEST[1]) + self.assertEqual( + headers, + {b'Host': b'localhost:1234', + b'Content-Type': b'text/plain; charset=utf-8', + b'X-Empty-Header': b''}) + + p.feed_data(REQUEST[2]) + self.assertEqual( + headers, + {b'Host': b'localhost:1234', + b'Content-Type': b'text/plain; charset=utf-8', + b'X-Empty-Header': b'', + b'Connection': b'close', + b'Content-Length': b'10'}) + m.on_message_complete.assert_called_once_with() + class TestUrlParser(unittest.TestCase): From af31207f66c584502024e9dcf23a20d78a58d78a Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Tue, 28 Nov 2017 01:37:36 +0900 Subject: [PATCH 2/3] Add more fragmented header tests --- tests/test_parser.py | 69 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tests/test_parser.py b/tests/test_parser.py index aca0182..ff9a9de 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -454,6 +454,75 @@ def test_parser_request_fragmented(self): b'Content-Length': b'10'}) m.on_message_complete.assert_called_once_with() + def test_parser_request_fragmented_header(self): + m = mock.Mock() + headers = {} + m.on_header.side_effect = headers.__setitem__ + p = httptools.HttpRequestParser(m) + + REQUEST = ( + b'PUT / HTTP/1.1\r\nHost: localhost:1234\r\nContent-', + b'Type: text/plain; charset=utf-8\r\n\r\n', + ) + + p.feed_data(REQUEST[0]) + + m.on_message_begin.assert_called_once_with() + m.on_url.assert_called_once_with(b'/') + self.assertEqual(headers, {b'Host': b'localhost:1234'}) + + p.feed_data(REQUEST[1]) + self.assertEqual( + headers, + {b'Host': b'localhost:1234', + b'Content-Type': b'text/plain; charset=utf-8'}) + + def test_parser_request_fragmented_value(self): + m = mock.Mock() + headers = {} + m.on_header.side_effect = headers.__setitem__ + p = httptools.HttpRequestParser(m) + + REQUEST = ( + b'PUT / HTTP/1.1\r\nHost: localhost:1234\r\nContent-Type:', + b' text/pla', + b'in; chars', + b'et=utf-8\r\n\r\n', + ) + + p.feed_data(REQUEST[0]) + + m.on_message_begin.assert_called_once_with() + m.on_url.assert_called_once_with(b'/') + self.assertEqual(headers, {b'Host': b'localhost:1234'}) + + p.feed_data(REQUEST[1]) + p.feed_data(REQUEST[2]) + p.feed_data(REQUEST[3]) + self.assertEqual( + headers, + {b'Host': b'localhost:1234', + b'Content-Type': b'text/plain; charset=utf-8'}) + + def test_parser_request_fragmented_bytes(self): + m = mock.Mock() + headers = {} + m.on_header.side_effect = headers.__setitem__ + p = httptools.HttpRequestParser(m) + + REQUEST = \ + b'PUT / HTTP/1.1\r\nHost: localhost:1234\r\nContent-' \ + b'Type: text/plain; charset=utf-8\r\n\r\n' + + step = 1 + for i in range(0, len(REQUEST), step): + p.feed_data(REQUEST[i:i+step]) + + self.assertEqual( + headers, + {b'Host': b'localhost:1234', + b'Content-Type': b'text/plain; charset=utf-8'}) + class TestUrlParser(unittest.TestCase): From 58dc648ce679badd82bf59da130025552d640f9d Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Tue, 28 Nov 2017 01:37:48 +0900 Subject: [PATCH 3/3] Fix header fragmentation parsing --- httptools/parser/parser.pyx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/httptools/parser/parser.pyx b/httptools/parser/parser.pyx index 064e55c..0b9c945 100644 --- a/httptools/parser/parser.pyx +++ b/httptools/parser/parser.pyx @@ -95,7 +95,7 @@ cdef class HttpParser: self._last_error = None cdef _maybe_call_on_header(self): - if self._current_header_name is not None: + if self._current_header_value is not None: current_header_name = self._current_header_name current_header_value = self._current_header_value @@ -107,7 +107,10 @@ cdef class HttpParser: cdef _on_header_field(self, bytes field): self._maybe_call_on_header() - self._current_header_name = field + if self._current_header_name is None: + self._current_header_name = field + else: + self._current_header_name += field cdef _on_header_value(self, bytes val): if self._current_header_value is None: