diff options
author | Bert JW Regeer <xistence@0x58.com> | 2019-04-11 22:02:42 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-11 22:02:42 +0100 |
commit | bf008d7c107c2f9f03fc088bd3af4fa94ad9ad10 (patch) | |
tree | a824289727edf52eba71148a8ccfaea0c68045d7 | |
parent | 5583715063c30fda571ab0e0169b0068403dc53d (diff) | |
parent | 17c1987efc5e0f5d3052e913561fb16e47e08a2b (diff) | |
download | waitress-bf008d7c107c2f9f03fc088bd3af4fa94ad9ad10.tar.gz |
Merge pull request #249 from Pylons/iobase
support iobase subclasses
-rw-r--r-- | CHANGES.txt | 4 | ||||
-rw-r--r-- | waitress/buffers.py | 7 | ||||
-rw-r--r-- | waitress/tests/fixtureapps/filewrapper.py | 24 | ||||
-rw-r--r-- | waitress/tests/test_functional.py | 18 |
4 files changed, 52 insertions, 1 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 2897939..cabd3b5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -54,6 +54,10 @@ Bugfixes bounds. These buffers now properly rotate and release their data. See https://github.com/Pylons/waitress/pull/242 +- Fix a bug in which non-seekable subclasses of ``io.IOBase`` would trigger + an exception when passed to the ``wsgi.file_wrapper`` callback. + See https://github.com/Pylons/waitress/pull/249 + 1.2.1 (2019-01-25) ------------------ diff --git a/waitress/buffers.py b/waitress/buffers.py index cacc094..aa11b70 100644 --- a/waitress/buffers.py +++ b/waitress/buffers.py @@ -129,6 +129,11 @@ class BytesIOBasedBuffer(FileBasedBuffer): def newfile(self): return BytesIO() +def _is_seekable(fp): + if hasattr(fp, 'seekable'): + return fp.seekable() + return hasattr(fp, 'seek') and hasattr(fp, 'tell') + class ReadOnlyFileBasedBuffer(FileBasedBuffer): # used as wsgi.file_wrapper @@ -137,7 +142,7 @@ class ReadOnlyFileBasedBuffer(FileBasedBuffer): self.block_size = block_size # for __iter__ def prepare(self, size=None): - if hasattr(self.file, 'seek') and hasattr(self.file, 'tell'): + if _is_seekable(self.file): start_pos = self.file.tell() self.file.seek(0, 2) end_pos = self.file.tell() diff --git a/waitress/tests/fixtureapps/filewrapper.py b/waitress/tests/fixtureapps/filewrapper.py index be35b02..d4e9a51 100644 --- a/waitress/tests/fixtureapps/filewrapper.py +++ b/waitress/tests/fixtureapps/filewrapper.py @@ -1,3 +1,4 @@ +import io import os here = os.path.dirname(os.path.abspath(__file__)) @@ -13,6 +14,23 @@ class KindaFilelike(object): # pragma: no cover self.bytes = self.bytes[n:] return bytes +class UnseekableIOBase(io.RawIOBase): # pragma: no cover + + def __init__(self, bytes): + self.buf = io.BytesIO(bytes) + + def writable(self): + return False + + def readable(self): + return True + + def seekable(self): + return False + + def read(self, n): + return self.buf.read(n) + def app(environ, start_response): # pragma: no cover path_info = environ['PATH_INFO'] if path_info.startswith('/filelike'): @@ -48,6 +66,12 @@ def app(environ, start_response): # pragma: no cover ('Content-Length', str(len(data))), ('Content-Type', 'image/jpeg'), ] + elif path_info == '/notfilelike_iobase': + headers = [ + ('Content-Length', str(len(data))), + ('Content-Type', 'image/jpeg'), + ] + f = UnseekableIOBase(data) elif path_info == '/notfilelike_nocl': headers = [('Content-Type', 'image/jpeg')] elif path_info == '/notfilelike_shortcl': diff --git a/waitress/tests/test_functional.py b/waitress/tests/test_functional.py index 0571ce6..25414ef 100644 --- a/waitress/tests/test_functional.py +++ b/waitress/tests/test_functional.py @@ -1223,6 +1223,24 @@ class FileWrapperTests(object): self.assertTrue(b'\377\330\377' in response_body) # connection has not been closed + def test_notfilelike_iobase_http11(self): + to_send = "GET /notfilelike_iobase HTTP/1.1\n\n" + to_send = tobytes(to_send) + + self.connect() + + for t in range(0, 2): + self.sock.send(to_send) + fp = self.sock.makefile('rb', 0) + line, headers, response_body = read_http(fp) + self.assertline(line, '200', 'OK', 'HTTP/1.1') + cl = int(headers['content-length']) + self.assertEqual(cl, len(response_body)) + ct = headers['content-type'] + self.assertEqual(ct, 'image/jpeg') + self.assertTrue(b'\377\330\377' in response_body) + # connection has not been closed + def test_notfilelike_nocl_http11(self): to_send = "GET /notfilelike_nocl HTTP/1.1\n\n" to_send = tobytes(to_send) |