1 # -*- coding: utf-8 -*-
7 This module contains the primary objects that power Requests.
14 from io import BytesIO, UnsupportedOperation
15 from .hooks import default_hooks
16 from .structures import CaseInsensitiveDict
18 from .auth import HTTPBasicAuth
19 from .cookies import cookiejar_from_dict, get_cookie_header
20 from .packages.urllib3.fields import RequestField
21 from .packages.urllib3.filepost import encode_multipart_formdata
22 from .packages.urllib3.util import parse_url
23 from .exceptions import (
24 HTTPError, RequestException, MissingSchema, InvalidURL,
27 guess_filename, get_auth_from_url, requote_uri,
28 stream_decode_response_unicode, to_key_val_list, parse_header_links,
29 iter_slices, guess_json_utf, super_len, to_native_string)
31 cookielib, urlunparse, urlsplit, urlencode, str, bytes, StringIO,
32 is_py2, chardet, json, builtin_str, basestring, IncompleteRead)
34 CONTENT_CHUNK_SIZE = 10 * 1024
37 log = logging.getLogger(__name__)
40 class RequestEncodingMixin(object):
43 """Build the path URL to use."""
47 p = urlsplit(self.url)
63 def _encode_params(data):
64 """Encode parameters in a piece of data.
66 Will successfully encode parameters when passed as a dict or a list of
67 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
68 if parameters are supplied as a dict.
71 if isinstance(data, (str, bytes)):
73 elif hasattr(data, 'read'):
75 elif hasattr(data, '__iter__'):
77 for k, vs in to_key_val_list(data):
78 if isinstance(vs, basestring) or not hasattr(vs, '__iter__'):
83 (k.encode('utf-8') if isinstance(k, str) else k,
84 v.encode('utf-8') if isinstance(v, str) else v))
85 return urlencode(result, doseq=True)
90 def _encode_files(files, data):
91 """Build the body for a multipart/form-data request.
93 Will successfully encode files when passed as a dict or a list of
94 2-tuples. Order is retained if data is a list of 2-tuples but arbitrary
95 if parameters are supplied as a dict.
99 raise ValueError("Files must be provided.")
100 elif isinstance(data, basestring):
101 raise ValueError("Data must not be a string.")
104 fields = to_key_val_list(data or {})
105 files = to_key_val_list(files or {})
107 for field, val in fields:
108 if isinstance(val, basestring) or not hasattr(val, '__iter__'):
112 # Don't call str() on bytestrings: in Py3 it all goes wrong.
113 if not isinstance(v, bytes):
117 (field.decode('utf-8') if isinstance(field, bytes) else field,
118 v.encode('utf-8') if isinstance(v, str) else v))
121 # support for explicit filename
124 if isinstance(v, (tuple, list)):
132 fn = guess_filename(v) or k
134 if isinstance(fp, str):
136 if isinstance(fp, bytes):
139 rf = RequestField(name=k, data=fp.read(),
140 filename=fn, headers=fh)
141 rf.make_multipart(content_type=ft)
142 new_fields.append(rf)
144 body, content_type = encode_multipart_formdata(new_fields)
146 return body, content_type
149 class RequestHooksMixin(object):
150 def register_hook(self, event, hook):
151 """Properly register a hook."""
153 if event not in self.hooks:
154 raise ValueError('Unsupported event specified, with event name "%s"' % (event))
156 if isinstance(hook, collections.Callable):
157 self.hooks[event].append(hook)
158 elif hasattr(hook, '__iter__'):
159 self.hooks[event].extend(h for h in hook if isinstance(h, collections.Callable))
161 def deregister_hook(self, event, hook):
162 """Deregister a previously registered hook.
163 Returns True if the hook existed, False if not.
167 self.hooks[event].remove(hook)
173 class Request(RequestHooksMixin):
174 """A user-created :class:`Request <Request>` object.
176 Used to prepare a :class:`PreparedRequest <PreparedRequest>`, which is sent to the server.
178 :param method: HTTP method to use.
179 :param url: URL to send.
180 :param headers: dictionary of headers to send.
181 :param files: dictionary of {filename: fileobject} files to multipart upload.
182 :param data: the body to attach the request. If a dictionary is provided, form-encoding will take place.
183 :param params: dictionary of URL parameters to append to the URL.
184 :param auth: Auth handler or (user, pass) tuple.
185 :param cookies: dictionary or CookieJar of cookies to attach to this request.
186 :param hooks: dictionary of callback hooks, for internal usage.
191 >>> req = requests.Request('GET', 'http://httpbin.org/get')
193 <PreparedRequest [GET]>
207 # Default empty dicts for dict params.
208 data = [] if data is None else data
209 files = [] if files is None else files
210 headers = {} if headers is None else headers
211 params = {} if params is None else params
212 hooks = {} if hooks is None else hooks
214 self.hooks = default_hooks()
215 for (k, v) in list(hooks.items()):
216 self.register_hook(event=k, hook=v)
220 self.headers = headers
225 self.cookies = cookies
228 return '<Request [%s]>' % (self.method)
231 """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmission and returns it."""
232 p = PreparedRequest()
236 headers=self.headers,
241 cookies=self.cookies,
247 class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
248 """The fully mutable :class:`PreparedRequest <PreparedRequest>` object,
249 containing the exact bytes that will be sent to the server.
251 Generated from either a :class:`Request <Request>` object or manually.
256 >>> req = requests.Request('GET', 'http://httpbin.org/get')
257 >>> r = req.prepare()
258 <PreparedRequest [GET]>
260 >>> s = requests.Session()
267 #: HTTP verb to send to the server.
269 #: HTTP URL to send the request to.
271 #: dictionary of HTTP headers.
273 # The `CookieJar` used to create the Cookie header will be stored here
274 # after prepare_cookies is called
276 #: request body to send to the server.
278 #: dictionary of callback hooks, for internal usage.
279 self.hooks = default_hooks()
281 def prepare(self, method=None, url=None, headers=None, files=None,
282 data=None, params=None, auth=None, cookies=None, hooks=None):
283 """Prepares the entire request with the given parameters."""
285 self.prepare_method(method)
286 self.prepare_url(url, params)
287 self.prepare_headers(headers)
288 self.prepare_cookies(cookies)
289 self.prepare_body(data, files)
290 self.prepare_auth(auth, url)
291 # Note that prepare_auth must be last to enable authentication schemes
292 # such as OAuth to work on a fully prepared request.
294 # This MUST go after prepare_auth. Authenticators could add a hook
295 self.prepare_hooks(hooks)
298 return '<PreparedRequest [%s]>' % (self.method)
301 p = PreparedRequest()
302 p.method = self.method
304 p.headers = self.headers.copy()
305 p._cookies = self._cookies.copy()
310 def prepare_method(self, method):
311 """Prepares the given HTTP method."""
313 if self.method is not None:
314 self.method = self.method.upper()
316 def prepare_url(self, url, params):
317 """Prepares the given HTTP URL."""
318 #: Accept objects that have string representations.
324 except UnicodeDecodeError:
327 # Don't do any URL preparation for oddball schemes
328 if ':' in url and not url.lower().startswith('http'):
332 # Support for unicode domain names and paths.
333 scheme, auth, host, port, path, query, fragment = parse_url(url)
336 raise MissingSchema("Invalid URL {0!r}: No schema supplied. "
337 "Perhaps you meant http://{0}?".format(url))
340 raise InvalidURL("Invalid URL %r: No host supplied" % url)
342 # Only want to apply IDNA to the hostname
344 host = host.encode('idna').decode('utf-8')
346 raise InvalidURL('URL has an invalid label.')
348 # Carefully reconstruct the network location
354 netloc += ':' + str(port)
356 # Bare domains aren't valid URLs.
361 if isinstance(scheme, str):
362 scheme = scheme.encode('utf-8')
363 if isinstance(netloc, str):
364 netloc = netloc.encode('utf-8')
365 if isinstance(path, str):
366 path = path.encode('utf-8')
367 if isinstance(query, str):
368 query = query.encode('utf-8')
369 if isinstance(fragment, str):
370 fragment = fragment.encode('utf-8')
372 enc_params = self._encode_params(params)
375 query = '%s&%s' % (query, enc_params)
379 url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))
382 def prepare_headers(self, headers):
383 """Prepares the given HTTP headers."""
386 self.headers = CaseInsensitiveDict((to_native_string(name), value) for name, value in headers.items())
388 self.headers = CaseInsensitiveDict()
390 def prepare_body(self, data, files):
391 """Prepares the given HTTP body data."""
393 # Check if file, fo, generator, iterator.
394 # If not, run through normal process.
402 hasattr(data, '__iter__'),
403 not isinstance(data, basestring),
404 not isinstance(data, list),
405 not isinstance(data, dict)
409 length = super_len(data)
410 except (TypeError, AttributeError, UnsupportedOperation):
417 raise NotImplementedError('Streamed bodies and files are mutually exclusive.')
419 if length is not None:
420 self.headers['Content-Length'] = builtin_str(length)
422 self.headers['Transfer-Encoding'] = 'chunked'
424 # Multi-part file uploads.
426 (body, content_type) = self._encode_files(files, data)
429 body = self._encode_params(data)
430 if isinstance(data, str) or isinstance(data, builtin_str) or hasattr(data, 'read'):
433 content_type = 'application/x-www-form-urlencoded'
435 self.prepare_content_length(body)
437 # Add content-type if it wasn't explicitly provided.
438 if (content_type) and (not 'content-type' in self.headers):
439 self.headers['Content-Type'] = content_type
443 def prepare_content_length(self, body):
444 if hasattr(body, 'seek') and hasattr(body, 'tell'):
446 self.headers['Content-Length'] = builtin_str(body.tell())
448 elif body is not None:
451 self.headers['Content-Length'] = builtin_str(l)
452 elif self.method not in ('GET', 'HEAD'):
453 self.headers['Content-Length'] = '0'
455 def prepare_auth(self, auth, url=''):
456 """Prepares the given HTTP auth data."""
458 # If no Auth is explicitly provided, extract it from the URL first.
460 url_auth = get_auth_from_url(self.url)
461 auth = url_auth if any(url_auth) else None
464 if isinstance(auth, tuple) and len(auth) == 2:
465 # special-case basic HTTP auth
466 auth = HTTPBasicAuth(*auth)
468 # Allow auth to make its changes.
471 # Update self to reflect the auth changes.
472 self.__dict__.update(r.__dict__)
474 # Recompute Content-Length
475 self.prepare_content_length(self.body)
477 def prepare_cookies(self, cookies):
478 """Prepares the given HTTP cookie data."""
480 if isinstance(cookies, cookielib.CookieJar):
481 self._cookies = cookies
483 self._cookies = cookiejar_from_dict(cookies)
485 cookie_header = get_cookie_header(self._cookies, self)
486 if cookie_header is not None:
487 self.headers['Cookie'] = cookie_header
489 def prepare_hooks(self, hooks):
490 """Prepares the given hooks."""
492 self.register_hook(event, hooks[event])
495 class Response(object):
496 """The :class:`Response <Response>` object, which contains a
497 server's response to an HTTP request.
514 super(Response, self).__init__()
516 self._content = False
517 self._content_consumed = False
519 #: Integer Code of responded HTTP Status.
520 self.status_code = None
522 #: Case-insensitive Dictionary of Response Headers.
523 #: For example, ``headers['content-encoding']`` will return the
524 #: value of a ``'Content-Encoding'`` response header.
525 self.headers = CaseInsensitiveDict()
527 #: File-like object representation of response (for advanced usage).
528 #: Requires that ``stream=True` on the request.
529 # This requirement does not apply for use internally to Requests.
532 #: Final URL location of Response.
535 #: Encoding to decode with when accessing r.text.
538 #: A list of :class:`Response <Response>` objects from
539 #: the history of the Request. Any redirect responses will end
540 #: up here. The list is sorted from the oldest to the most recent request.
545 #: A CookieJar of Cookies the server sent back.
546 self.cookies = cookiejar_from_dict({})
548 #: The amount of time elapsed between sending the request
549 #: and the arrival of the response (as a timedelta)
550 self.elapsed = datetime.timedelta(0)
552 def __getstate__(self):
553 # Consume everything; accessing the content attribute makes
554 # sure the content has been fully read.
555 if not self._content_consumed:
559 (attr, getattr(self, attr, None))
560 for attr in self.__attrs__
563 def __setstate__(self, state):
564 for name, value in state.items():
565 setattr(self, name, value)
567 # pickled objects do not have .raw
568 setattr(self, '_content_consumed', True)
571 return '<Response [%s]>' % (self.status_code)
574 """Returns true if :attr:`status_code` is 'OK'."""
577 def __nonzero__(self):
578 """Returns true if :attr:`status_code` is 'OK'."""
582 """Allows you to use a response as an iterator."""
583 return self.iter_content(128)
588 self.raise_for_status()
589 except RequestException:
594 def apparent_encoding(self):
595 """The apparent encoding, provided by the lovely Charade library
597 return chardet.detect(self.content)['encoding']
599 def iter_content(self, chunk_size=1, decode_unicode=False):
600 """Iterates over the response data. When stream=True is set on the
601 request, this avoids reading the content at once into memory for
602 large responses. The chunk size is the number of bytes it should
603 read into memory. This is not necessarily the length of each item
604 returned as decoding can take place.
606 if self._content_consumed:
607 # simulate reading small chunks of the content
608 return iter_slices(self._content, chunk_size)
612 # Special case for urllib3.
614 for chunk in self.raw.stream(chunk_size,
615 decode_content=True):
617 except IncompleteRead as e:
618 raise ChunkedEncodingError(e)
619 except AttributeError:
620 # Standard file-like object.
622 chunk = self.raw.read(chunk_size)
627 self._content_consumed = True
632 gen = stream_decode_response_unicode(gen, self)
636 def iter_lines(self, chunk_size=ITER_CHUNK_SIZE, decode_unicode=None):
637 """Iterates over the response data, one line at a time. When
638 stream=True is set on the request, this avoids reading the
639 content at once into memory for large responses.
644 for chunk in self.iter_content(chunk_size=chunk_size,
645 decode_unicode=decode_unicode):
647 if pending is not None:
648 chunk = pending + chunk
649 lines = chunk.splitlines()
651 if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:
652 pending = lines.pop()
659 if pending is not None:
664 """Content of the response, in bytes."""
666 if self._content is False:
669 if self._content_consumed:
671 'The content for this response was already consumed')
673 if self.status_code == 0:
676 self._content = bytes().join(self.iter_content(CONTENT_CHUNK_SIZE)) or bytes()
678 except AttributeError:
681 self._content_consumed = True
682 # don't need to release the connection; that's been handled by urllib3
683 # since we exhausted the data.
688 """Content of the response, in unicode.
690 If Response.encoding is None, encoding will be guessed using
694 # Try charset from content-type
696 encoding = self.encoding
701 # Fallback to auto-detected encoding.
702 if self.encoding is None:
703 encoding = self.apparent_encoding
705 # Decode unicode from given encoding.
707 content = str(self.content, encoding, errors='replace')
708 except (LookupError, TypeError):
709 # A LookupError is raised if the encoding was not found which could
710 # indicate a misspelling or similar mistake.
712 # A TypeError can be raised if encoding is None
714 # So we try blindly encoding.
715 content = str(self.content, errors='replace')
719 def json(self, **kwargs):
720 """Returns the json-encoded content of a response, if any.
722 :param \*\*kwargs: Optional arguments that ``json.loads`` takes.
725 if not self.encoding and len(self.content) > 3:
726 # No encoding set. JSON RFC 4627 section 3 states we should expect
727 # UTF-8, -16 or -32. Detect which one to use; If the detection or
728 # decoding fails, fall back to `self.text` (using chardet to make
730 encoding = guess_json_utf(self.content)
731 if encoding is not None:
732 return json.loads(self.content.decode(encoding), **kwargs)
733 return json.loads(self.text, **kwargs)
737 """Returns the parsed header links of the response, if any."""
739 header = self.headers.get('link')
745 links = parse_header_links(header)
748 key = link.get('rel') or link.get('url')
753 def raise_for_status(self):
754 """Raises stored :class:`HTTPError`, if one occurred."""
758 if 400 <= self.status_code < 500:
759 http_error_msg = '%s Client Error: %s' % (self.status_code, self.reason)
761 elif 500 <= self.status_code < 600:
762 http_error_msg = '%s Server Error: %s' % (self.status_code, self.reason)
765 raise HTTPError(http_error_msg, response=self)
768 """Closes the underlying file descriptor and releases the connection
771 *Note: Should not normally need to be called explicitly.*
773 return self.raw.release_conn()