2525
2626
2727class Transport (abstract .Transport ):
28+ _connection_timeout_seconds = 20
29+ _request_max_retries = 2
30+
2831 def __init__ (self , logger , endpoint = None , max_connections = None , timeout = None , verbosity = None ):
2932 super (Transport , self ).__init__ (logger , endpoint , max_connections , timeout , verbosity )
3033
@@ -36,24 +39,19 @@ def __init__(self, logger, endpoint=None, max_connections=None, timeout=None, ve
3639 # create the pool connection
3740 self ._create_connections (self .max_connections , self ._host , self ._ssl_context )
3841
39- # python 2 and 3 have different exceptions
40- if sys .version_info [0 ] >= 3 :
41- self ._wait_response_exceptions = (
42- http .client .RemoteDisconnected ,
43- ConnectionResetError ,
44- ConnectionRefusedError ,
45- http .client .ResponseNotReady ,
46- )
47- self ._send_request_exceptions = (
48- BrokenPipeError ,
49- http .client .CannotSendRequest ,
50- http .client .RemoteDisconnected ,
51- )
52- self ._get_status_and_headers = self ._get_status_and_headers_py3
53- else :
54- self ._wait_response_exceptions = (http .client .BadStatusLine , socket .error )
55- self ._send_request_exceptions = (http .client .CannotSendRequest , http .client .BadStatusLine )
56- self ._get_status_and_headers = self ._get_status_and_headers_py2
42+ self ._wait_response_exceptions = (
43+ http .client .RemoteDisconnected ,
44+ ConnectionResetError ,
45+ ConnectionRefusedError ,
46+ http .client .ResponseNotReady ,
47+ )
48+ self ._send_request_exceptions = (
49+ BrokenPipeError ,
50+ http .client .CannotSendRequest ,
51+ http .client .RemoteDisconnected ,
52+ socket .timeout ,
53+ )
54+ self ._get_status_and_headers = self ._get_status_and_headers_py3
5755
5856 def close (self ):
5957 # Ignore redundant calls to close
@@ -154,20 +152,27 @@ def _send_request_on_connection(self, request, connection):
154152 self .log (
155153 "Tx" , connection = connection , method = request .method , path = path , headers = request .headers , body = request .body
156154 )
155+
157156 starting_offset = 0
158157 is_body_seekable = request .body and hasattr (request .body , "seek" ) and hasattr (request .body , "tell" )
159158 if is_body_seekable :
160159 starting_offset = request .body .tell ()
161- try :
160+
161+ retries_left = self ._request_max_retries
162+ while True :
162163 try :
163164 connection .request (request .method , path , request .body , request .headers )
165+ break
164166 except self ._send_request_exceptions as e :
165167 self ._logger .debug_with (
166- "Disconnected while attempting to send. Recreating connection and retrying" ,
168+ f"Disconnected while attempting to send request – "
169+ f"{ retries_left } out of { self ._request_max_retries } retries left." ,
167170 e = type (e ),
168171 e_msg = e ,
169- connection = connection ,
170172 )
173+ if retries_left == 0 :
174+ raise
175+ retries_left -= 1
171176 connection .close ()
172177 if is_body_seekable :
173178 # If the first connection fails, the pointer of the body might move at the size
@@ -176,12 +181,11 @@ def _send_request_on_connection(self, request, connection):
176181 request .body .seek (starting_offset )
177182 connection = self ._create_connection (self ._host , self ._ssl_context )
178183 request .transport .connection_used = connection
179- connection .request (request .method , path , request .body , request .headers )
180- except BaseException as e :
181- self ._logger .error_with (
182- "Unhandled exception while sending request" , e = type (e ), e_msg = e , connection = connection
183- )
184- raise e
184+ except BaseException as e :
185+ self ._logger .error_with (
186+ "Unhandled exception while sending request" , e = type (e ), e_msg = e , connection = connection
187+ )
188+ raise e
185189
186190 return request
187191
@@ -192,9 +196,9 @@ def _create_connections(self, num_connections, host, ssl_context):
192196
193197 def _create_connection (self , host , ssl_context ):
194198 if ssl_context is None :
195- return http .client .HTTPConnection (host )
199+ return http .client .HTTPConnection (host , timeout = self . _connection_timeout_seconds )
196200
197- return http .client .HTTPSConnection (host , context = ssl_context )
201+ return http .client .HTTPSConnection (host , timeout = self . _connection_timeout_seconds , context = ssl_context )
198202
199203 def _parse_endpoint (self , endpoint ):
200204 if endpoint .startswith ("http://" ):
0 commit comments