diff options
-rw-r--r-- | Lib/http/client.py | 59 | ||||
-rw-r--r-- | Lib/test/test_httplib.py | 32 |
2 files changed, 55 insertions, 36 deletions
diff --git a/Lib/http/client.py b/Lib/http/client.py index 0ea15ab..6547f85 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -66,10 +66,11 @@ Req-started-unread-response _CS_REQ_STARTED <response_class> Req-sent-unread-response _CS_REQ_SENT <response_class> """ -import io -import socket import email.parser import email.message +import io +import os +import socket from urllib.parse import urlsplit import warnings @@ -673,29 +674,24 @@ class HTTPConnection: # ignore the error... the caller will know if they can retry. if self.debuglevel > 0: print("send:", repr(str)) - try: - blocksize = 8192 - if hasattr(str, "read") : + blocksize = 8192 + if hasattr(str, "read") : + if self.debuglevel > 0: + print("sendIng a read()able") + encode = False + if "b" not in str.mode: + encode = True if self.debuglevel > 0: - print("sendIng a read()able") - encode = False - if "b" not in str.mode: - encode = True - if self.debuglevel > 0: - print("encoding file using iso-8859-1") - while 1: - data = str.read(blocksize) - if not data: - break - if encode: - data = data.encode("iso-8859-1") - self.sock.sendall(data) - else: - self.sock.sendall(str) - except socket.error as v: - if v.args[0] == 32: # Broken pipe - self.close() - raise + print("encoding file using iso-8859-1") + while 1: + data = str.read(blocksize) + if not data: + break + if encode: + data = data.encode("iso-8859-1") + self.sock.sendall(data) + else: + self.sock.sendall(str) def _output(self, s): """Add a line of output to the current request buffer. @@ -869,14 +865,7 @@ class HTTPConnection: def request(self, method, url, body=None, headers={}): """Send a complete request to the server.""" - try: - self._send_request(method, url, body, headers) - except socket.error as v: - # trap 'Broken pipe' if we're allowed to automatically reconnect - if v.args[0] != 32 or not self.auto_open: - raise - # try one more time - self._send_request(method, url, body, headers) + self._send_request(method, url, body, headers) def _set_content_length(self, body): # Set the content-length based on the body. @@ -886,7 +875,6 @@ class HTTPConnection: except TypeError as te: # If this is a file-like object, try to # fstat its file descriptor - import os try: thelen = str(os.fstat(body.fileno()).st_size) except (AttributeError, OSError): @@ -897,7 +885,7 @@ class HTTPConnection: self.putheader('Content-Length', thelen) def _send_request(self, method, url, body, headers): - # honour explicitly requested Host: and Accept-Encoding headers + # Honor explicitly requested Host: and Accept-Encoding: headers. header_names = dict.fromkeys([k.lower() for k in headers]) skips = {} if 'host' in header_names: @@ -983,7 +971,8 @@ else: def connect(self): "Connect to a host on a given (SSL) port." - sock = socket.create_connection((self.host, self.port), self.timeout) + sock = socket.create_connection((self.host, self.port), + self.timeout) self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file) diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 89413a5..d43c82d 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -1,3 +1,4 @@ +import errno from http import client import io import socket @@ -24,6 +25,21 @@ class FakeSocket: raise client.UnimplementedFileMode() return self.fileclass(self.text) +class EPipeSocket(FakeSocket): + + def __init__(self, text, pipe_trigger): + # When sendall() is called with pipe_trigger, raise EPIPE. + FakeSocket.__init__(self, text) + self.pipe_trigger = pipe_trigger + + def sendall(self, data): + if self.pipe_trigger in data: + raise socket.error(errno.EPIPE, "gotcha") + self.data += data + + def close(self): + pass + class NoEOFStringIO(io.BytesIO): """Like StringIO, but raises AssertionError on EOF. @@ -213,6 +229,20 @@ class BasicTest(TestCase): finally: resp.close() + def test_epipe(self): + sock = EPipeSocket( + "HTTP/1.0 401 Authorization Required\r\n" + "Content-type: text/html\r\n" + "WWW-Authenticate: Basic realm=\"example\"\r\n", + b"Content-Length") + conn = client.HTTPConnection("example.com") + conn.sock = sock + self.assertRaises(socket.error, + lambda: conn.request("PUT", "/url", "body")) + resp = conn.getresponse() + self.assertEqual(401, resp.status) + self.assertEqual("Basic realm=\"example\"", + resp.getheader("www-authenticate")) class OfflineTest(TestCase): def test_responses(self): @@ -277,7 +307,7 @@ class RequestBodyTest(TestCase): def setUp(self): self.conn = client.HTTPConnection('example.com') - self.sock = FakeSocket("") + self.conn.sock = self.sock = FakeSocket("") self.conn.sock = self.sock def get_headers_and_fp(self): |