Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: third_party/requests/packages/urllib3/util.py

Issue 24076010: Add 'requests' library to third_party. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/swarm_client
Patch Set: Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # urllib3/util.py
2 # Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt)
3 #
4 # This module is part of urllib3 and is released under
5 # the MIT License: http://www.opensource.org/licenses/mit-license.php
6
7
8 from base64 import b64encode
9 from collections import namedtuple
10 from socket import error as SocketError
11 from hashlib import md5, sha1
12 from binascii import hexlify, unhexlify
13
14 try:
15 from select import poll, POLLIN
16 except ImportError: # `poll` doesn't exist on OSX and other platforms
17 poll = False
18 try:
19 from select import select
20 except ImportError: # `select` doesn't exist on AppEngine.
21 select = False
22
23 try: # Test for SSL features
24 SSLContext = None
25 HAS_SNI = False
26
27 import ssl
28 from ssl import wrap_socket, CERT_NONE, PROTOCOL_SSLv23
29 from ssl import SSLContext # Modern SSL?
30 from ssl import HAS_SNI # Has SNI?
31 except ImportError:
32 pass
33
34
35 from .packages import six
36 from .exceptions import LocationParseError, SSLError
37
38
39 class Url(namedtuple('Url', ['scheme', 'auth', 'host', 'port', 'path', 'query', 'fragment'])):
40 """
41 Datastructure for representing an HTTP URL. Used as a return value for
42 :func:`parse_url`.
43 """
44 slots = ()
45
46 def __new__(cls, scheme=None, auth=None, host=None, port=None, path=None, qu ery=None, fragment=None):
47 return super(Url, cls).__new__(cls, scheme, auth, host, port, path, quer y, fragment)
48
49 @property
50 def hostname(self):
51 """For backwards-compatibility with urlparse. We're nice like that."""
52 return self.host
53
54 @property
55 def request_uri(self):
56 """Absolute path including the query string."""
57 uri = self.path or '/'
58
59 if self.query is not None:
60 uri += '?' + self.query
61
62 return uri
63
64
65 def split_first(s, delims):
66 """
67 Given a string and an iterable of delimiters, split on the first found
68 delimiter. Return two split parts and the matched delimiter.
69
70 If not found, then the first part is the full input string.
71
72 Example: ::
73
74 >>> split_first('foo/bar?baz', '?/=')
75 ('foo', 'bar?baz', '/')
76 >>> split_first('foo/bar?baz', '123')
77 ('foo/bar?baz', '', None)
78
79 Scales linearly with number of delims. Not ideal for large number of delims.
80 """
81 min_idx = None
82 min_delim = None
83 for d in delims:
84 idx = s.find(d)
85 if idx < 0:
86 continue
87
88 if min_idx is None or idx < min_idx:
89 min_idx = idx
90 min_delim = d
91
92 if min_idx is None or min_idx < 0:
93 return s, '', None
94
95 return s[:min_idx], s[min_idx+1:], min_delim
96
97
98 def parse_url(url):
99 """
100 Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is
101 performed to parse incomplete urls. Fields not provided will be None.
102
103 Partly backwards-compatible with :mod:`urlparse`.
104
105 Example: ::
106
107 >>> parse_url('http://google.com/mail/')
108 Url(scheme='http', host='google.com', port=None, path='/', ...)
109 >>> parse_url('google.com:80')
110 Url(scheme=None, host='google.com', port=80, path=None, ...)
111 >>> parse_url('/foo?bar')
112 Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...)
113 """
114
115 # While this code has overlap with stdlib's urlparse, it is much
116 # simplified for our needs and less annoying.
117 # Additionally, this imeplementations does silly things to be optimal
118 # on CPython.
119
120 scheme = None
121 auth = None
122 host = None
123 port = None
124 path = None
125 fragment = None
126 query = None
127
128 # Scheme
129 if '://' in url:
130 scheme, url = url.split('://', 1)
131
132 # Find the earliest Authority Terminator
133 # (http://tools.ietf.org/html/rfc3986#section-3.2)
134 url, path_, delim = split_first(url, ['/', '?', '#'])
135
136 if delim:
137 # Reassemble the path
138 path = delim + path_
139
140 # Auth
141 if '@' in url:
142 auth, url = url.split('@', 1)
143
144 # IPv6
145 if url and url[0] == '[':
146 host, url = url[1:].split(']', 1)
147
148 # Port
149 if ':' in url:
150 _host, port = url.split(':', 1)
151
152 if not host:
153 host = _host
154
155 if not port.isdigit():
156 raise LocationParseError("Failed to parse: %s" % url)
157
158 port = int(port)
159
160 elif not host and url:
161 host = url
162
163 if not path:
164 return Url(scheme, auth, host, port, path, query, fragment)
165
166 # Fragment
167 if '#' in path:
168 path, fragment = path.split('#', 1)
169
170 # Query
171 if '?' in path:
172 path, query = path.split('?', 1)
173
174 return Url(scheme, auth, host, port, path, query, fragment)
175
176
177 def get_host(url):
178 """
179 Deprecated. Use :func:`.parse_url` instead.
180 """
181 p = parse_url(url)
182 return p.scheme or 'http', p.hostname, p.port
183
184
185 def make_headers(keep_alive=None, accept_encoding=None, user_agent=None,
186 basic_auth=None):
187 """
188 Shortcuts for generating request headers.
189
190 :param keep_alive:
191 If ``True``, adds 'connection: keep-alive' header.
192
193 :param accept_encoding:
194 Can be a boolean, list, or string.
195 ``True`` translates to 'gzip,deflate'.
196 List will get joined by comma.
197 String will be used as provided.
198
199 :param user_agent:
200 String representing the user-agent you want, such as
201 "python-urllib3/0.6"
202
203 :param basic_auth:
204 Colon-separated username:password string for 'authorization: basic ...'
205 auth header.
206
207 Example: ::
208
209 >>> make_headers(keep_alive=True, user_agent="Batman/1.0")
210 {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'}
211 >>> make_headers(accept_encoding=True)
212 {'accept-encoding': 'gzip,deflate'}
213 """
214 headers = {}
215 if accept_encoding:
216 if isinstance(accept_encoding, str):
217 pass
218 elif isinstance(accept_encoding, list):
219 accept_encoding = ','.join(accept_encoding)
220 else:
221 accept_encoding = 'gzip,deflate'
222 headers['accept-encoding'] = accept_encoding
223
224 if user_agent:
225 headers['user-agent'] = user_agent
226
227 if keep_alive:
228 headers['connection'] = 'keep-alive'
229
230 if basic_auth:
231 headers['authorization'] = 'Basic ' + \
232 b64encode(six.b(basic_auth)).decode('utf-8')
233
234 return headers
235
236
237 def is_connection_dropped(conn): # Platform-specific
238 """
239 Returns True if the connection is dropped and should be closed.
240
241 :param conn:
242 :class:`httplib.HTTPConnection` object.
243
244 Note: For platforms like AppEngine, this will always return ``False`` to
245 let the platform handle connection recycling transparently for us.
246 """
247 sock = getattr(conn, 'sock', False)
248 if not sock: # Platform-specific: AppEngine
249 return False
250
251 if not poll:
252 if not select: # Platform-specific: AppEngine
253 return False
254
255 try:
256 return select([sock], [], [], 0.0)[0]
257 except SocketError:
258 return True
259
260 # This version is better on platforms that support it.
261 p = poll()
262 p.register(sock, POLLIN)
263 for (fno, ev) in p.poll(0.0):
264 if fno == sock.fileno():
265 # Either data is buffered (bad), or the connection is dropped.
266 return True
267
268
269 def resolve_cert_reqs(candidate):
270 """
271 Resolves the argument to a numeric constant, which can be passed to
272 the wrap_socket function/method from the ssl module.
273 Defaults to :data:`ssl.CERT_NONE`.
274 If given a string it is assumed to be the name of the constant in the
275 :mod:`ssl` module or its abbrevation.
276 (So you can specify `REQUIRED` instead of `CERT_REQUIRED`.
277 If it's neither `None` nor a string we assume it is already the numeric
278 constant which can directly be passed to wrap_socket.
279 """
280 if candidate is None:
281 return CERT_NONE
282
283 if isinstance(candidate, str):
284 res = getattr(ssl, candidate, None)
285 if res is None:
286 res = getattr(ssl, 'CERT_' + candidate)
287 return res
288
289 return candidate
290
291
292 def resolve_ssl_version(candidate):
293 """
294 like resolve_cert_reqs
295 """
296 if candidate is None:
297 return PROTOCOL_SSLv23
298
299 if isinstance(candidate, str):
300 res = getattr(ssl, candidate, None)
301 if res is None:
302 res = getattr(ssl, 'PROTOCOL_' + candidate)
303 return res
304
305 return candidate
306
307
308 def assert_fingerprint(cert, fingerprint):
309 """
310 Checks if given fingerprint matches the supplied certificate.
311
312 :param cert:
313 Certificate as bytes object.
314 :param fingerprint:
315 Fingerprint as string of hexdigits, can be interspersed by colons.
316 """
317
318 # Maps the length of a digest to a possible hash function producing
319 # this digest.
320 hashfunc_map = {
321 16: md5,
322 20: sha1
323 }
324
325 fingerprint = fingerprint.replace(':', '').lower()
326
327 digest_length, rest = divmod(len(fingerprint), 2)
328
329 if rest or digest_length not in hashfunc_map:
330 raise SSLError('Fingerprint is of invalid length.')
331
332 # We need encode() here for py32; works on py2 and p33.
333 fingerprint_bytes = unhexlify(fingerprint.encode())
334
335 hashfunc = hashfunc_map[digest_length]
336
337 cert_digest = hashfunc(cert).digest()
338
339 if not cert_digest == fingerprint_bytes:
340 raise SSLError('Fingerprints did not match. Expected "{0}", got "{1}".'
341 .format(hexlify(fingerprint_bytes),
342 hexlify(cert_digest)))
343
344
345 if SSLContext is not None: # Python 3.2+
346 def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
347 ca_certs=None, server_hostname=None,
348 ssl_version=None):
349 """
350 All arguments except `server_hostname` have the same meaning as for
351 :func:`ssl.wrap_socket`
352
353 :param server_hostname:
354 Hostname of the expected certificate
355 """
356 context = SSLContext(ssl_version)
357 context.verify_mode = cert_reqs
358 if ca_certs:
359 try:
360 context.load_verify_locations(ca_certs)
361 # Py32 raises IOError
362 # Py33 raises FileNotFoundError
363 except Exception as e: # Reraise as SSLError
364 raise SSLError(e)
365 if certfile:
366 # FIXME: This block needs a test.
367 context.load_cert_chain(certfile, keyfile)
368 if HAS_SNI: # Platform-specific: OpenSSL with enabled SNI
369 return context.wrap_socket(sock, server_hostname=server_hostname)
370 return context.wrap_socket(sock)
371
372 else: # Python 3.1 and earlier
373 def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None,
374 ca_certs=None, server_hostname=None,
375 ssl_version=None):
376 return wrap_socket(sock, keyfile=keyfile, certfile=certfile,
377 ca_certs=ca_certs, cert_reqs=cert_reqs,
378 ssl_version=ssl_version)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698