async def arequest(self, method, url, headers=None, stream=None, ext=None): retry = 2 while retry > 0: retry -= 1 try: return await super().arequest(method, url, headers, stream, ext) except (python_socks._errors.ProxyConnectionError, python_socks._errors.ProxyTimeoutError, python_socks._errors.ProxyError) as e: raise httpcore.ProxyError(e) except OSError as e: # socket.gaierror when DNS resolution fails raise httpcore.NetworkError(e) except httpcore.RemoteProtocolError as e: # in case of httpcore.RemoteProtocolError: Server disconnected await close_connections_for_url(self, url) logger.warning('httpcore.RemoteProtocolError: retry', exc_info=e) # retry except (httpcore.NetworkError, httpcore.ProtocolError) as e: # httpcore.WriteError on HTTP/2 connection leaves a new opened stream # then each new request creates a new stream and raise the same WriteError await close_connections_for_url(self, url) raise e
async def test_retries_enabled(server: Server) -> None: """ When retries are enabled, connection failures are retried on with a fixed exponential backoff. """ method = b"GET" url = (b"http", *server.netloc, b"/") headers = [server.host_header] backend = AsyncMockBackend() retries = 10 # Large enough to not run out of retries within this test. async with httpcore.AsyncConnectionPool(retries=retries, max_keepalive_connections=0, backend=backend) as http: # Standard case, no failures. response = await http.arequest(method, url, headers) assert backend.pop_open_tcp_stream_intervals() == [] status_code, _, stream, _ = response assert status_code == 200 await read_body(stream) # One failure, then success. backend.push(httpcore.ConnectError(), None) response = await http.arequest(method, url, headers) assert backend.pop_open_tcp_stream_intervals() == [ pytest.approx(0, abs=1e-3), # Retry immediately. ] status_code, _, stream, _ = response assert status_code == 200 await read_body(stream) # Three failures, then success. backend.push( httpcore.ConnectError(), httpcore.ConnectTimeout(), httpcore.ConnectTimeout(), None, ) response = await http.arequest(method, url, headers) assert backend.pop_open_tcp_stream_intervals() == [ pytest.approx(0, abs=1e-3), # Retry immediately. pytest.approx(0.5, rel=0.1), # First backoff. pytest.approx(1.0, rel=0.1), # Second (increased) backoff. ] status_code, _, stream, _ = response assert status_code == 200 await read_body(stream) # Non-connect exceptions are not retried on. backend.push(httpcore.ReadTimeout(), httpcore.NetworkError()) with pytest.raises(httpcore.ReadTimeout): await http.arequest(method, url, headers) with pytest.raises(httpcore.NetworkError): await http.arequest(method, url, headers)
def test_side_effect_exception(): router = Router() router.get("https://foo.bar/").side_effect(httpx.ConnectError) router.get("https://ham.spam/").side_effect(httpcore.NetworkError) router.get("https://egg.plant/").side_effect(httpcore.NetworkError()) request = httpx.Request("GET", "https://foo.bar") with pytest.raises(httpx.ConnectError) as e: router.resolve(request) assert e.value.request == request request = httpx.Request("GET", "https://ham.spam") with pytest.raises(httpcore.NetworkError) as e: router.resolve(request) request = httpx.Request("GET", "https://egg.plant") with pytest.raises(httpcore.NetworkError) as e: router.resolve(request)
def _callback(request): raise httpcore.NetworkError()