summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBert JW Regeer <xistence@0x58.com>2019-04-11 22:02:42 +0100
committerGitHub <noreply@github.com>2019-04-11 22:02:42 +0100
commitbf008d7c107c2f9f03fc088bd3af4fa94ad9ad10 (patch)
treea824289727edf52eba71148a8ccfaea0c68045d7
parent5583715063c30fda571ab0e0169b0068403dc53d (diff)
parent17c1987efc5e0f5d3052e913561fb16e47e08a2b (diff)
downloadwaitress-bf008d7c107c2f9f03fc088bd3af4fa94ad9ad10.tar.gz
Merge pull request #249 from Pylons/iobase
support iobase subclasses
-rw-r--r--CHANGES.txt4
-rw-r--r--waitress/buffers.py7
-rw-r--r--waitress/tests/fixtureapps/filewrapper.py24
-rw-r--r--waitress/tests/test_functional.py18
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)