From 7319f0acecefe35e212a195c384033c2dcc12556 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Fri, 11 Sep 2015 01:49:22 +0300 Subject: [PATCH 1/2] Correctly process data=io.BytesIO(...) --- aiohttp/client_reqrep.py | 16 +++++++++++++--- tests/test_client_functional2.py | 23 +++++++++++++++++++++++ tests/test_client_request.py | 3 ++- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 652a2cf8fa9..76b630cfea9 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -287,13 +287,23 @@ def update_body_from_data(self, data): assert not isinstance(data, io.StringIO), \ 'attempt to send text data instead of binary' self.body = data - if not self.chunked and isinstance(data, io.BufferedReader): - # Not chunking if content-length can be determined - size = os.fstat(data.fileno()).st_size - data.tell() + if not self.chunked and isinstance(data, io.BytesIO): + size = len(data.getbuffer()) self.headers[hdrs.CONTENT_LENGTH] = str(size) self.chunked = False + elif not self.chunked and isinstance(data, io.BufferedReader): + # Not chunking if content-length can be determined + try: + size = os.fstat(data.fileno()).st_size - data.tell() + self.headers[hdrs.CONTENT_LENGTH] = str(size) + self.chunked = False + except OSError: + # data.fileno() is not supported, e.g. + # io.BufferedReader(io.BytesIO(b'data')) + self.chunked = True else: self.chunked = True + if hasattr(data, 'mode'): if data.mode == 'r': raise ValueError('file {!r} should be open in binary mode' diff --git a/tests/test_client_functional2.py b/tests/test_client_functional2.py index b2c53e2cb58..c4ac3c46547 100644 --- a/tests/test_client_functional2.py +++ b/tests/test_client_functional2.py @@ -1,4 +1,5 @@ import asyncio +import io import socket import unittest @@ -114,3 +115,25 @@ def go(): yield from resp.release() self.loop.run_until_complete(go()) + + def test_raw_post_data(self): + data = b'some buffer' + + @asyncio.coroutine + def handler(request): + self.assertEqual(len(data), request.content_length) + val = yield from request.read() + self.assertEqual(data, val) + return web.Response() + + @asyncio.coroutine + def go(): + app, srv, url = yield from self.create_server() + app.router.add_route('post', '/', handler) + resp = yield from self.client.post( + url+'/', + data=io.BytesIO(data)) + self.assertEqual(200, resp.status) + yield from resp.release() + + self.loop.run_until_complete(go()) diff --git a/tests/test_client_request.py b/tests/test_client_request.py index fa7469b3b6a..31e1e7a1111 100644 --- a/tests/test_client_request.py +++ b/tests/test_client_request.py @@ -559,7 +559,8 @@ def gen(): def test_data_file(self): req = ClientRequest( - 'POST', 'http://python.org/', data=io.BytesIO(b'*' * 2), + 'POST', 'http://python.org/', + data=io.BufferedReader(io.BytesIO(b'*' * 2)), loop=self.loop) self.assertTrue(req.chunked) self.assertTrue(isinstance(req.body, io.IOBase)) From d15c1c13fe43d8e7692f099da9563adc315ed1c7 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Fri, 11 Sep 2015 02:02:00 +0300 Subject: [PATCH 2/2] Add test for BytesIO in multipart request --- aiohttp/client_reqrep.py | 1 + tests/test_client_functional2.py | 24 +++++++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py index 76b630cfea9..f3d2248965b 100644 --- a/aiohttp/client_reqrep.py +++ b/aiohttp/client_reqrep.py @@ -288,6 +288,7 @@ def update_body_from_data(self, data): 'attempt to send text data instead of binary' self.body = data if not self.chunked and isinstance(data, io.BytesIO): + # Not chunking if content-length can be determined size = len(data.getbuffer()) self.headers[hdrs.CONTENT_LENGTH] = str(size) self.chunked = False diff --git a/tests/test_client_functional2.py b/tests/test_client_functional2.py index c4ac3c46547..bcf86e2b805 100644 --- a/tests/test_client_functional2.py +++ b/tests/test_client_functional2.py @@ -116,7 +116,7 @@ def go(): self.loop.run_until_complete(go()) - def test_raw_post_data(self): + def test_post_data_bytesio(self): data = b'some buffer' @asyncio.coroutine @@ -137,3 +137,25 @@ def go(): yield from resp.release() self.loop.run_until_complete(go()) + + def test_post_data_with_bytesio_file(self): + data = b'some buffer' + + @asyncio.coroutine + def handler(request): + post_data = yield from request.post() + self.assertEqual(['file'], list(post_data.keys())) + self.assertEqual(data, post_data['file'].file.read()) + return web.Response() + + @asyncio.coroutine + def go(): + app, srv, url = yield from self.create_server() + app.router.add_route('post', '/', handler) + resp = yield from self.client.post( + url+'/', + data={'file': io.BytesIO(data)}) + self.assertEqual(200, resp.status) + yield from resp.release() + + self.loop.run_until_complete(go())