def test_retry_higher_total_loses_vs_read(self): """ A lower read timeout than the total is honored """ error = ReadTimeoutError(None, "/", "read timed out") retry = Retry(read=2, total=3) retry = retry.increment(error=error) retry = retry.increment(error=error) self.assertRaises(MaxRetryError, retry.increment, error=error)
def test_string(self): """ Retry string representation looks the way we expect """ retry = Retry() self.assertEqual(str(retry), 'Retry(total=10, connect=None, read=None, redirect=None)') for _ in range(3): retry = retry.increment() self.assertEqual(str(retry), 'Retry(total=7, connect=None, read=None, redirect=None)')
def test_retry_higher_total_loses(self): """ A lower connect timeout than the total is honored """ error = ConnectTimeoutError() retry = Retry(connect=2, total=3) retry = retry.increment(error=error) retry = retry.increment(error=error) self.assertRaises(MaxRetryError, retry.increment, error=error)
def test_retry_read_zero(self): """ No second chances on read timeouts, by default """ error = ReadTimeoutError(None, "/", "read timed out") retry = Retry(read=0) try: retry.increment(error=error) self.fail("Failed to raise error.") except MaxRetryError as e: self.assertEqual(e.reason, error)
def test_string(self): """ Retry string representation looks the way we expect """ retry = Retry() self.assertEqual( str(retry), 'Retry(total=10, connect=None, read=None, redirect=None)') for _ in range(3): retry = retry.increment() self.assertEqual( str(retry), 'Retry(total=7, connect=None, read=None, redirect=None)')
def test_retry_both_specified(self): """Total can win if it's lower than the connect value""" error = ConnectTimeoutError() retry = Retry(connect=3, total=2) retry = retry.increment(error=error) retry = retry.increment(error=error) try: retry.increment(error=error) self.fail("Failed to raise error.") except MaxRetryError as e: self.assertEqual(e.reason, error)
def test_read_retries(self): """ Should retry for status codes in the whitelist """ retry = Retry(read=1, status_forcelist=[418]) resp = yield From(self.pool.request('GET', '/successful_retry', headers={'test-name': 'test_read_retries'}, retries=retry)) self.assertEqual(resp.status, 200)
def test_read_total_retries(self): """ HTTP response w/ status code in the whitelist should be retried """ headers = {'test-name': 'test_read_total_retries'} retry = Retry(total=1, status_forcelist=[418]) resp = yield From(self.pool.request('GET', '/successful_retry', headers=headers, retries=retry)) self.assertEqual(resp.status, 200)
def test_retries_wrong_whitelist(self): """HTTP response w/ status code not in whitelist shouldn't be retried""" retry = Retry(total=1, status_forcelist=[202]) resp = yield From(self.pool.request('GET', '/successful_retry', headers={'test-name': 'test_wrong_whitelist'}, retries=retry)) self.assertEqual(resp.status, 418)
def test_connect_timeout(self): url = '/sleep?seconds=0.005' timeout = Timeout(connect=0.001) # Pool-global timeout pool = HTTPConnectionPool(TARPIT_HOST, self.port, timeout=timeout) conn = pool._get_conn() yield From(self.aioAssertRaises(ConnectTimeoutError, pool._make_request, conn, 'GET', url)) # Retries retries = Retry(connect=0) yield From(self.aioAssertRaises(MaxRetryError, pool.request, 'GET', url, retries=retries)) # Request-specific connection timeouts big_timeout = Timeout(read=0.2, connect=0.2) pool = HTTPConnectionPool(TARPIT_HOST, self.port, timeout=big_timeout, retries=False) conn = pool._get_conn() yield From(self.aioAssertRaises(ConnectTimeoutError, pool._make_request, conn, 'GET', url, timeout=timeout)) pool._put_conn(conn) yield From(self.aioAssertRaises(ConnectTimeoutError, pool.request, 'GET', url, timeout=timeout))
def test_default_method_whitelist_retried(self): """ urllib3 should retry methods in the default method whitelist """ retry = Retry(total=1, status_forcelist=[418]) resp = yield From(self.pool.request('OPTIONS', '/successful_retry', headers={'test-name': 'test_default_whitelist'}, retries=retry)) self.assertEqual(resp.status, 200)
def test_status_forcelist(self): retry = Retry(status_forcelist=xrange(500, 600)) self.assertFalse(retry.is_forced_retry('GET', status_code=200)) self.assertFalse(retry.is_forced_retry('GET', status_code=400)) self.assertTrue(retry.is_forced_retry('GET', status_code=500)) retry = Retry(total=1, status_forcelist=[418]) self.assertFalse(retry.is_forced_retry('GET', status_code=400)) self.assertTrue(retry.is_forced_retry('GET', status_code=418))
def test_retries_wrong_method_list(self): """Method not in our whitelist should not be retried, even if code matches""" headers = {'test-name': 'test_wrong_method_whitelist'} retry = Retry(total=1, status_forcelist=[418], method_whitelist=['POST']) resp = yield From(self.pool.request('GET', '/successful_retry', headers=headers, retries=retry)) self.assertEqual(resp.status, 418)
def test_retry_total_none(self): """ if Total is none, connect error should take precedence """ error = ConnectTimeoutError() retry = Retry(connect=2, total=None) retry = retry.increment(error=error) retry = retry.increment(error=error) try: retry.increment(error=error) self.fail("Failed to raise error.") except MaxRetryError as e: self.assertEqual(e.reason, error) error = ReadTimeoutError(None, "/", "read timed out") retry = Retry(connect=2, total=None) retry = retry.increment(error=error) retry = retry.increment(error=error) retry = retry.increment(error=error) self.assertFalse(retry.is_exhausted())
def test_retry_reuse_safe(self): """ It should be possible to reuse a Retry object across requests """ headers = {'test-name': 'test_retry_safe'} retry = Retry(total=1, status_forcelist=[418]) resp = yield From(self.pool.request('GET', '/successful_retry', headers=headers, retries=retry)) self.assertEqual(resp.status, 200) resp = yield From(self.pool.request('GET', '/successful_retry', headers=headers, retries=retry)) self.assertEqual(resp.status, 200)
def test_connection_error_retries(self): """ ECONNREFUSED error should raise a connection error, with retries """ port = find_unused_port() pool = HTTPConnectionPool(self.host, port) try: yield From(pool.request('GET', '/', retries=Retry(connect=3))) self.fail("Should have failed with a connection error.") except MaxRetryError as e: self.assertTrue(isinstance(e.reason, ProtocolError)) self.assertEqual(e.reason.args[1].errno, errno.ECONNREFUSED)
def test_disabled_retry(self): """ Disabled retries should disable redirect handling. """ r = yield From(self.pool.request('GET', '/redirect', fields={'target': '/'}, retries=False)) self.assertEqual(r.status, 303) r = yield From(self.pool.request('GET', '/redirect', fields={'target': '/'}, retries=Retry(redirect=False))) self.assertEqual(r.status, 303)
def test_status_forcelist(self): retry = Retry(status_forcelist=xrange(500,600)) self.assertFalse(retry.is_forced_retry('GET', status_code=200)) self.assertFalse(retry.is_forced_retry('GET', status_code=400)) self.assertTrue(retry.is_forced_retry('GET', status_code=500)) retry = Retry(total=1, status_forcelist=[418]) self.assertFalse(retry.is_forced_retry('GET', status_code=400)) self.assertTrue(retry.is_forced_retry('GET', status_code=418))
def test_retry_weird_http_version(self): """ Retry class should handle httplib.BadStatusLine errors properly """ def socket_handler(listener): sock = listener.accept()[0] # First request. # Pause before responding so the first request times out. buf = b'' while not buf.endswith(b'\r\n\r\n'): buf += sock.recv(65536) # send unknown http protocol body = "bad http 0.5 response" sock.send(('HTTP/0.5 200 OK\r\n' 'Content-Type: text/plain\r\n' 'Content-Length: %d\r\n' '\r\n' '%s' % (len(body), body)).encode('utf-8')) sock.close() # Second request. sock = listener.accept()[0] buf = b'' while not buf.endswith(b'\r\n\r\n'): buf += sock.recv(65536) # Now respond immediately. sock.send(('HTTP/1.1 200 OK\r\n' 'Content-Type: text/plain\r\n' 'Content-Length: %d\r\n' '\r\n' 'foo' % (len('foo'))).encode('utf-8')) sock.close() # Close the socket. self._start_server(socket_handler) pool = HTTPConnectionPool(self.host, self.port) retry = Retry(read=1) response = yield From(pool.request('GET', '/', retries=retry)) self.assertEqual(response.status, 200) self.assertEqual((yield From(response.data), b'foo'))
def test_retry_default(self): """ If no value is specified, should retry connects 3 times """ retry = Retry() self.assertEqual(retry.total, 10) self.assertEqual(retry.connect, None) self.assertEqual(retry.read, None) self.assertEqual(retry.redirect, None) error = ConnectTimeoutError() retry = Retry(connect=1) retry = retry.increment(error=error) self.assertRaises(MaxRetryError, retry.increment, error=error) retry = Retry(connect=1) retry = retry.increment(error=error) self.assertFalse(retry.is_exhausted()) self.assertTrue(Retry(0).raise_on_redirect) self.assertFalse(Retry(False).raise_on_redirect)
def test_exhausted(self): self.assertFalse(Retry(0).is_exhausted()) self.assertTrue(Retry(-1).is_exhausted()) self.assertEqual(Retry(1).increment().total, 0)
def test_sleep(self): # sleep a very small amount of time so our code coverage is happy retry = Retry(backoff_factor=0.0001) retry = retry.increment() retry = retry.increment() retry.sleep()
def test_backoff(self): """ Backoff is computed correctly """ max_backoff = Retry.BACKOFF_MAX retry = Retry(total=100, backoff_factor=0.2) self.assertEqual(retry.get_backoff_time(), 0) # First request retry = retry.increment() self.assertEqual(retry.get_backoff_time(), 0) # First retry retry = retry.increment() self.assertEqual(retry.backoff_factor, 0.2) self.assertEqual(retry.total, 98) self.assertEqual(retry.get_backoff_time(), 0.4) # Start backoff retry = retry.increment() self.assertEqual(retry.get_backoff_time(), 0.8) retry = retry.increment() self.assertEqual(retry.get_backoff_time(), 1.6) for i in xrange(10): retry = retry.increment() self.assertEqual(retry.get_backoff_time(), max_backoff)
def test_zero_backoff(self): retry = Retry() self.assertEqual(retry.get_backoff_time(), 0) retry = retry.increment() retry = retry.increment() self.assertEqual(retry.get_backoff_time(), 0)
def test_disabled(self): self.assertRaises(MaxRetryError, Retry(-1).increment) self.assertRaises(MaxRetryError, Retry(0).increment)