summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorNir Soffer <nirsof@gmail.com>2017-11-06 21:16:37 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2017-11-06 21:16:37 (GMT)
commitad455cd9243319b896c86074ffeb3bf78a82f4ec (patch)
tree48a4989023b45958c9fa7dfe090936506998906d /Lib
parent30f4fa456ef626ad7a92759f492ec7a268f7af4e (diff)
downloadcpython-ad455cd9243319b896c86074ffeb3bf78a82f4ec.zip
cpython-ad455cd9243319b896c86074ffeb3bf78a82f4ec.tar.gz
cpython-ad455cd9243319b896c86074ffeb3bf78a82f4ec.tar.bz2
bpo-31945: Configurable blocksize in HTTP(S)Connection (#4279)
blocksize was hardcoded to 8192, preventing efficient upload when using file-like body. Add blocksize argument to __init__, so users can configure the blocksize to fit their needs. I tested this uploading data from /dev/zero to a web server dropping the received data, to test the overhead of the HTTPConnection.send() with a file-like object. Here is an example 10g upload with the default buffer size (8192): $ time ~/src/cpython/release/python upload-httplib.py 10 https://localhost:8000/ Uploaded 10.00g in 17.53 seconds (584.00m/s) real 0m17.574s user 0m8.887s sys 0m5.971s Same with 512k blocksize: $ time ~/src/cpython/release/python upload-httplib.py 10 https://localhost:8000/ Uploaded 10.00g in 6.60 seconds (1551.15m/s) real 0m6.641s user 0m3.426s sys 0m2.162s In real world usage the difference will be smaller, depending on the local and remote storage and the network. See https://github.com/nirs/http-bench for more info.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/http/client.py14
-rw-r--r--Lib/test/test_httplib.py23
2 files changed, 30 insertions, 7 deletions
diff --git a/Lib/http/client.py b/Lib/http/client.py
index bbb3152..70eadae 100644
--- a/Lib/http/client.py
+++ b/Lib/http/client.py
@@ -825,9 +825,10 @@ class HTTPConnection:
return None
def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
- source_address=None):
+ source_address=None, blocksize=8192):
self.timeout = timeout
self.source_address = source_address
+ self.blocksize = blocksize
self.sock = None
self._buffer = []
self.__response = None
@@ -958,7 +959,6 @@ class HTTPConnection:
if self.debuglevel > 0:
print("send:", repr(data))
- blocksize = 8192
if hasattr(data, "read") :
if self.debuglevel > 0:
print("sendIng a read()able")
@@ -966,7 +966,7 @@ class HTTPConnection:
if encode and self.debuglevel > 0:
print("encoding file using iso-8859-1")
while 1:
- datablock = data.read(blocksize)
+ datablock = data.read(self.blocksize)
if not datablock:
break
if encode:
@@ -991,14 +991,13 @@ class HTTPConnection:
self._buffer.append(s)
def _read_readable(self, readable):
- blocksize = 8192
if self.debuglevel > 0:
print("sendIng a read()able")
encode = self._is_textIO(readable)
if encode and self.debuglevel > 0:
print("encoding file using iso-8859-1")
while True:
- datablock = readable.read(blocksize)
+ datablock = readable.read(self.blocksize)
if not datablock:
break
if encode:
@@ -1353,9 +1352,10 @@ else:
def __init__(self, host, port=None, key_file=None, cert_file=None,
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
source_address=None, *, context=None,
- check_hostname=None):
+ check_hostname=None, blocksize=8192):
super(HTTPSConnection, self).__init__(host, port, timeout,
- source_address)
+ source_address,
+ blocksize=blocksize)
if (key_file is not None or cert_file is not None or
check_hostname is not None):
import warnings
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
index 5591f1d..0d79cae 100644
--- a/Lib/test/test_httplib.py
+++ b/Lib/test/test_httplib.py
@@ -756,6 +756,29 @@ class BasicTest(TestCase):
conn.request('GET', '/foo', body(), {'Content-Length': '11'})
self.assertEqual(sock.data, expected)
+ def test_blocksize_request(self):
+ """Check that request() respects the configured block size."""
+ blocksize = 8 # For easy debugging.
+ conn = client.HTTPConnection('example.com', blocksize=blocksize)
+ sock = FakeSocket(None)
+ conn.sock = sock
+ expected = b"a" * blocksize + b"b"
+ conn.request("PUT", "/", io.BytesIO(expected), {"Content-Length": "9"})
+ self.assertEqual(sock.sendall_calls, 3)
+ body = sock.data.split(b"\r\n\r\n", 1)[1]
+ self.assertEqual(body, expected)
+
+ def test_blocksize_send(self):
+ """Check that send() respects the configured block size."""
+ blocksize = 8 # For easy debugging.
+ conn = client.HTTPConnection('example.com', blocksize=blocksize)
+ sock = FakeSocket(None)
+ conn.sock = sock
+ expected = b"a" * blocksize + b"b"
+ conn.send(io.BytesIO(expected))
+ self.assertEqual(sock.sendall_calls, 2)
+ self.assertEqual(sock.data, expected)
+
def test_send_type_error(self):
# See: Issue #12676
conn = client.HTTPConnection('example.com')