From 0b2c0911963e37428f9166b093aff56e5019c141 Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Thu, 19 Nov 2015 18:22:10 +0100 Subject: [PATCH 1/2] Reading the actual response may trigger ECONNRESET or EPIPE (if the connection was cached) thus move that code inside the try:. --- python3/httplib2/__init__.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/python3/httplib2/__init__.py b/python3/httplib2/__init__.py index 3ce019e..00ab5ee 100644 --- a/python3/httplib2/__init__.py +++ b/python3/httplib2/__init__.py @@ -1015,6 +1015,14 @@ def _conn_request(self, conn, request_uri, method, body, headers): pass try: response = conn.getresponse() + content = b"" + if method == "HEAD": + conn.close() + else: + content = response.read() + response = Response(response) + if method != "HEAD": + content = _decompressContent(response, content) except (http.client.BadStatusLine, http.client.ResponseNotReady): # If we get a BadStatusLine on the first try then that means # the connection just went stale, so retry regardless of the @@ -1038,16 +1046,6 @@ def _conn_request(self, conn, request_uri, method, body, headers): continue else: raise - else: - content = b"" - if method == "HEAD": - conn.close() - else: - content = response.read() - response = Response(response) - if method != "HEAD": - content = _decompressContent(response, content) - break return (response, content) From ca9e0b1b5834f3dffd15ba22b6267f3ba24752d8 Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Thu, 19 Nov 2015 19:00:20 +0100 Subject: [PATCH 2/2] More changes for handling broken connections Handle ECONNRESET and EPIPE correctly and simplify the code a bit (pull up conn.close() while we're at it --- python3/httplib2/__init__.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/python3/httplib2/__init__.py b/python3/httplib2/__init__.py index 00ab5ee..398f9af 100644 --- a/python3/httplib2/__init__.py +++ b/python3/httplib2/__init__.py @@ -996,16 +996,18 @@ def _conn_request(self, conn, request_uri, method, body, headers): errno_ = (e.args[0].errno if isinstance(e.args[0], socket.error) else e.errno) if errno_ in (errno.ENETUNREACH, errno.EADDRNOTAVAIL) and i < RETRIES: continue # retry on potentially transient errors + if errno_ in (errno.ECONNRESET, errno.EPIPE) and i == 1: + conn.close() + conn.connect() + continue # retry on closed connection raise except http.client.HTTPException: if conn.sock is None: + conn.close() if i < RETRIES-1: - conn.close() conn.connect() continue - else: - conn.close() - raise + raise if i < RETRIES-1: conn.close() conn.connect() @@ -1027,25 +1029,22 @@ def _conn_request(self, conn, request_uri, method, body, headers): # If we get a BadStatusLine on the first try then that means # the connection just went stale, so retry regardless of the # number of RETRIES set. + conn.close() if not seen_bad_status_line and i == 1: i = 0 seen_bad_status_line = True - conn.close() conn.connect() continue - else: - conn.close() - raise + raise except socket.timeout: raise - except (socket.error, http.client.HTTPException): + except (socket.error, http.client.HTTPException) as e: conn.close() - if i == 0: - conn.close() + errno_ = (e.args[0].errno if isinstance(e.args[0], socket.error) else e.errno) + if errno_ in (errno.ECONNRESET, errno.EPIPE) and i == 1: conn.connect() continue - else: - raise + raise break return (response, content)