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
@@ -130,7 +130,7 @@ class RequestField(object):
130
130
  iterable = header_parts.items()
131
131
 
132
132
  for name, value in iterable:
133
- if value:
133
+ if value is not None:
134
134
  parts.append(self._render_part(name, value))
135
135
 
136
136
  return '; '.join(parts)
@@ -13,7 +13,7 @@ writer = codecs.lookup('utf-8')[3]
13
13
 
14
14
  def choose_boundary():
15
15
  """
16
- Our embarassingly-simple replacement for mimetools.choose_boundary.
16
+ Our embarrassingly-simple replacement for mimetools.choose_boundary.
17
17
  """
18
18
  return uuid4().hex
19
19
 
@@ -0,0 +1,53 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ backports.makefile
4
+ ~~~~~~~~~~~~~~~~~~
5
+
6
+ Backports the Python 3 ``socket.makefile`` method for use with anything that
7
+ wants to create a "fake" socket object.
8
+ """
9
+ import io
10
+
11
+ from socket import SocketIO
12
+
13
+
14
+ def backport_makefile(self, mode="r", buffering=None, encoding=None,
15
+ errors=None, newline=None):
16
+ """
17
+ Backport of ``socket.makefile`` from Python 3.5.
18
+ """
19
+ if not set(mode) <= set(["r", "w", "b"]):
20
+ raise ValueError(
21
+ "invalid mode %r (only r, w, b allowed)" % (mode,)
22
+ )
23
+ writing = "w" in mode
24
+ reading = "r" in mode or not writing
25
+ assert reading or writing
26
+ binary = "b" in mode
27
+ rawmode = ""
28
+ if reading:
29
+ rawmode += "r"
30
+ if writing:
31
+ rawmode += "w"
32
+ raw = SocketIO(self, rawmode)
33
+ self._makefile_refs += 1
34
+ if buffering is None:
35
+ buffering = -1
36
+ if buffering < 0:
37
+ buffering = io.DEFAULT_BUFFER_SIZE
38
+ if buffering == 0:
39
+ if not binary:
40
+ raise ValueError("unbuffered streams must be binary")
41
+ return raw
42
+ if reading and writing:
43
+ buffer = io.BufferedRWPair(raw, raw, buffering)
44
+ elif reading:
45
+ buffer = io.BufferedReader(raw, buffering)
46
+ else:
47
+ assert writing
48
+ buffer = io.BufferedWriter(raw, buffering)
49
+ if binary:
50
+ return buffer
51
+ text = io.TextIOWrapper(buffer, encoding, errors, newline)
52
+ text.mode = mode
53
+ return text
@@ -1,5 +1,11 @@
1
+ import sys
2
+
1
3
  try:
2
- # Python 3.2+
4
+ # Our match_hostname function is the same as 3.5's, so we only want to
5
+ # import the match_hostname function if it's at least that good.
6
+ if sys.version_info < (3, 5):
7
+ raise ImportError("Fallback to vendored code")
8
+
3
9
  from ssl import CertificateError, match_hostname
4
10
  except ImportError:
5
11
  try:
@@ -4,8 +4,20 @@
4
4
  # stdlib. http://docs.python.org/3/license.html
5
5
 
6
6
  import re
7
+ import sys
8
+
9
+ # ipaddress has been backported to 2.6+ in pypi. If it is installed on the
10
+ # system, use it to handle IPAddress ServerAltnames (this was added in
11
+ # python-3.5) otherwise only do DNS matching. This allows
12
+ # backports.ssl_match_hostname to continue to be used all the way back to
13
+ # python-2.4.
14
+ try:
15
+ import ipaddress
16
+ except ImportError:
17
+ ipaddress = None
18
+
19
+ __version__ = '3.5.0.1'
7
20
 
8
- __version__ = '3.4.0.2'
9
21
 
10
22
  class CertificateError(ValueError):
11
23
  pass
@@ -64,6 +76,23 @@ def _dnsname_match(dn, hostname, max_wildcards=1):
64
76
  return pat.match(hostname)
65
77
 
66
78
 
79
+ def _to_unicode(obj):
80
+ if isinstance(obj, str) and sys.version_info < (3,):
81
+ obj = unicode(obj, encoding='ascii', errors='strict')
82
+ return obj
83
+
84
+ def _ipaddress_match(ipname, host_ip):
85
+ """Exact matching of IP addresses.
86
+
87
+ RFC 6125 explicitly doesn't define an algorithm for this
88
+ (section 1.7.2 - "Out of Scope").
89
+ """
90
+ # OpenSSL may add a trailing newline to a subjectAltName's IP address
91
+ # Divergence from upstream: ipaddress can't handle byte str
92
+ ip = ipaddress.ip_address(_to_unicode(ipname).rstrip())
93
+ return ip == host_ip
94
+
95
+
67
96
  def match_hostname(cert, hostname):
68
97
  """Verify that *cert* (in decoded format as returned by
69
98
  SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125
@@ -73,12 +102,35 @@ def match_hostname(cert, hostname):
73
102
  returns nothing.
74
103
  """
75
104
  if not cert:
76
- raise ValueError("empty or no certificate")
105
+ raise ValueError("empty or no certificate, match_hostname needs a "
106
+ "SSL socket or SSL context with either "
107
+ "CERT_OPTIONAL or CERT_REQUIRED")
108
+ try:
109
+ # Divergence from upstream: ipaddress can't handle byte str
110
+ host_ip = ipaddress.ip_address(_to_unicode(hostname))
111
+ except ValueError:
112
+ # Not an IP address (common case)
113
+ host_ip = None
114
+ except UnicodeError:
115
+ # Divergence from upstream: Have to deal with ipaddress not taking
116
+ # byte strings. addresses should be all ascii, so we consider it not
117
+ # an ipaddress in this case
118
+ host_ip = None
119
+ except AttributeError:
120
+ # Divergence from upstream: Make ipaddress library optional
121
+ if ipaddress is None:
122
+ host_ip = None
123
+ else:
124
+ raise
77
125
  dnsnames = []
78
126
  san = cert.get('subjectAltName', ())
79
127
  for key, value in san:
80
128
  if key == 'DNS':
81
- if _dnsname_match(value, hostname):
129
+ if host_ip is None and _dnsname_match(value, hostname):
130
+ return
131
+ dnsnames.append(value)
132
+ elif key == 'IP Address':
133
+ if host_ip is not None and _ipaddress_match(value, host_ip):
82
134
  return
83
135
  dnsnames.append(value)
84
136
  if not dnsnames:
@@ -3,15 +3,11 @@ import collections
3
3
  import functools
4
4
  import logging
5
5
 
6
- try: # Python 3
7
- from urllib.parse import urljoin
8
- except ImportError:
9
- from urlparse import urljoin
10
-
11
6
  from ._collections import RecentlyUsedContainer
12
7
  from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool
13
8
  from .connectionpool import port_by_scheme
14
9
  from .exceptions import LocationValueError, MaxRetryError, ProxySchemeUnknown
10
+ from .packages.six.moves.urllib.parse import urljoin
15
11
  from .request import RequestMethods
16
12
  from .util.url import parse_url
17
13
  from .util.retry import Retry
@@ -23,7 +19,7 @@ __all__ = ['PoolManager', 'ProxyManager', 'proxy_from_url']
23
19
  log = logging.getLogger(__name__)
24
20
 
25
21
  SSL_KEYWORDS = ('key_file', 'cert_file', 'cert_reqs', 'ca_certs',
26
- 'ssl_version', 'ca_cert_dir')
22
+ 'ssl_version', 'ca_cert_dir', 'ssl_context')
27
23
 
28
24
  # The base fields to use when determining what pool to get a connection from;
29
25
  # these do not rely on the ``connection_pool_kw`` and can be determined by the
@@ -1,10 +1,7 @@
1
1
  from __future__ import absolute_import
2
- try:
3
- from urllib.parse import urlencode
4
- except ImportError:
5
- from urllib import urlencode
6
2
 
7
3
  from .filepost import encode_multipart_formdata
4
+ from .packages.six.moves.urllib.parse import urlencode
8
5
 
9
6
 
10
7
  __all__ = ['RequestMethods']
@@ -2,18 +2,22 @@ from __future__ import absolute_import
2
2
  from contextlib import contextmanager
3
3
  import zlib
4
4
  import io
5
+ import logging
5
6
  from socket import timeout as SocketTimeout
6
7
  from socket import error as SocketError
7
8
 
8
9
  from ._collections import HTTPHeaderDict
9
10
  from .exceptions import (
10
- ProtocolError, DecodeError, ReadTimeoutError, ResponseNotChunked
11
+ BodyNotHttplibCompatible, ProtocolError, DecodeError, ReadTimeoutError,
12
+ ResponseNotChunked, IncompleteRead, InvalidHeader
11
13
  )
12
14
  from .packages.six import string_types as basestring, binary_type, PY3
13
15
  from .packages.six.moves import http_client as httplib
14
16
  from .connection import HTTPException, BaseSSLError
15
17
  from .util.response import is_fp_closed, is_response_to_head
16
18
 
19
+ log = logging.getLogger(__name__)
20
+
17
21
 
18
22
  class DeflateDecoder(object):
19
23
 
@@ -89,6 +93,14 @@ class HTTPResponse(io.IOBase):
89
93
  When this HTTPResponse wrapper is generated from an httplib.HTTPResponse
90
94
  object, it's convenient to include the original for debug purposes. It's
91
95
  otherwise unused.
96
+
97
+ :param retries:
98
+ The retries contains the last :class:`~urllib3.util.retry.Retry` that
99
+ was used during the request.
100
+
101
+ :param enforce_content_length:
102
+ Enforce content length checking. Body returned by server must match
103
+ value of Content-Length header, if present. Otherwise, raise error.
92
104
  """
93
105
 
94
106
  CONTENT_DECODERS = ['gzip', 'deflate']
@@ -96,7 +108,8 @@ class HTTPResponse(io.IOBase):
96
108
 
97
109
  def __init__(self, body='', headers=None, status=0, version=0, reason=None,
98
110
  strict=0, preload_content=True, decode_content=True,
99
- original_response=None, pool=None, connection=None):
111
+ original_response=None, pool=None, connection=None,
112
+ retries=None, enforce_content_length=False, request_method=None):
100
113
 
101
114
  if isinstance(headers, HTTPHeaderDict):
102
115
  self.headers = headers
@@ -107,6 +120,8 @@ class HTTPResponse(io.IOBase):
107
120
  self.reason = reason
108
121
  self.strict = strict
109
122
  self.decode_content = decode_content
123
+ self.retries = retries
124
+ self.enforce_content_length = enforce_content_length
110
125
 
111
126
  self._decoder = None
112
127
  self._body = None
@@ -132,6 +147,9 @@ class HTTPResponse(io.IOBase):
132
147
  if "chunked" in encodings:
133
148
  self.chunked = True
134
149
 
150
+ # Determine length of response
151
+ self.length_remaining = self._init_length(request_method)
152
+
135
153
  # If requested, preload the body.
136
154
  if preload_content and not self._body:
137
155
  self._body = self.read(decode_content=decode_content)
@@ -177,9 +195,57 @@ class HTTPResponse(io.IOBase):
177
195
  """
178
196
  return self._fp_bytes_read
179
197
 
198
+ def _init_length(self, request_method):
199
+ """
200
+ Set initial length value for Response content if available.
201
+ """
202
+ length = self.headers.get('content-length')
203
+
204
+ if length is not None and self.chunked:
205
+ # This Response will fail with an IncompleteRead if it can't be
206
+ # received as chunked. This method falls back to attempt reading
207
+ # the response before raising an exception.
208
+ log.warning("Received response with both Content-Length and "
209
+ "Transfer-Encoding set. This is expressly forbidden "
210
+ "by RFC 7230 sec 3.3.2. Ignoring Content-Length and "
211
+ "attempting to process response as Transfer-Encoding: "
212
+ "chunked.")
213
+ return None
214
+
215
+ elif length is not None:
216
+ try:
217
+ # RFC 7230 section 3.3.2 specifies multiple content lengths can
218
+ # be sent in a single Content-Length header
219
+ # (e.g. Content-Length: 42, 42). This line ensures the values
220
+ # are all valid ints and that as long as the `set` length is 1,
221
+ # all values are the same. Otherwise, the header is invalid.
222
+ lengths = set([int(val) for val in length.split(',')])
223
+ if len(lengths) > 1:
224
+ raise InvalidHeader("Content-Length contained multiple "
225
+ "unmatching values (%s)" % length)
226
+ length = lengths.pop()
227
+ except ValueError:
228
+ length = None
229
+ else:
230
+ if length < 0:
231
+ length = None
232
+
233
+ # Convert status to int for comparison
234
+ # In some cases, httplib returns a status of "_UNKNOWN"
235
+ try:
236
+ status = int(self.status)
237
+ except ValueError:
238
+ status = 0
239
+
240
+ # Check for responses that shouldn't include a body
241
+ if status in (204, 304) or 100 <= status < 200 or request_method == 'HEAD':
242
+ length = 0
243
+
244
+ return length
245
+
180
246
  def _init_decoder(self):
181
247
  """
182
- Set-up the _decoder attribute if necessar.
248
+ Set-up the _decoder attribute if necessary.
183
249
  """
184
250
  # Note: content-encoding value should be case-insensitive, per RFC 7230
185
251
  # Section 3.2
@@ -322,9 +388,18 @@ class HTTPResponse(io.IOBase):
322
388
  # no harm in redundantly calling close.
323
389
  self._fp.close()
324
390
  flush_decoder = True
391
+ if self.enforce_content_length and self.length_remaining not in (0, None):
392
+ # This is an edge case that httplib failed to cover due
393
+ # to concerns of backward compatibility. We're
394
+ # addressing it here to make sure IncompleteRead is
395
+ # raised during streaming, so all calls with incorrect
396
+ # Content-Length are caught.
397
+ raise IncompleteRead(self._fp_bytes_read, self.length_remaining)
325
398
 
326
399
  if data:
327
400
  self._fp_bytes_read += len(data)
401
+ if self.length_remaining is not None:
402
+ self.length_remaining -= len(data)
328
403
 
329
404
  data = self._decode(data, decode_content, flush_decoder)
330
405
 
@@ -349,7 +424,7 @@ class HTTPResponse(io.IOBase):
349
424
  If True, will attempt to decode the body based on the
350
425
  'content-encoding' header.
351
426
  """
352
- if self.chunked:
427
+ if self.chunked and self.supports_chunked_reads():
353
428
  for line in self.read_chunked(amt, decode_content=decode_content):
354
429
  yield line
355
430
  else:
@@ -407,10 +482,10 @@ class HTTPResponse(io.IOBase):
407
482
  def closed(self):
408
483
  if self._fp is None:
409
484
  return True
485
+ elif hasattr(self._fp, 'isclosed'):
486
+ return self._fp.isclosed()
410
487
  elif hasattr(self._fp, 'closed'):
411
488
  return self._fp.closed
412
- elif hasattr(self._fp, 'isclosed'): # Python 2
413
- return self._fp.isclosed()
414
489
  else:
415
490
  return True
416
491
 
@@ -440,6 +515,15 @@ class HTTPResponse(io.IOBase):
440
515
  b[:len(temp)] = temp
441
516
  return len(temp)
442
517
 
518
+ def supports_chunked_reads(self):
519
+ """
520
+ Checks if the underlying file-like object looks like a
521
+ httplib.HTTPResponse object. We do this by testing for the fp
522
+ attribute. If it is present we assume it returns raw chunks as
523
+ processed by read_chunked().
524
+ """
525
+ return hasattr(self._fp, 'fp')
526
+
443
527
  def _update_chunk_length(self):
444
528
  # First, we'll figure out length of a chunk and then
445
529
  # we'll try to read it from socket.
@@ -491,6 +575,10 @@ class HTTPResponse(io.IOBase):
491
575
  raise ResponseNotChunked(
492
576
  "Response is not chunked. "
493
577
  "Header 'transfer-encoding: chunked' is missing.")
578
+ if not self.supports_chunked_reads():
579
+ raise BodyNotHttplibCompatible(
580
+ "Body should be httplib.HTTPResponse like. "
581
+ "It should have have an fp attribute which returns raw chunks.")
494
582
 
495
583
  # Don't bother reading the body of a HEAD request.
496
584
  if self._original_response and is_response_to_head(self._original_response):
@@ -141,4 +141,5 @@ def _has_ipv6(host):
141
141
  sock.close()
142
142
  return has_ipv6
143
143
 
144
+
144
145
  HAS_IPV6 = _has_ipv6('::1')
@@ -12,6 +12,13 @@ def is_fp_closed(obj):
12
12
  The file-like object to check.
13
13
  """
14
14
 
15
+ try:
16
+ # Check `isclosed()` first, in case Python3 doesn't set `closed`.
17
+ # GH Issue #928
18
+ return obj.isclosed()
19
+ except AttributeError:
20
+ pass
21
+
15
22
  try:
16
23
  # Check via the official file-like-object way.
17
24
  return obj.closed