diff --git a/requests/models.py b/requests/models.py index 54428882..fe4bec1b 100644 --- a/requests/models.py +++ b/requests/models.py @@ -465,9 +465,11 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): def prepare_content_length(self, body): if hasattr(body, 'seek') and hasattr(body, 'tell'): + curr_pos = body.tell() body.seek(0, 2) - self.headers['Content-Length'] = builtin_str(body.tell()) - body.seek(0, 0) + end_pos = body.tell() + self.headers['Content-Length'] = builtin_str(max(0, end_pos - curr_pos)) + body.seek(curr_pos, 0) elif body is not None: l = super_len(body) if l: diff --git a/tests/test_requests.py b/tests/test_requests.py index 282a6679..04a27a44 100755 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -482,6 +482,48 @@ class TestRequests: with pytest.raises(ValueError): requests.post(url, files=['bad file data']) + def test_POSTBIN_SEEKED_OBJECT_WITH_NO_ITER(self, httpbin): + + class TestStream(object): + def __init__(self, data): + self.data = data.encode() + self.length = len(self.data) + self.index = 0 + + def __len__(self): + return self.length + + def read(self, size=None): + if size: + ret = self.data[self.index:self.index + size] + self.index += size + else: + ret = self.data[self.index:] + self.index = self.length + return ret + + def tell(self): + return self.index + + def seek(self, offset, where=0): + if where == 0: + self.index = offset + elif where == 1: + self.index += offset + elif where == 2: + self.index = self.length + offset + + test = TestStream('test') + post1 = requests.post(httpbin('post'), data=test) + assert post1.status_code == 200 + assert post1.json()['data'] == 'test' + + test = TestStream('test') + test.seek(2) + post2 = requests.post(httpbin('post'), data=test) + assert post2.status_code == 200 + assert post2.json()['data'] == 'st' + def test_POSTBIN_GET_POST_FILES_WITH_DATA(self, httpbin): url = httpbin('post')