Skip to content

Commit c7516a2

Browse files
damgadbusunkim96
andauthored
fix: Adding ConnectionError to retry mechanism (#822)
This commit fixes issue #558 Co-authored-by: Bu Sun Kim <[email protected]>
1 parent 4114485 commit c7516a2

File tree

2 files changed

+38
-30
lines changed

2 files changed

+38
-30
lines changed

googleapiclient/http.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@
8181

8282
_LEGACY_BATCH_URI = "https://www.googleapis.com/batch"
8383

84+
if six.PY2:
85+
# That's a builtin python3 exception, nonexistent in python2.
86+
# Defined to None to avoid NameError while trying to catch it
87+
ConnectionError = None
88+
8489

8590
def _should_retry_response(resp_status, content):
8691
"""Determines whether a response should be retried.
@@ -177,6 +182,10 @@ def _retry_request(
177182
# It's important that this be before socket.error as it's a subclass
178183
# socket.timeout has no errorcode
179184
exception = socket_timeout
185+
except ConnectionError as connection_error:
186+
# Needs to be before socket.error as it's a subclass of
187+
# OSError (socket.error)
188+
exception = connection_error
180189
except socket.error as socket_error:
181190
# errno's contents differ by platform, so we have to match by name.
182191
if socket.errno.errorcode.get(socket_error.errno) not in {

tests/test_http.py

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -132,32 +132,31 @@ def __init__(self, num_errors, success_json, success_data):
132132
def request(self, *args, **kwargs):
133133
if not self.num_errors:
134134
return httplib2.Response(self.success_json), self.success_data
135+
elif self.num_errors == 5 and PY3:
136+
ex = ConnectionResetError # noqa: F821
137+
elif self.num_errors == 4:
138+
ex = httplib2.ServerNotFoundError()
139+
elif self.num_errors == 3:
140+
ex = socket.error()
141+
ex.errno = socket.errno.EPIPE
142+
elif self.num_errors == 2:
143+
ex = ssl.SSLError()
135144
else:
136-
self.num_errors -= 1
137-
if self.num_errors == 1: # initial == 2
138-
raise ssl.SSLError()
139-
if self.num_errors == 3: # initial == 4
140-
raise httplib2.ServerNotFoundError()
141-
else: # initial != 2,4
142-
if self.num_errors == 2:
143-
# first try a broken pipe error (#218)
144-
ex = socket.error()
145-
ex.errno = socket.errno.EPIPE
145+
# Initialize the timeout error code to the platform's error code.
146+
try:
147+
# For Windows:
148+
ex = socket.error()
149+
ex.errno = socket.errno.WSAETIMEDOUT
150+
except AttributeError:
151+
# For Linux/Mac:
152+
if PY3:
153+
ex = socket.timeout()
146154
else:
147-
# Initialize the timeout error code to the platform's error code.
148-
try:
149-
# For Windows:
150-
ex = socket.error()
151-
ex.errno = socket.errno.WSAETIMEDOUT
152-
except AttributeError:
153-
# For Linux/Mac:
154-
if PY3:
155-
ex = socket.timeout()
156-
else:
157-
ex = socket.error()
158-
ex.errno = socket.errno.ETIMEDOUT
159-
# Now raise the correct error.
160-
raise ex
155+
ex = socket.error()
156+
ex.errno = socket.errno.ETIMEDOUT
157+
158+
self.num_errors -= 1
159+
raise ex
161160

162161

163162
class HttpMockWithNonRetriableErrors(object):
@@ -562,14 +561,14 @@ def test_media_io_base_download_handle_4xx(self):
562561

563562
def test_media_io_base_download_retries_connection_errors(self):
564563
self.request.http = HttpMockWithErrors(
565-
4, {"status": "200", "content-range": "0-2/3"}, b"123"
564+
5, {"status": "200", "content-range": "0-2/3"}, b"123"
566565
)
567566

568567
download = MediaIoBaseDownload(fd=self.fd, request=self.request, chunksize=3)
569568
download._sleep = lambda _x: 0 # do nothing
570569
download._rand = lambda: 10
571570

572-
status, done = download.next_chunk(num_retries=4)
571+
status, done = download.next_chunk(num_retries=5)
573572

574573
self.assertEqual(self.fd.getvalue(), b"123")
575574
self.assertEqual(True, done)
@@ -899,13 +898,13 @@ def test_no_retry_connection_errors(self):
899898
def test_retry_connection_errors_non_resumable(self):
900899
model = JsonModel()
901900
request = HttpRequest(
902-
HttpMockWithErrors(4, {"status": "200"}, '{"foo": "bar"}'),
901+
HttpMockWithErrors(5, {"status": "200"}, '{"foo": "bar"}'),
903902
model.response,
904903
u"https://www.example.com/json_api_endpoint",
905904
)
906905
request._sleep = lambda _x: 0 # do nothing
907906
request._rand = lambda: 10
908-
response = request.execute(num_retries=4)
907+
response = request.execute(num_retries=5)
909908
self.assertEqual({u"foo": u"bar"}, response)
910909

911910
def test_retry_connection_errors_resumable(self):
@@ -918,7 +917,7 @@ def test_retry_connection_errors_resumable(self):
918917

919918
request = HttpRequest(
920919
HttpMockWithErrors(
921-
4, {"status": "200", "location": "location"}, '{"foo": "bar"}'
920+
5, {"status": "200", "location": "location"}, '{"foo": "bar"}'
922921
),
923922
model.response,
924923
u"https://www.example.com/file_upload",
@@ -927,7 +926,7 @@ def test_retry_connection_errors_resumable(self):
927926
)
928927
request._sleep = lambda _x: 0 # do nothing
929928
request._rand = lambda: 10
930-
response = request.execute(num_retries=4)
929+
response = request.execute(num_retries=5)
931930
self.assertEqual({u"foo": u"bar"}, response)
932931

933932
def test_retry(self):

0 commit comments

Comments
 (0)