Commits

Sergey Schetinin committed e25aa9d

Add req.is_body_readable / env['webob.is_body_readable'] flag and only read wsgi.input if either CONTENT_LENGTH is present or that flag is set.
This partially reverts the change made in 1.0.2 in how the missing CONTENT_LENGTH is interpreted -- in 1.0.1 and before the body would be assumed to be empty, in 1.0.2-1.0.5 webob would attempt to read it.
This restores compatibility with some servers (CherryPy 3.2, wsgiref and possibly more).
Note that with this new flag we can support FakeCGIBody and chunked input streams without the env['CONTENT_LENGTH'] = '-1' hack.
For related discussion see https://bitbucket.org/ianb/webob/issue/6

Comments (0)

Files changed (2)

tests/test_request_nose.py

+from webob import Request
+
+
+def test_request_read_no_content_length():
+    req, input = _make_read_tracked_request('abc')
+    assert req.content_length is None
+    assert req.body == ''
+    assert not input.was_read
+
+def test_request_read_no_flag_but_content_length_is_present():
+    req, input = _make_read_tracked_request('abc')
+    req.content_length = 3
+    assert req.body == 'abc'
+    assert input.was_read
+
+def test_request_read_no_content_length_but_flagged_readable():
+    req, input = _make_read_tracked_request('abc')
+    req.is_body_readable = True
+    assert req.body == 'abc'
+    assert input.was_read
+
+def test_request_read_after_setting_body_file():
+    req = _make_read_tracked_request()[0]
+    input = req.body_file = ReadTracker('abc')
+    assert req.content_length is None
+    assert not req.is_body_seekable
+    assert req.body == 'abc'
+    # reading body made the input seekable and set the clen
+    assert req.content_length == 3
+    assert req.is_body_seekable
+    assert input.was_read
+
+
+def _make_read_tracked_request(data=''):
+    input = ReadTracker(data)
+    env = {
+        'HTTP_METHOD': 'PUT',
+        'wsgi.input': input,
+    }
+    return Request(env), input
+
+class ReadTracker(object):
+    """
+        Helper object to determine if the input was read or not
+    """
+    def __init__(self, data):
+        self.data = data
+        self.was_read = False
+    def read(self, size=-1):
+        if size < 0:
+            size = len(self.data)
+        assert size == len(self.data)
+        self.was_read = True
+        return self.data
             self.content_length = None
             self.body_file_raw = value
             self.is_body_seekable = False
+            self.is_body_readable = True
     def _body_file__del(self):
         self.body = ''
     body_file = property(_body_file__get,
         env = self.environ.copy()
         return self.__class__(env, method='GET', content_type=None, body='')
 
+    # webob.is_body_seekalbe marks input streams that are seekable
+    # this way we can have seekable input without testing the .seek() method
     is_body_seekable = environ_getter('webob.is_body_seekable', False)
 
+    # webob.is_body_readable is a flag that tells us
+    # that we can read the input stream even though
+    # CONTENT_LENGTH is missing. this allows FakeCGIBody
+    # to work and can be used by servers to support
+    # chunked encoding in requests.
+    # for background see https://bitbucket.org/ianb/webob/issue/6
+    is_body_readable = environ_getter('webob.is_body_readable', False)
+
+
     def make_body_seekable(self):
         """
         This forces ``environ['wsgi.input']`` to be seekable.
             did_copy = self._copy_body_tempfile()
             if not did_copy:
                 self.body = self.body_file_raw.read(length)
-        else:
+        elif self.is_body_readable:
             self.body = self.body_file_raw.read()
             self._copy_body_tempfile()
+        else:
+            self.body = ''
 
     def _copy_body_tempfile(self):
         """