rigid 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. checksums.yaml +4 -4
  2. data/vendor/click/__init__.pyc +0 -0
  3. data/vendor/click/_bashcomplete.pyc +0 -0
  4. data/vendor/click/_compat.pyc +0 -0
  5. data/vendor/click/_termui_impl.pyc +0 -0
  6. data/vendor/click/_textwrap.pyc +0 -0
  7. data/vendor/click/_unicodefun.pyc +0 -0
  8. data/vendor/click/_winconsole.pyc +0 -0
  9. data/vendor/click/core.pyc +0 -0
  10. data/vendor/click/decorators.pyc +0 -0
  11. data/vendor/click/exceptions.pyc +0 -0
  12. data/vendor/click/formatting.pyc +0 -0
  13. data/vendor/click/globals.pyc +0 -0
  14. data/vendor/click/parser.pyc +0 -0
  15. data/vendor/click/termui.pyc +0 -0
  16. data/vendor/click/testing.pyc +0 -0
  17. data/vendor/click/types.pyc +0 -0
  18. data/vendor/click/utils.pyc +0 -0
  19. data/vendor/easy_install.pyc +0 -0
  20. data/vendor/pkg_resources/__init__.pyc +0 -0
  21. data/vendor/pkg_resources/_vendor/__init__.pyc +0 -0
  22. data/vendor/pkg_resources/_vendor/appdirs.pyc +0 -0
  23. data/vendor/pkg_resources/_vendor/packaging/__about__.pyc +0 -0
  24. data/vendor/pkg_resources/_vendor/packaging/__init__.pyc +0 -0
  25. data/vendor/pkg_resources/_vendor/packaging/_compat.pyc +0 -0
  26. data/vendor/pkg_resources/_vendor/packaging/_structures.pyc +0 -0
  27. data/vendor/pkg_resources/_vendor/packaging/markers.pyc +0 -0
  28. data/vendor/pkg_resources/_vendor/packaging/requirements.pyc +0 -0
  29. data/vendor/pkg_resources/_vendor/packaging/specifiers.pyc +0 -0
  30. data/vendor/pkg_resources/_vendor/packaging/utils.pyc +0 -0
  31. data/vendor/pkg_resources/_vendor/packaging/version.pyc +0 -0
  32. data/vendor/pkg_resources/_vendor/pyparsing.pyc +0 -0
  33. data/vendor/pkg_resources/_vendor/six.pyc +0 -0
  34. data/vendor/pkg_resources/extern/__init__.pyc +0 -0
  35. data/vendor/{requests-2.11.1.dist-info/METADATA → requests-2.12.1.dist-info/DESCRIPTION.rst} +51 -30
  36. data/vendor/{requests-2.11.1.dist-info → requests-2.12.1.dist-info}/INSTALLER +0 -0
  37. data/vendor/{requests-2.11.1.dist-info/DESCRIPTION.rst → requests-2.12.1.dist-info/METADATA} +80 -1
  38. data/vendor/{requests-2.11.1.dist-info → requests-2.12.1.dist-info}/RECORD +61 -41
  39. data/vendor/{requests-2.11.1.dist-info → requests-2.12.1.dist-info}/WHEEL +1 -1
  40. data/vendor/requests-2.12.1.dist-info/metadata.json +1 -0
  41. data/vendor/{requests-2.11.1.dist-info → requests-2.12.1.dist-info}/top_level.txt +0 -0
  42. data/vendor/requests/__init__.py +2 -2
  43. data/vendor/requests/__init__.pyc +0 -0
  44. data/vendor/requests/_internal_utils.py +27 -0
  45. data/vendor/requests/_internal_utils.pyc +0 -0
  46. data/vendor/requests/adapters.py +1 -1
  47. data/vendor/requests/adapters.pyc +0 -0
  48. data/vendor/requests/api.py +3 -1
  49. data/vendor/requests/api.pyc +0 -0
  50. data/vendor/requests/auth.py +2 -1
  51. data/vendor/requests/auth.pyc +0 -0
  52. data/vendor/requests/cacert.pem +448 -375
  53. data/vendor/requests/certs.pyc +0 -0
  54. data/vendor/requests/compat.py +2 -0
  55. data/vendor/requests/compat.pyc +0 -0
  56. data/vendor/requests/cookies.py +3 -1
  57. data/vendor/requests/cookies.pyc +0 -0
  58. data/vendor/requests/exceptions.py +2 -0
  59. data/vendor/requests/exceptions.pyc +0 -0
  60. data/vendor/requests/hooks.pyc +0 -0
  61. data/vendor/requests/models.py +53 -29
  62. data/vendor/requests/models.pyc +0 -0
  63. data/vendor/requests/packages/__init__.py +6 -0
  64. data/vendor/requests/packages/__init__.pyc +0 -0
  65. data/vendor/requests/packages/chardet/__init__.pyc +0 -0
  66. data/vendor/requests/packages/chardet/big5freq.pyc +0 -0
  67. data/vendor/requests/packages/chardet/big5prober.pyc +0 -0
  68. data/vendor/requests/packages/chardet/chardetect.pyc +0 -0
  69. data/vendor/requests/packages/chardet/chardistribution.pyc +0 -0
  70. data/vendor/requests/packages/chardet/charsetgroupprober.pyc +0 -0
  71. data/vendor/requests/packages/chardet/charsetprober.pyc +0 -0
  72. data/vendor/requests/packages/chardet/codingstatemachine.pyc +0 -0
  73. data/vendor/requests/packages/chardet/compat.pyc +0 -0
  74. data/vendor/requests/packages/chardet/constants.pyc +0 -0
  75. data/vendor/requests/packages/chardet/cp949prober.pyc +0 -0
  76. data/vendor/requests/packages/chardet/escprober.pyc +0 -0
  77. data/vendor/requests/packages/chardet/escsm.pyc +0 -0
  78. data/vendor/requests/packages/chardet/eucjpprober.pyc +0 -0
  79. data/vendor/requests/packages/chardet/euckrfreq.pyc +0 -0
  80. data/vendor/requests/packages/chardet/euckrprober.pyc +0 -0
  81. data/vendor/requests/packages/chardet/euctwfreq.pyc +0 -0
  82. data/vendor/requests/packages/chardet/euctwprober.pyc +0 -0
  83. data/vendor/requests/packages/chardet/gb2312freq.pyc +0 -0
  84. data/vendor/requests/packages/chardet/gb2312prober.pyc +0 -0
  85. data/vendor/requests/packages/chardet/hebrewprober.pyc +0 -0
  86. data/vendor/requests/packages/chardet/jisfreq.pyc +0 -0
  87. data/vendor/requests/packages/chardet/jpcntx.pyc +0 -0
  88. data/vendor/requests/packages/chardet/langbulgarianmodel.pyc +0 -0
  89. data/vendor/requests/packages/chardet/langcyrillicmodel.pyc +0 -0
  90. data/vendor/requests/packages/chardet/langgreekmodel.pyc +0 -0
  91. data/vendor/requests/packages/chardet/langhebrewmodel.pyc +0 -0
  92. data/vendor/requests/packages/chardet/langhungarianmodel.pyc +0 -0
  93. data/vendor/requests/packages/chardet/langthaimodel.pyc +0 -0
  94. data/vendor/requests/packages/chardet/latin1prober.pyc +0 -0
  95. data/vendor/requests/packages/chardet/mbcharsetprober.pyc +0 -0
  96. data/vendor/requests/packages/chardet/mbcsgroupprober.pyc +0 -0
  97. data/vendor/requests/packages/chardet/mbcssm.pyc +0 -0
  98. data/vendor/requests/packages/chardet/sbcharsetprober.pyc +0 -0
  99. data/vendor/requests/packages/chardet/sbcsgroupprober.pyc +0 -0
  100. data/vendor/requests/packages/chardet/sjisprober.pyc +0 -0
  101. data/vendor/requests/packages/chardet/universaldetector.pyc +0 -0
  102. data/vendor/requests/packages/chardet/utf8prober.pyc +0 -0
  103. data/vendor/requests/packages/idna/__init__.py +1 -0
  104. data/vendor/requests/packages/idna/__init__.pyc +0 -0
  105. data/vendor/requests/packages/idna/codec.py +118 -0
  106. data/vendor/requests/packages/idna/codec.pyc +0 -0
  107. data/vendor/requests/packages/idna/compat.py +12 -0
  108. data/vendor/requests/packages/idna/compat.pyc +0 -0
  109. data/vendor/requests/packages/idna/core.py +387 -0
  110. data/vendor/requests/packages/idna/core.pyc +0 -0
  111. data/vendor/requests/packages/idna/idnadata.py +1584 -0
  112. data/vendor/requests/packages/idna/idnadata.pyc +0 -0
  113. data/vendor/requests/packages/idna/intranges.py +46 -0
  114. data/vendor/requests/packages/idna/intranges.pyc +0 -0
  115. data/vendor/requests/packages/idna/uts46data.py +7267 -0
  116. data/vendor/requests/packages/idna/uts46data.pyc +0 -0
  117. data/vendor/requests/packages/urllib3/__init__.py +2 -1
  118. data/vendor/requests/packages/urllib3/__init__.pyc +0 -0
  119. data/vendor/requests/packages/urllib3/_collections.pyc +0 -0
  120. data/vendor/requests/packages/urllib3/connection.py +62 -26
  121. data/vendor/requests/packages/urllib3/connection.pyc +0 -0
  122. data/vendor/requests/packages/urllib3/connectionpool.py +25 -20
  123. data/vendor/requests/packages/urllib3/connectionpool.pyc +0 -0
  124. data/vendor/requests/packages/urllib3/contrib/__init__.pyc +0 -0
  125. data/vendor/requests/packages/urllib3/contrib/appengine.py +87 -22
  126. data/vendor/requests/packages/urllib3/contrib/appengine.pyc +0 -0
  127. data/vendor/requests/packages/urllib3/contrib/ntlmpool.py +2 -5
  128. data/vendor/requests/packages/urllib3/contrib/ntlmpool.pyc +0 -0
  129. data/vendor/requests/packages/urllib3/contrib/pyopenssl.py +191 -118
  130. data/vendor/requests/packages/urllib3/contrib/pyopenssl.pyc +0 -0
  131. data/vendor/requests/packages/urllib3/contrib/socks.py +11 -5
  132. data/vendor/requests/packages/urllib3/contrib/socks.pyc +0 -0
  133. data/vendor/requests/packages/urllib3/exceptions.py +32 -0
  134. data/vendor/requests/packages/urllib3/exceptions.pyc +0 -0
  135. data/vendor/requests/packages/urllib3/fields.py +1 -1
  136. data/vendor/requests/packages/urllib3/fields.pyc +0 -0
  137. data/vendor/requests/packages/urllib3/filepost.py +1 -1
  138. data/vendor/requests/packages/urllib3/filepost.pyc +0 -0
  139. data/vendor/requests/packages/urllib3/packages/__init__.pyc +0 -0
  140. data/vendor/requests/packages/urllib3/packages/backports/__init__.py +0 -0
  141. data/vendor/requests/packages/urllib3/packages/backports/__init__.pyc +0 -0
  142. data/vendor/requests/packages/urllib3/packages/backports/makefile.py +53 -0
  143. data/vendor/requests/packages/urllib3/packages/backports/makefile.pyc +0 -0
  144. data/vendor/requests/packages/urllib3/packages/ordered_dict.pyc +0 -0
  145. data/vendor/requests/packages/urllib3/packages/six.pyc +0 -0
  146. data/vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py +7 -1
  147. data/vendor/requests/packages/urllib3/packages/ssl_match_hostname/__init__.pyc +0 -0
  148. data/vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.py +55 -3
  149. data/vendor/requests/packages/urllib3/packages/ssl_match_hostname/_implementation.pyc +0 -0
  150. data/vendor/requests/packages/urllib3/poolmanager.py +2 -6
  151. data/vendor/requests/packages/urllib3/poolmanager.pyc +0 -0
  152. data/vendor/requests/packages/urllib3/request.py +1 -4
  153. data/vendor/requests/packages/urllib3/request.pyc +0 -0
  154. data/vendor/requests/packages/urllib3/response.py +94 -6
  155. data/vendor/requests/packages/urllib3/response.pyc +0 -0
  156. data/vendor/requests/packages/urllib3/util/__init__.pyc +0 -0
  157. data/vendor/requests/packages/urllib3/util/connection.py +1 -0
  158. data/vendor/requests/packages/urllib3/util/connection.pyc +0 -0
  159. data/vendor/requests/packages/urllib3/util/request.pyc +0 -0
  160. data/vendor/requests/packages/urllib3/util/response.py +7 -0
  161. data/vendor/requests/packages/urllib3/util/response.pyc +0 -0
  162. data/vendor/requests/packages/urllib3/util/retry.py +93 -17
  163. data/vendor/requests/packages/urllib3/util/retry.pyc +0 -0
  164. data/vendor/requests/packages/urllib3/util/ssl_.py +28 -12
  165. data/vendor/requests/packages/urllib3/util/ssl_.pyc +0 -0
  166. data/vendor/requests/packages/urllib3/util/timeout.py +9 -6
  167. data/vendor/requests/packages/urllib3/util/timeout.pyc +0 -0
  168. data/vendor/requests/packages/urllib3/util/url.py +14 -5
  169. data/vendor/requests/packages/urllib3/util/url.pyc +0 -0
  170. data/vendor/requests/sessions.py +18 -5
  171. data/vendor/requests/sessions.pyc +0 -0
  172. data/vendor/requests/status_codes.pyc +0 -0
  173. data/vendor/requests/structures.pyc +0 -0
  174. data/vendor/requests/utils.py +42 -32
  175. data/vendor/requests/utils.pyc +0 -0
  176. data/vendor/{rigid-0.2.0.dist-info → rigid-0.2.1.dist-info}/DESCRIPTION.rst +0 -0
  177. data/vendor/{rigid-0.2.0.dist-info → rigid-0.2.1.dist-info}/INSTALLER +0 -0
  178. data/vendor/{rigid-0.2.0.dist-info → rigid-0.2.1.dist-info}/METADATA +2 -2
  179. data/vendor/{rigid-0.2.0.dist-info → rigid-0.2.1.dist-info}/RECORD +18 -18
  180. data/vendor/{rigid-0.2.0.dist-info → rigid-0.2.1.dist-info}/WHEEL +0 -0
  181. data/vendor/{rigid-0.2.0.dist-info → rigid-0.2.1.dist-info}/entry_points.txt +0 -0
  182. data/vendor/{rigid-0.2.0.dist-info → rigid-0.2.1.dist-info}/metadata.json +1 -1
  183. data/vendor/{rigid-0.2.0.dist-info → rigid-0.2.1.dist-info}/top_level.txt +0 -0
  184. data/vendor/rigid/__init__.pyc +0 -0
  185. data/vendor/rigid/api.py +39 -2
  186. data/vendor/rigid/api.pyc +0 -0
  187. data/vendor/rigid/commands/__init__.py +71 -11
  188. data/vendor/rigid/commands/__init__.pyc +0 -0
  189. data/vendor/rigid/commands/deploy.pyc +0 -0
  190. data/vendor/rigid/deploy.py +1 -1
  191. data/vendor/rigid/deploy.pyc +0 -0
  192. data/vendor/rigid/file_scanner.py +1 -1
  193. data/vendor/rigid/file_scanner.pyc +0 -0
  194. data/vendor/rigid/utils.pyc +0 -0
  195. data/vendor/tests/__init__.pyc +0 -0
  196. data/vendor/tests/integration/__init__.pyc +0 -0
  197. data/vendor/tests/integration/test_app.py +12 -0
  198. data/vendor/tests/integration/test_app.pyc +0 -0
  199. data/vendor/tests/integration/test_apps.pyc +0 -0
  200. data/vendor/tests/integration/test_deploy.pyc +0 -0
  201. data/vendor/tests/integration/test_domains.pyc +0 -0
  202. data/vendor/tests/integration/test_login.pyc +0 -0
  203. data/vendor/tests/integration/test_promote.py +2 -2
  204. data/vendor/tests/integration/test_promote.pyc +0 -0
  205. data/vendor/tests/integration/test_token.pyc +0 -0
  206. data/vendor/tests/integration/test_whoami.pyc +0 -0
  207. data/vendor/tests/test_deploy.pyc +0 -0
  208. data/vendor/tests/test_file_scanner.pyc +0 -0
  209. data/vendor/tests/utils.py +5 -2
  210. data/vendor/tests/utils.pyc +0 -0
  211. data/vendor/yaml/__init__.pyc +0 -0
  212. data/vendor/yaml/composer.pyc +0 -0
  213. data/vendor/yaml/constructor.pyc +0 -0
  214. data/vendor/yaml/cyaml.pyc +0 -0
  215. data/vendor/yaml/dumper.pyc +0 -0
  216. data/vendor/yaml/emitter.pyc +0 -0
  217. data/vendor/yaml/error.pyc +0 -0
  218. data/vendor/yaml/events.pyc +0 -0
  219. data/vendor/yaml/loader.pyc +0 -0
  220. data/vendor/yaml/nodes.pyc +0 -0
  221. data/vendor/yaml/parser.pyc +0 -0
  222. data/vendor/yaml/reader.pyc +0 -0
  223. data/vendor/yaml/representer.pyc +0 -0
  224. data/vendor/yaml/resolver.pyc +0 -0
  225. data/vendor/yaml/scanner.pyc +0 -0
  226. data/vendor/yaml/serializer.pyc +0 -0
  227. data/vendor/yaml/tokens.pyc +0 -0
  228. metadata +37 -17
  229. data/vendor/requests-2.11.1.dist-info/metadata.json +0 -1
@@ -1,6 +1,10 @@
1
1
  from __future__ import absolute_import
2
2
  import time
3
3
  import logging
4
+ from collections import namedtuple
5
+ from itertools import takewhile
6
+ import email
7
+ import re
4
8
 
5
9
  from ..exceptions import (
6
10
  ConnectTimeoutError,
@@ -8,12 +12,17 @@ from ..exceptions import (
8
12
  ProtocolError,
9
13
  ReadTimeoutError,
10
14
  ResponseError,
15
+ InvalidHeader,
11
16
  )
12
17
  from ..packages import six
13
18
 
14
19
 
15
20
  log = logging.getLogger(__name__)
16
21
 
22
+ # Data structure for representing the metadata of requests that result in a retry.
23
+ RequestHistory = namedtuple('RequestHistory', ["method", "url", "error",
24
+ "status", "redirect_location"])
25
+
17
26
 
18
27
  class Retry(object):
19
28
  """ Retry configuration.
@@ -113,18 +122,29 @@ class Retry(object):
113
122
  whether we should raise an exception, or return a response,
114
123
  if status falls in ``status_forcelist`` range and retries have
115
124
  been exhausted.
125
+
126
+ :param tuple history: The history of the request encountered during
127
+ each call to :meth:`~Retry.increment`. The list is in the order
128
+ the requests occurred. Each list item is of class :class:`RequestHistory`.
129
+
130
+ :param bool respect_retry_after_header:
131
+ Whether to respect Retry-After header on status codes defined as
132
+ :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not.
133
+
116
134
  """
117
135
 
118
136
  DEFAULT_METHOD_WHITELIST = frozenset([
119
137
  'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'])
120
138
 
139
+ RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503])
140
+
121
141
  #: Maximum backoff time.
122
142
  BACKOFF_MAX = 120
123
143
 
124
144
  def __init__(self, total=10, connect=None, read=None, redirect=None,
125
145
  method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None,
126
146
  backoff_factor=0, raise_on_redirect=True, raise_on_status=True,
127
- _observed_errors=0):
147
+ history=None, respect_retry_after_header=True):
128
148
 
129
149
  self.total = total
130
150
  self.connect = connect
@@ -140,7 +160,8 @@ class Retry(object):
140
160
  self.backoff_factor = backoff_factor
141
161
  self.raise_on_redirect = raise_on_redirect
142
162
  self.raise_on_status = raise_on_status
143
- self._observed_errors = _observed_errors # TODO: use .history instead?
163
+ self.history = history or tuple()
164
+ self.respect_retry_after_header = respect_retry_after_header
144
165
 
145
166
  def new(self, **kw):
146
167
  params = dict(
@@ -151,7 +172,7 @@ class Retry(object):
151
172
  backoff_factor=self.backoff_factor,
152
173
  raise_on_redirect=self.raise_on_redirect,
153
174
  raise_on_status=self.raise_on_status,
154
- _observed_errors=self._observed_errors,
175
+ history=self.history,
155
176
  )
156
177
  params.update(kw)
157
178
  return type(self)(**params)
@@ -175,23 +196,71 @@ class Retry(object):
175
196
 
176
197
  :rtype: float
177
198
  """
178
- if self._observed_errors <= 1:
199
+ # We want to consider only the last consecutive errors sequence (Ignore redirects).
200
+ consecutive_errors_len = len(list(takewhile(lambda x: x.redirect_location is None,
201
+ reversed(self.history))))
202
+ if consecutive_errors_len <= 1:
179
203
  return 0
180
204
 
181
- backoff_value = self.backoff_factor * (2 ** (self._observed_errors - 1))
205
+ backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1))
182
206
  return min(self.BACKOFF_MAX, backoff_value)
183
207
 
184
- def sleep(self):
185
- """ Sleep between retry attempts using an exponential backoff.
208
+ def parse_retry_after(self, retry_after):
209
+ # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4
210
+ if re.match(r"^\s*[0-9]+\s*$", retry_after):
211
+ seconds = int(retry_after)
212
+ else:
213
+ retry_date_tuple = email.utils.parsedate(retry_after)
214
+ if retry_date_tuple is None:
215
+ raise InvalidHeader("Invalid Retry-After header: %s" % retry_after)
216
+ retry_date = time.mktime(retry_date_tuple)
217
+ seconds = retry_date - time.time()
186
218
 
187
- By default, the backoff factor is 0 and this method will return
188
- immediately.
189
- """
219
+ if seconds < 0:
220
+ seconds = 0
221
+
222
+ return seconds
223
+
224
+ def get_retry_after(self, response):
225
+ """ Get the value of Retry-After in seconds. """
226
+
227
+ retry_after = response.getheader("Retry-After")
228
+
229
+ if retry_after is None:
230
+ return None
231
+
232
+ return self.parse_retry_after(retry_after)
233
+
234
+ def sleep_for_retry(self, response=None):
235
+ retry_after = self.get_retry_after(response)
236
+ if retry_after:
237
+ time.sleep(retry_after)
238
+ return True
239
+
240
+ return False
241
+
242
+ def _sleep_backoff(self):
190
243
  backoff = self.get_backoff_time()
191
244
  if backoff <= 0:
192
245
  return
193
246
  time.sleep(backoff)
194
247
 
248
+ def sleep(self, response=None):
249
+ """ Sleep between retry attempts.
250
+
251
+ This method will respect a server's ``Retry-After`` response header
252
+ and sleep the duration of the time requested. If that is not present, it
253
+ will use an exponential backoff. By default, the backoff factor is 0 and
254
+ this method will return immediately.
255
+ """
256
+
257
+ if response:
258
+ slept = self.sleep_for_retry(response)
259
+ if slept:
260
+ return
261
+
262
+ self._sleep_backoff()
263
+
195
264
  def _is_connection_error(self, err):
196
265
  """ Errors when we're fairly sure that the server did not receive the
197
266
  request, so it should be safe to retry.
@@ -204,13 +273,17 @@ class Retry(object):
204
273
  """
205
274
  return isinstance(err, (ReadTimeoutError, ProtocolError))
206
275
 
207
- def is_forced_retry(self, method, status_code):
276
+ def is_retry(self, method, status_code, has_retry_after=False):
208
277
  """ Is this method/status code retryable? (Based on method/codes whitelists)
209
278
  """
210
279
  if self.method_whitelist and method.upper() not in self.method_whitelist:
211
280
  return False
212
281
 
213
- return self.status_forcelist and status_code in self.status_forcelist
282
+ if self.status_forcelist and status_code in self.status_forcelist:
283
+ return True
284
+
285
+ return (self.total and self.respect_retry_after_header and
286
+ has_retry_after and (status_code in self.RETRY_AFTER_STATUS_CODES))
214
287
 
215
288
  def is_exhausted(self):
216
289
  """ Are we out of retries? """
@@ -241,11 +314,12 @@ class Retry(object):
241
314
  if total is not None:
242
315
  total -= 1
243
316
 
244
- _observed_errors = self._observed_errors
245
317
  connect = self.connect
246
318
  read = self.read
247
319
  redirect = self.redirect
248
320
  cause = 'unknown'
321
+ status = None
322
+ redirect_location = None
249
323
 
250
324
  if error and self._is_connection_error(error):
251
325
  # Connect retry?
@@ -253,7 +327,6 @@ class Retry(object):
253
327
  raise six.reraise(type(error), error, _stacktrace)
254
328
  elif connect is not None:
255
329
  connect -= 1
256
- _observed_errors += 1
257
330
 
258
331
  elif error and self._is_read_error(error):
259
332
  # Read retry?
@@ -261,27 +334,30 @@ class Retry(object):
261
334
  raise six.reraise(type(error), error, _stacktrace)
262
335
  elif read is not None:
263
336
  read -= 1
264
- _observed_errors += 1
265
337
 
266
338
  elif response and response.get_redirect_location():
267
339
  # Redirect retry?
268
340
  if redirect is not None:
269
341
  redirect -= 1
270
342
  cause = 'too many redirects'
343
+ redirect_location = response.get_redirect_location()
344
+ status = response.status
271
345
 
272
346
  else:
273
347
  # Incrementing because of a server error like a 500 in
274
348
  # status_forcelist and a the given method is in the whitelist
275
- _observed_errors += 1
276
349
  cause = ResponseError.GENERIC_ERROR
277
350
  if response and response.status:
278
351
  cause = ResponseError.SPECIFIC_ERROR.format(
279
352
  status_code=response.status)
353
+ status = response.status
354
+
355
+ history = self.history + (RequestHistory(method, url, error, status, redirect_location),)
280
356
 
281
357
  new_retry = self.new(
282
358
  total=total,
283
359
  connect=connect, read=read, redirect=redirect,
284
- _observed_errors=_observed_errors)
360
+ history=history)
285
361
 
286
362
  if new_retry.is_exhausted():
287
363
  raise MaxRetryError(_pool, url, error or ResponseError(cause))
@@ -11,7 +11,6 @@ from ..exceptions import SSLError, InsecurePlatformWarning, SNIMissingWarning
11
11
 
12
12
  SSLContext = None
13
13
  HAS_SNI = False
14
- create_default_context = None
15
14
  IS_PYOPENSSL = False
16
15
 
17
16
  # Maps the length of a digest to a possible hash function producing this digest
@@ -63,14 +62,25 @@ except ImportError:
63
62
  # The general intent is:
64
63
  # - Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE),
65
64
  # - prefer ECDHE over DHE for better performance,
66
- # - prefer any AES-GCM over any AES-CBC for better performance and security,
67
- # - use 3DES as fallback which is secure but slow,
65
+ # - prefer any AES-GCM and ChaCha20 over any AES-CBC for better performance and
66
+ # security,
67
+ # - prefer AES-GCM over ChaCha20 because hardware-accelerated AES is common,
68
68
  # - disable NULL authentication, MD5 MACs and DSS for security reasons.
69
- DEFAULT_CIPHERS = (
70
- 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:'
71
- 'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:'
72
- '!eNULL:!MD5'
73
- )
69
+ DEFAULT_CIPHERS = ':'.join([
70
+ 'ECDH+AESGCM',
71
+ 'ECDH+CHACHA20',
72
+ 'DH+AESGCM',
73
+ 'DH+CHACHA20',
74
+ 'ECDH+AES256',
75
+ 'DH+AES256',
76
+ 'ECDH+AES128',
77
+ 'DH+AES',
78
+ 'RSA+AESGCM',
79
+ 'RSA+AES',
80
+ '!aNULL',
81
+ '!eNULL',
82
+ '!MD5',
83
+ ])
74
84
 
75
85
  try:
76
86
  from ssl import SSLContext # Modern SSL?
@@ -117,8 +127,8 @@ except ImportError:
117
127
  'urllib3 from configuring SSL appropriately and may cause '
118
128
  'certain SSL connections to fail. You can upgrade to a newer '
119
129
  'version of Python to solve this. For more information, see '
120
- 'https://urllib3.readthedocs.io/en/latest/security.html'
121
- '#insecureplatformwarning.',
130
+ 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html'
131
+ '#ssl-warnings',
122
132
  InsecurePlatformWarning
123
133
  )
124
134
  kwargs = {
@@ -287,6 +297,9 @@ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
287
297
  """
288
298
  context = ssl_context
289
299
  if context is None:
300
+ # Note: This branch of code and all the variables in it are no longer
301
+ # used by urllib3 itself. We should consider deprecating and removing
302
+ # this code.
290
303
  context = create_urllib3_context(ssl_version, cert_reqs,
291
304
  ciphers=ciphers)
292
305
 
@@ -301,6 +314,9 @@ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
301
314
  if e.errno == errno.ENOENT:
302
315
  raise SSLError(e)
303
316
  raise
317
+ elif getattr(context, 'load_default_certs', None) is not None:
318
+ # try to load OS default certs; works well on Windows (require Python3.4+)
319
+ context.load_default_certs()
304
320
 
305
321
  if certfile:
306
322
  context.load_cert_chain(certfile, keyfile)
@@ -313,8 +329,8 @@ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
313
329
  'This may cause the server to present an incorrect TLS '
314
330
  'certificate, which can cause validation failures. You can upgrade to '
315
331
  'a newer version of Python to solve this. For more information, see '
316
- 'https://urllib3.readthedocs.io/en/latest/security.html'
317
- '#snimissingwarning.',
332
+ 'https://urllib3.readthedocs.io/en/latest/advanced-usage.html'
333
+ '#ssl-warnings',
318
334
  SNIMissingWarning
319
335
  )
320
336
  return context.wrap_socket(sock)
@@ -111,8 +111,8 @@ class Timeout(object):
111
111
  :param name: The name of the timeout attribute to validate. This is
112
112
  used to specify in error messages.
113
113
  :return: The validated and casted version of the given value.
114
- :raises ValueError: If the type is not an integer or a float, or if it
115
- is a numeric value less than zero.
114
+ :raises ValueError: If it is a numeric value less than or equal to
115
+ zero, or the type is not an integer, float, or None.
116
116
  """
117
117
  if value is _Default:
118
118
  return cls.DEFAULT_TIMEOUT
@@ -120,20 +120,23 @@ class Timeout(object):
120
120
  if value is None or value is cls.DEFAULT_TIMEOUT:
121
121
  return value
122
122
 
123
+ if isinstance(value, bool):
124
+ raise ValueError("Timeout cannot be a boolean value. It must "
125
+ "be an int, float or None.")
123
126
  try:
124
127
  float(value)
125
128
  except (TypeError, ValueError):
126
129
  raise ValueError("Timeout value %s was %s, but it must be an "
127
- "int or float." % (name, value))
130
+ "int, float or None." % (name, value))
128
131
 
129
132
  try:
130
- if value < 0:
133
+ if value <= 0:
131
134
  raise ValueError("Attempted to set %s timeout to %s, but the "
132
135
  "timeout cannot be set to a value less "
133
- "than 0." % (name, value))
136
+ "than or equal to 0." % (name, value))
134
137
  except TypeError: # Python 3
135
138
  raise ValueError("Timeout value %s was %s, but it must be an "
136
- "int or float." % (name, value))
139
+ "int, float or None." % (name, value))
137
140
 
138
141
  return value
139
142
 
@@ -10,14 +10,19 @@ url_attrs = ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment']
10
10
  class Url(namedtuple('Url', url_attrs)):
11
11
  """
12
12
  Datastructure for representing an HTTP URL. Used as a return value for
13
- :func:`parse_url`.
13
+ :func:`parse_url`. Both the scheme and host are normalized as they are
14
+ both case-insensitive according to RFC 3986.
14
15
  """
15
- slots = ()
16
+ __slots__ = ()
16
17
 
17
18
  def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None,
18
19
  query=None, fragment=None):
19
20
  if path and not path.startswith('/'):
20
21
  path = '/' + path
22
+ if scheme:
23
+ scheme = scheme.lower()
24
+ if host:
25
+ host = host.lower()
21
26
  return super(Url, cls).__new__(cls, scheme, auth, host, port, path,
22
27
  query, fragment)
23
28
 
@@ -184,10 +189,14 @@ def parse_url(url):
184
189
  host = _host
185
190
 
186
191
  if port:
187
- # If given, ports must be integers.
192
+ # If given, ports must be integers. No whitespace, no plus or
193
+ # minus prefixes, no non-integer digits such as ^2 (superscript).
188
194
  if not port.isdigit():
189
195
  raise LocationParseError(url)
190
- port = int(port)
196
+ try:
197
+ port = int(port)
198
+ except ValueError:
199
+ raise LocationParseError(url)
191
200
  else:
192
201
  # Blank ports are cool, too. (rfc3986#section-3.2.3)
193
202
  port = None
@@ -211,7 +220,7 @@ def parse_url(url):
211
220
 
212
221
  def get_host(url):
213
222
  """
214
- Deprecated. Use :func:`.parse_url` instead.
223
+ Deprecated. Use :func:`parse_url` instead.
215
224
  """
216
225
  p = parse_url(url)
217
226
  return p.scheme or 'http', p.hostname, p.port
@@ -17,7 +17,8 @@ from .cookies import (
17
17
  cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies)
18
18
  from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT
19
19
  from .hooks import default_hooks, dispatch_hook
20
- from .utils import to_key_val_list, default_headers, to_native_string
20
+ from ._internal_utils import to_native_string
21
+ from .utils import to_key_val_list, default_headers
21
22
  from .exceptions import (
22
23
  TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError)
23
24
  from .packages.urllib3._collections import RecentlyUsedContainer
@@ -27,7 +28,7 @@ from .adapters import HTTPAdapter
27
28
 
28
29
  from .utils import (
29
30
  requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies,
30
- get_auth_from_url
31
+ get_auth_from_url, rewind_body
31
32
  )
32
33
 
33
34
  from .status_codes import codes
@@ -156,13 +157,25 @@ class SessionRedirectMixin(object):
156
157
  # in the new request. Because we've mutated our copied prepared
157
158
  # request, use the old one that we haven't yet touched.
158
159
  extract_cookies_to_jar(prepared_request._cookies, req, resp.raw)
159
- prepared_request._cookies.update(self.cookies)
160
+ merge_cookies(prepared_request._cookies, self.cookies)
160
161
  prepared_request.prepare_cookies(prepared_request._cookies)
161
162
 
162
163
  # Rebuild auth and proxy information.
163
164
  proxies = self.rebuild_proxies(prepared_request, proxies)
164
165
  self.rebuild_auth(prepared_request, resp)
165
166
 
167
+ # A failed tell() sets `_body_position` to `object()`. This non-None
168
+ # value ensures `rewindable` will be True, allowing us to raise an
169
+ # UnrewindableBodyError, instead of hanging the connection.
170
+ rewindable = (
171
+ prepared_request._body_position is not None and
172
+ ('Content-Length' in headers or 'Transfer-Encoding' in headers)
173
+ )
174
+
175
+ # Attempt to rewind consumed file-like object.
176
+ if rewindable:
177
+ rewind_body(prepared_request)
178
+
166
179
  # Override the original request.
167
180
  req = prepared_request
168
181
 
@@ -226,7 +239,7 @@ class SessionRedirectMixin(object):
226
239
  if self.trust_env and not should_bypass_proxies(url):
227
240
  environ_proxies = get_environ_proxies(url)
228
241
 
229
- proxy = environ_proxies.get('all', environ_proxies.get(scheme))
242
+ proxy = environ_proxies.get(scheme, environ_proxies.get('all'))
230
243
 
231
244
  if proxy:
232
245
  new_proxies.setdefault(scheme, proxy)
@@ -322,7 +335,7 @@ class Session(SessionRedirectMixin):
322
335
  #: SSL Verification default.
323
336
  self.verify = True
324
337
 
325
- #: SSL certificate default.
338
+ #: SSL client certificate default.
326
339
  self.cert = None
327
340
 
328
341
  #: Maximum number of redirects allowed. If the request exceeds this