Fixes #27
Preserve size in chunk buffer until CRLF is received avoiding error in cases when received data contains size without a following CRLF.
This commit is contained in:
parent
5c1fac2ffa
commit
4fde72fd10
21
proxy.py
21
proxy.py
|
@ -79,17 +79,27 @@ class ChunkParser(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.state = CHUNK_PARSER_STATE_WAITING_FOR_SIZE
|
self.state = CHUNK_PARSER_STATE_WAITING_FOR_SIZE
|
||||||
self.body = b''
|
self.body = b'' # Parsed chunks
|
||||||
self.chunk = b''
|
self.chunk = b'' # Partial chunk received
|
||||||
self.size = None
|
self.size = None # Expected size of next following chunk
|
||||||
|
|
||||||
def parse(self, data):
|
def parse(self, data):
|
||||||
more = True if len(data) > 0 else False
|
more = True if len(data) > 0 else False
|
||||||
while more: more, data = self.process(data)
|
while more:
|
||||||
|
more, data = self.process(data)
|
||||||
|
|
||||||
def process(self, data):
|
def process(self, data):
|
||||||
if self.state == CHUNK_PARSER_STATE_WAITING_FOR_SIZE:
|
if self.state == CHUNK_PARSER_STATE_WAITING_FOR_SIZE:
|
||||||
|
# Consume prior chunk in buffer
|
||||||
|
# in case chunk size without CRLF was received
|
||||||
|
data = self.chunk + data
|
||||||
|
self.chunk = b''
|
||||||
|
# Extract following chunk data size
|
||||||
line, data = HttpParser.split(data)
|
line, data = HttpParser.split(data)
|
||||||
|
if not line: # CRLF not received
|
||||||
|
self.chunk = data
|
||||||
|
data = b''
|
||||||
|
else:
|
||||||
self.size = int(line, 16)
|
self.size = int(line, 16)
|
||||||
self.state = CHUNK_PARSER_STATE_WAITING_FOR_DATA
|
self.state = CHUNK_PARSER_STATE_WAITING_FOR_DATA
|
||||||
elif self.state == CHUNK_PARSER_STATE_WAITING_FOR_DATA:
|
elif self.state == CHUNK_PARSER_STATE_WAITING_FOR_DATA:
|
||||||
|
@ -388,8 +398,7 @@ class Proxy(threading.Thread):
|
||||||
elif self.request.url:
|
elif self.request.url:
|
||||||
host, port = self.request.url.hostname, self.request.url.port if self.request.url.port else 80
|
host, port = self.request.url.hostname, self.request.url.port if self.request.url.port else 80
|
||||||
else:
|
else:
|
||||||
# TODO(abhinavsingh): Gracefully return invalid request in such cases
|
raise Exception('Invalid request\n%s' % self.request.raw)
|
||||||
pass
|
|
||||||
|
|
||||||
self.server = Server(host, port)
|
self.server = Server(host, port)
|
||||||
try:
|
try:
|
||||||
|
|
37
tests.py
37
tests.py
|
@ -24,6 +24,43 @@ class TestChunkParser(unittest.TestCase):
|
||||||
self.assertEqual(self.parser.body, b'Wikipedia in\r\n\r\nchunks.')
|
self.assertEqual(self.parser.body, b'Wikipedia in\r\n\r\nchunks.')
|
||||||
self.assertEqual(self.parser.state, CHUNK_PARSER_STATE_COMPLETE)
|
self.assertEqual(self.parser.state, CHUNK_PARSER_STATE_COMPLETE)
|
||||||
|
|
||||||
|
def test_chunk_parse_issue_27(self):
|
||||||
|
self.parser.parse(b'3')
|
||||||
|
self.assertEqual(self.parser.chunk, b'3')
|
||||||
|
self.assertEqual(self.parser.size, None)
|
||||||
|
self.assertEqual(self.parser.body, b'')
|
||||||
|
self.assertEqual(self.parser.state, CHUNK_PARSER_STATE_WAITING_FOR_SIZE)
|
||||||
|
self.parser.parse(b'\r\n')
|
||||||
|
self.assertEqual(self.parser.chunk, b'')
|
||||||
|
self.assertEqual(self.parser.size, 3)
|
||||||
|
self.assertEqual(self.parser.body, b'')
|
||||||
|
self.assertEqual(self.parser.state, CHUNK_PARSER_STATE_WAITING_FOR_DATA)
|
||||||
|
self.parser.parse(b'abc')
|
||||||
|
self.assertEqual(self.parser.chunk, b'')
|
||||||
|
self.assertEqual(self.parser.size, None)
|
||||||
|
self.assertEqual(self.parser.body, b'abc')
|
||||||
|
self.assertEqual(self.parser.state, CHUNK_PARSER_STATE_WAITING_FOR_SIZE)
|
||||||
|
self.parser.parse(b'\r\n')
|
||||||
|
self.assertEqual(self.parser.chunk, b'')
|
||||||
|
self.assertEqual(self.parser.size, None)
|
||||||
|
self.assertEqual(self.parser.body, b'abc')
|
||||||
|
self.assertEqual(self.parser.state, CHUNK_PARSER_STATE_WAITING_FOR_SIZE)
|
||||||
|
self.parser.parse(b'4\r\n')
|
||||||
|
self.assertEqual(self.parser.chunk, b'')
|
||||||
|
self.assertEqual(self.parser.size, 4)
|
||||||
|
self.assertEqual(self.parser.body, b'abc')
|
||||||
|
self.assertEqual(self.parser.state, CHUNK_PARSER_STATE_WAITING_FOR_DATA)
|
||||||
|
self.parser.parse(b'defg\r\n0')
|
||||||
|
self.assertEqual(self.parser.chunk, b'0')
|
||||||
|
self.assertEqual(self.parser.size, None)
|
||||||
|
self.assertEqual(self.parser.body, b'abcdefg')
|
||||||
|
self.assertEqual(self.parser.state, CHUNK_PARSER_STATE_WAITING_FOR_SIZE)
|
||||||
|
self.parser.parse(b'\r\n\r\n')
|
||||||
|
self.assertEqual(self.parser.chunk, b'')
|
||||||
|
self.assertEqual(self.parser.size, None)
|
||||||
|
self.assertEqual(self.parser.body, b'abcdefg')
|
||||||
|
self.assertEqual(self.parser.state, CHUNK_PARSER_STATE_COMPLETE)
|
||||||
|
|
||||||
|
|
||||||
class TestHttpParser(unittest.TestCase):
|
class TestHttpParser(unittest.TestCase):
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue