diff --git a/aiohttp/client.py b/aiohttp/client.py index d95cce18e2d..61d84116198 100644 --- a/aiohttp/client.py +++ b/aiohttp/client.py @@ -9,6 +9,7 @@ import weakref import warnings import chardet +from os import fstat import aiohttp from . import hdrs, helpers, streams @@ -383,7 +384,13 @@ def update_body_from_data(self, data): assert not isinstance(data, io.StringIO), \ 'attempt to send text data instead of binary' self.body = data - self.chunked = True + if not self.chunked and isinstance(data, io.BufferedReader): + # Not chunking if content-length can be determined + size = fstat(data.fileno()).st_size - data.tell() + self.headers[hdrs.CONTENT_LENGTH] = str(size) + self.chunked = False + 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.py b/tests/test_client.py index 5f40db3f122..9250b9e23bc 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -7,6 +7,7 @@ import unittest import unittest.mock import urllib.parse +import os.path import aiohttp from aiohttp.client import ClientRequest, ClientResponse @@ -542,6 +543,39 @@ def test_chunked_length(self): self.assertEqual(req.headers['TRANSFER-ENCODING'], 'chunked') self.assertNotIn('CONTENT-LENGTH', req.headers) + def test_file_upload_not_chunked(self): + here = os.path.dirname(__file__) + fname = os.path.join(here, 'sample.key') + with open(fname, 'rb') as f: + req = ClientRequest( + 'post', 'http://python.org/', + data=f) + self.assertFalse(req.chunked) + self.assertEqual(req.headers['CONTENT-LENGTH'], + str(os.path.getsize(fname))) + + def test_file_upload_not_chunked_seek(self): + here = os.path.dirname(__file__) + fname = os.path.join(here, 'sample.key') + with open(fname, 'rb') as f: + f.seek(100) + req = ClientRequest( + 'post', 'http://python.org/', + data=f) + self.assertEqual(req.headers['CONTENT-LENGTH'], + str(os.path.getsize(fname) - 100)) + + def test_file_upload_force_chunked(self): + here = os.path.dirname(__file__) + fname = os.path.join(here, 'sample.key') + with open(fname, 'rb') as f: + req = ClientRequest( + 'post', 'http://python.org/', + data=f, + chunked=True) + self.assertTrue(req.chunked) + self.assertNotIn('CONTENT-LENGTH', req.headers) + def test_expect100(self): req = ClientRequest('get', 'http://python.org/', expect100=True, loop=self.loop)