diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index 64de9de..4b5f38a 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -50,6 +50,10 @@ BAD_SOCK = set((errno.EBADF, 10053)) BROKEN_SOCK = set((errno.EPIPE, errno.ECONNRESET)) +class ChunkReadError(ValueError): + pass + + # special flag return value for apps class _AlreadyHandled(object): @@ -176,7 +180,10 @@ class Input(object): if use_readline and data[-1] == "\n": break else: - self.chunk_length = int(rfile.readline().split(b";", 1)[0], 16) + try: + self.chunk_length = int(rfile.readline().split(b";", 1)[0], 16) + except ValueError as err: + raise ChunkReadError(err) self.position = 0 if self.chunk_length == 0: rfile.readline() diff --git a/tests/wsgi_test.py b/tests/wsgi_test.py index a37f3a1..4d67ede 100644 --- a/tests/wsgi_test.py +++ b/tests/wsgi_test.py @@ -1319,6 +1319,70 @@ class TestHttpd(_TestBase): self.assertEqual(read_content.wait(), b'ok') assert blew_up[0] + def test_aborted_chunked_post_between_chunks(self): + read_content = event.Event() + blew_up = [False] + + def chunk_reader(env, start_response): + try: + content = env['wsgi.input'].read(1024) + except wsgi.ChunkReadError: + blew_up[0] = True + content = b'ok' + except Exception as err: + blew_up[0] = True + content = b'wrong exception: ' + str(err).encode() + read_content.send(content) + start_response('200 OK', [('Content-Type', 'text/plain')]) + return [content] + self.site.application = chunk_reader + expected_body = 'A' * 0xdb + data = "\r\n".join(['PUT /somefile HTTP/1.0', + 'Transfer-Encoding: chunked', + '', + 'db', + expected_body]) + # start PUT-ing some chunked data but close prematurely + sock = eventlet.connect(('127.0.0.1', self.port)) + sock.sendall(data.encode()) + sock.close() + # the test passes if we successfully get here, and read all the data + # in spite of the early close + self.assertEqual(read_content.wait(), b'ok') + assert blew_up[0] + + def test_aborted_chunked_post_bad_chunks(self): + read_content = event.Event() + blew_up = [False] + + def chunk_reader(env, start_response): + try: + content = env['wsgi.input'].read(1024) + except wsgi.ChunkReadError: + blew_up[0] = True + content = b'ok' + except Exception as err: + blew_up[0] = True + content = b'wrong exception: ' + str(err).encode() + read_content.send(content) + start_response('200 OK', [('Content-Type', 'text/plain')]) + return [content] + self.site.application = chunk_reader + expected_body = 'look here is some data for you' + data = "\r\n".join(['PUT /somefile HTTP/1.0', + 'Transfer-Encoding: chunked', + '', + 'cats', + expected_body]) + # start PUT-ing some garbage + sock = eventlet.connect(('127.0.0.1', self.port)) + sock.sendall(data.encode()) + sock.close() + # the test passes if we successfully get here, and read all the data + # in spite of the early close + self.assertEqual(read_content.wait(), b'ok') + assert blew_up[0] + def test_exceptions_close_connection(self): def wsgi_app(environ, start_response): raise RuntimeError("intentional error")