1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
From: Bulat Gaifullin <bgaifullin@mirantis.com>
Date: Fri, 24 Jun 2016 15:52:41 +0200
Subject: [PATCH] Fixed handling http redirects
Override method 'redirect_request' for patching
new request, that has been created on handling
http redirect.
Change-Id: I40db406e2377bebec1113639b91a0b5262e2e9ad
Closes-Bug: 1593674
(cherry picked from commit 192a3d9f8f993afb12c5108dd9339c6688c23e11)
---
packetary/library/connections.py | 21 ++++++++++++++++++++-
packetary/tests/test_connections.py | 19 +++++++++++++++++++
2 files changed, 39 insertions(+), 1 deletion(-)
diff --git a/packetary/library/connections.py b/packetary/library/connections.py
index 36a7a84..49b6c9b 100644
--- a/packetary/library/connections.py
+++ b/packetary/library/connections.py
@@ -93,9 +93,23 @@ class ResumableResponse(StreamWrapper):
self.stream = response.stream
-class RetryHandler(urllib.BaseHandler):
+class RetryHandler(urllib.HTTPRedirectHandler):
"""urllib Handler to add ability for retrying on server errors."""
+ def redirect_request(self, req, fp, code, msg, headers, newurl):
+ new_req = urllib.HTTPRedirectHandler.redirect_request(
+ self, req, fp, code, msg, headers, newurl
+ )
+ if new_req is not None:
+ # We use class assignment for casting new request to type
+ # RetryableRequest
+ new_req.__class__ = RetryableRequest
+ new_req.retries_left = req.retries_left
+ new_req.offset = req.offset
+ new_req.start_time = req.start_time
+ new_req.retry_interval = req.retry_interval
+ return new_req
+
@staticmethod
def http_request(request):
"""Initialises http request.
@@ -118,6 +132,11 @@ class RetryHandler(urllib.BaseHandler):
:return: ResumableResponse if success otherwise same response
"""
code, msg = response.getcode(), response.msg
+
+ if 300 <= code < 400:
+ # the redirect group, pass to next handler as is
+ return response
+
# the server should response partial content if range is specified
if request.offset > 0 and code != 206:
raise RangeError(msg)
diff --git a/packetary/tests/test_connections.py b/packetary/tests/test_connections.py
index a2621c8..c80b03d 100644
--- a/packetary/tests/test_connections.py
+++ b/packetary/tests/test_connections.py
@@ -268,6 +268,25 @@ class TestRetryHandler(base.TestCase):
self.handler.http_response(request, response_mock)
self.handler.parent.open.assert_called_once_with(request)
+ @mock.patch(
+ 'packetary.library.connections.urllib.'
+ 'HTTPRedirectHandler.redirect_request'
+ )
+ def test_redirect_request(self, redirect_mock, _):
+ redirect_mock.return_value = connections.urllib.Request(
+ 'http://localhost/'
+ )
+ req = mock.MagicMock(retries_left=10, retry_interval=5, offset=100)
+ new_req = self.handler.redirect_request(req, -1, 301, "", {}, "")
+ self.assertIsInstance(new_req, connections.RetryableRequest)
+ self.assertEqual(req.retries_left, new_req.retries_left)
+ self.assertEqual(req.retry_interval, new_req.retry_interval)
+ self.assertEqual(req.offset, new_req.offset)
+ redirect_mock.return_value = None
+ self.assertIsNone(
+ self.handler.redirect_request(req, -1, 301, "", {}, "")
+ )
+
class TestResumeableResponse(base.TestCase):
def setUp(self):
|