Skip to content

Commit 972bed1

Browse files
committed
wrap requests.Response to ResponseInfo with middleware for reusing need_retry
1 parent da7e06c commit 972bed1

File tree

6 files changed

+61
-34
lines changed

6 files changed

+61
-34
lines changed

qiniu/http/client.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ def __init__(self, middlewares=None, send_opts=None):
1313
self.middlewares = [] if middlewares is None else middlewares
1414
self.send_opts = {} if send_opts is None else send_opts
1515

16+
def _wrap_send(self, req, **kwargs):
17+
resp = self.session.send(req.prepare(), **kwargs)
18+
return ResponseInfo(resp, None)
19+
1620
def send_request(self, request, middlewares=None, **kwargs):
1721
"""
1822
@@ -55,23 +59,20 @@ def send_request(self, request, middlewares=None, **kwargs):
5559
try:
5660
handle = compose_middleware(
5761
mw_ls,
58-
lambda req: self.session.send(req.prepare(), **kwargs)
62+
lambda req: self._wrap_send(req, **kwargs)
5963
)
60-
resp = handle(request)
64+
resp_info = handle(request)
6165
except Exception as e:
6266
return None, ResponseInfo(None, e)
6367

64-
# wrap resp
65-
resp_info = ResponseInfo(resp)
68+
# if ok try dump response info to dict from json
6669
if not resp_info.ok:
6770
return None, resp_info
6871

69-
# try dump response info to dict from json
70-
resp.encoding = "utf-8"
7172
try:
72-
ret = resp.json()
73+
ret = resp_info.json()
7374
except ValueError:
74-
logging.debug("response body decode error: %s" % resp.text)
75+
logging.debug("response body decode error: %s" % resp_info.text_body)
7576
ret = {}
7677
return ret, resp_info
7778

qiniu/http/middleware/base.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ def compose_middleware(middlewares, handle):
66
"""
77
Args:
88
middlewares (list[Middleware]): Middlewares
9-
handle ((requests.Request) -> requests.Response): The send request handle
9+
handle ((requests.Request) -> qiniu.http.response.ResponseInfo): The send request handle
1010
1111
Returns:
12-
(requests.Request) -> requests.Response: Composed handle
12+
(requests.Request) -> qiniu.http.response.ResponseInfo: Composed handle
1313
1414
"""
1515
middlewares.reverse()
@@ -27,7 +27,7 @@ def __call__(self, request, nxt):
2727
"""
2828
Args:
2929
request (requests.Request):
30-
nxt ((requests.Request) -> requests.Response):
30+
nxt ((requests.Request) -> qiniu.http.response.ResponseInfo):
3131
3232
Returns:
3333
requests.Response:

qiniu/http/middleware/retry_domains.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ def _get_changed_url(url, domain):
3434
backup_netloc += domain
3535
if url_parse_result.port is not None:
3636
backup_netloc += ':' + str(url_parse_result.port)
37+
38+
# the _replace is a public method. start with `_` just to prevent conflicts with field names
39+
# see namedtuple docs
3740
url_parse_result = url_parse_result._replace(
3841
netloc=backup_netloc
3942
)
@@ -54,25 +57,25 @@ def _should_retry(self, resp, req):
5457
if callable(self.retry_condition):
5558
return self.retry_condition(resp, req)
5659

57-
return resp is None or not resp.ok
60+
return resp is None or resp.need_retry()
5861

5962
def __call__(self, request, nxt):
60-
resp, err = None, None
63+
resp_info, err = None, None
6164
url_parse_result = urlparse(request.url)
6265

6366
for backup_domain in [str(url_parse_result.hostname)] + self.backup_domains:
6467
request.url = RetryDomainsMiddleware._get_changed_url(request.url, backup_domain)
6568
self.retried_times = 0
6669

6770
while self.retried_times < self.max_retry_times:
68-
resp, err = RetryDomainsMiddleware._try_nxt(request, nxt)
71+
resp_info, err = RetryDomainsMiddleware._try_nxt(request, nxt)
6972
self.retried_times += 1
70-
if not self._should_retry(resp, request):
73+
if not self._should_retry(resp_info, request):
7174
if err is not None:
7275
raise err
73-
return resp
76+
return resp_info
7477

7578
if err is not None:
7679
raise err
7780

78-
return resp
81+
return resp_info

qiniu/http/response.py

+15-6
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,25 @@ def ok(self):
4646
return self.status_code // 100 == 2
4747

4848
def need_retry(self):
49-
if self.__response is None or self.req_id is None:
50-
return True
51-
code = self.status_code
52-
if (code // 100 == 5 and code != 579) or code == 996:
53-
return True
54-
return False
49+
if 0 < self.status_code < 500:
50+
return False
51+
# https://developer.qiniu.com/fusion/kb/1352/the-http-request-return-a-status-code
52+
if self.status_code in [
53+
501, 509, 573, 579, 608, 612, 614, 616, 618, 630, 631, 632, 640, 701
54+
]:
55+
return False
56+
return True
5557

5658
def connect_failed(self):
5759
return self.__response is None or self.req_id is None
5860

61+
def json(self):
62+
try:
63+
self.__response.encoding = "utf-8"
64+
return self.__response.json()
65+
except Exception:
66+
return {}
67+
5968
def __str__(self):
6069
if is_py2:
6170
return ', '.join(

qiniu/region.py

-10
Original file line numberDiff line numberDiff line change
@@ -231,22 +231,12 @@ def bucket_hosts(self, ak, bucket):
231231
uc_backup_retry_times = get_default('default_uc_backup_retry_times')
232232
url = "{0}/v4/query?ak={1}&bucket={2}".format(uc_host, ak, bucket)
233233

234-
def retry_condition(resp, _):
235-
if resp is None:
236-
return True
237-
if resp.status_code in [612, 631]:
238-
# 612 is app / accesskey is not found
239-
# 631 is no such bucket
240-
return False
241-
return not resp.ok
242-
243234
ret, _resp = qn_http_client.get(
244235
url,
245236
middlewares=[
246237
RetryDomainsMiddleware(
247238
backup_domains=uc_backup_hosts,
248239
max_retry_times=uc_backup_retry_times,
249-
retry_condition=retry_condition
250240
)
251241
]
252242
)

test_qiniu.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,36 @@ def __call__(self, request, nxt):
9696

9797

9898
class HttpTest(unittest.TestCase):
99+
def test_response_need_retry(self):
100+
_ret, resp_info = qn_http_client.get('https://qiniu.com/index.html')
101+
102+
resp_info.req_id = 'mocked-req-id'
103+
resp_info.error = None
104+
105+
def gen_case(code):
106+
if 0 < code < 500:
107+
return code, False
108+
if code in [
109+
501, 509, 573, 579, 608, 612, 614, 616, 618, 630, 631, 632, 640, 701
110+
]:
111+
return code, False
112+
return code, True
113+
114+
cases = [
115+
gen_case(i) for i in range(-1, 800)
116+
]
117+
118+
for test_code, should_retry in cases:
119+
resp_info.status_code = test_code
120+
assert_msg = '{0} should{1} retry'.format(test_code, '' if should_retry else ' NOT')
121+
assert resp_info.need_retry() == should_retry, assert_msg
122+
99123
def test_middlewares(self):
100124
rec_ls = []
101125
mw_a = MiddlewareRecorder(rec_ls, 'A')
102126
mw_b = MiddlewareRecorder(rec_ls, 'B')
103127
qn_http_client.get(
104-
"https://qiniu.com/index.html",
128+
'https://qiniu.com/index.html',
105129
middlewares=[
106130
mw_a,
107131
mw_b

0 commit comments

Comments
 (0)