summaryrefslogtreecommitdiff
path: root/requests/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'requests/models.py')
-rw-r--r--requests/models.py621
1 files changed, 0 insertions, 621 deletions
diff --git a/requests/models.py b/requests/models.py
deleted file mode 100644
index b3a60c2..0000000
--- a/requests/models.py
+++ /dev/null
@@ -1,621 +0,0 @@
-# -*- coding: utf-8 -*-
-
-"""
-requests.models
-~~~~~~~~~~~~~~~
-
-"""
-
-import urllib
-import urllib2
-import socket
-import zlib
-
-from urllib2 import HTTPError
-from urlparse import urlparse, urlunparse, urljoin
-from datetime import datetime
-
-from .config import settings
-from .monkeys import Request as _Request, HTTPBasicAuthHandler, HTTPForcedBasicAuthHandler, HTTPDigestAuthHandler, HTTPRedirectHandler
-from .structures import CaseInsensitiveDict
-from .packages.poster.encode import multipart_encode
-from .packages.poster.streaminghttp import register_openers, get_handlers
-from .utils import dict_from_cookiejar
-from .exceptions import RequestException, AuthenticationError, Timeout, URLRequired, InvalidMethod, TooManyRedirects
-from .status_codes import codes
-
-
-REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved)
-
-
-
-class Request(object):
- """The :class:`Request <models.Request>` object. It carries out all functionality of
- Requests. Recommended interface is with the Requests functions.
- """
-
- def __init__(self,
- url=None, headers=dict(), files=None, method=None, data=dict(),
- params=dict(), auth=None, cookiejar=None, timeout=None, redirect=False,
- allow_redirects=False, proxies=None):
-
- #: Float describ the timeout of the request.
- # (Use socket.setdefaulttimeout() as fallback)
- self.timeout = timeout
-
- #: Request URL.
- self.url = url
-
- #: Dictonary of HTTP Headers to attach to the :class:`Request <models.Request>`.
- self.headers = headers
-
- #: Dictionary of files to multipart upload (``{filename: content}``).
- self.files = files
-
- #: HTTP Method to use. Available: GET, HEAD, PUT, POST, DELETE.
- self.method = method
-
- #: Dictionary or byte of request body data to attach to the
- #: :class:`Request <models.Request>`.
- self.data = None
-
- #: Dictionary or byte of querystring data to attach to the
- #: :class:`Request <models.Request>`.
- self.params = None
-
- #: True if :class:`Request <models.Request>` is part of a redirect chain (disables history
- #: and HTTPError storage).
- self.redirect = redirect
-
- #: Set to True if full redirects are allowed (e.g. re-POST-ing of data at new ``Location``)
- self.allow_redirects = allow_redirects
-
- # Dictionary mapping protocol to the URL of the proxy (e.g. {'http': 'foo.bar:3128'})
- self.proxies = proxies
-
- self.data, self._enc_data = self._encode_params(data)
- self.params, self._enc_params = self._encode_params(params)
-
- #: :class:`Response <models.Response>` instance, containing
- #: content and metadata of HTTP Response, once :attr:`sent <send>`.
- self.response = Response()
-
- if isinstance(auth, (list, tuple)):
- auth = AuthObject(*auth)
- if not auth:
- auth = auth_manager.get_auth(self.url)
-
- #: :class:`AuthObject` to attach to :class:`Request <models.Request>`.
- self.auth = auth
-
- #: CookieJar to attach to :class:`Request <models.Request>`.
- self.cookiejar = cookiejar
-
- #: True if Request has been sent.
- self.sent = False
-
-
- # Header manipulation and defaults.
-
- if settings.accept_gzip:
- settings.base_headers.update({'Accept-Encoding': 'gzip'})
-
- if headers:
- headers = CaseInsensitiveDict(self.headers)
- else:
- headers = CaseInsensitiveDict()
-
- for (k, v) in settings.base_headers.items():
- if k not in headers:
- headers[k] = v
-
- self.headers = headers
-
-
- def __repr__(self):
- return '<Request [%s]>' % (self.method)
-
-
- def _checks(self):
- """Deterministic checks for consistency."""
-
- if not self.url:
- raise URLRequired
-
-
- def _get_opener(self):
- """Creates appropriate opener object for urllib2."""
-
- _handlers = []
-
- if self.cookiejar is not None:
- _handlers.append(urllib2.HTTPCookieProcessor(self.cookiejar))
-
- if self.auth:
- if not isinstance(self.auth.handler, (urllib2.AbstractBasicAuthHandler, urllib2.AbstractDigestAuthHandler)):
- # TODO: REMOVE THIS COMPLETELY
- auth_manager.add_password(self.auth.realm, self.url, self.auth.username, self.auth.password)
- self.auth.handler = self.auth.handler(auth_manager)
- auth_manager.add_auth(self.url, self.auth)
-
- _handlers.append(self.auth.handler)
-
- if self.proxies:
- _handlers.append(urllib2.ProxyHandler(self.proxies))
-
- _handlers.append(HTTPRedirectHandler)
-
- if not _handlers:
- return urllib2.urlopen
-
- if self.data or self.files:
- _handlers.extend(get_handlers())
-
- opener = urllib2.build_opener(*_handlers)
-
- if self.headers:
- # Allow default headers in the opener to be overloaded
- normal_keys = [k.capitalize() for k in self.headers]
- for key, val in opener.addheaders[:]:
- if key not in normal_keys:
- continue
- # Remove it, we have a value to take its place
- opener.addheaders.remove((key, val))
-
- return opener.open
-
-
- def _build_response(self, resp, is_error=False):
- """Build internal :class:`Response <models.Response>` object from given response."""
-
- def build(resp):
-
- response = Response()
- response.status_code = getattr(resp, 'code', None)
-
- try:
- response.headers = CaseInsensitiveDict(getattr(resp.info(), 'dict', None))
- response.read = resp.read
- response._resp = resp
- response._close = resp.close
-
- if self.cookiejar:
-
- response.cookies = dict_from_cookiejar(self.cookiejar)
-
-
- except AttributeError:
- pass
-
- if is_error:
- response.error = resp
-
- response.url = getattr(resp, 'url', None)
-
- return response
-
-
- history = []
-
- r = build(resp)
-
- if r.status_code in REDIRECT_STATI and not self.redirect:
-
- while (
- ('location' in r.headers) and
- ((r.status_code is codes.see_other) or (self.allow_redirects))
- ):
-
- r.close()
-
- if not len(history) < settings.max_redirects:
- raise TooManyRedirects()
-
- history.append(r)
-
- url = r.headers['location']
-
- # Handle redirection without scheme (see: RFC 1808 Section 4)
- if url.startswith('//'):
- parsed_rurl = urlparse(r.url)
- url = '%s:%s' % (parsed_rurl.scheme, url)
-
- # Facilitate non-RFC2616-compliant 'location' headers
- # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
- if not urlparse(url).netloc:
- url = urljoin(r.url, urllib.quote(urllib.unquote(url)))
-
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4
- if r.status_code is codes.see_other:
- method = 'GET'
- else:
- method = self.method
-
- request = Request(
- url, self.headers, self.files, method,
- self.data, self.params, self.auth, self.cookiejar,
- redirect=True
- )
- request.send()
- r = request.response
-
- r.history = history
-
- self.response = r
- self.response.request = self
-
-
- @staticmethod
- def _encode_params(data):
- """Encode parameters in a piece of data.
-
- If the data supplied is a dictionary, encodes each parameter in it, and
- returns a list of tuples containing the encoded parameters, and a urlencoded
- version of that.
-
- Otherwise, assumes the data is already encoded appropriately, and
- returns it twice.
-
- """
- if hasattr(data, 'items'):
- result = []
- for k, vs in data.items():
- for v in isinstance(vs, list) and vs or [vs]:
- result.append((k.encode('utf-8') if isinstance(k, unicode) else k,
- v.encode('utf-8') if isinstance(v, unicode) else v))
- return result, urllib.urlencode(result, doseq=True)
- else:
- return data, data
-
-
- def _build_url(self):
- """Build the actual URL to use."""
-
- # Support for unicode domain names and paths.
- scheme, netloc, path, params, query, fragment = urlparse(self.url)
- netloc = netloc.encode('idna')
- if isinstance(path, unicode):
- path = path.encode('utf-8')
- path = urllib.quote(urllib.unquote(path))
- self.url = str(urlunparse([ scheme, netloc, path, params, query, fragment ]))
-
- if self._enc_params:
- if urlparse(self.url).query:
- return '%s&%s' % (self.url, self._enc_params)
- else:
- return '%s?%s' % (self.url, self._enc_params)
- else:
- return self.url
-
-
- def send(self, anyway=False):
- """Sends the request. Returns True of successful, false if not.
- If there was an HTTPError during transmission,
- self.response.status_code will contain the HTTPError code.
-
- Once a request is successfully sent, `sent` will equal True.
-
- :param anyway: If True, request will be sent, even if it has
- already been sent.
- """
-
- self._checks()
- success = False
-
- # Logging
- if settings.verbose:
- settings.verbose.write('%s %s %s\n' % (
- datetime.now().isoformat(), self.method, self.url
- ))
-
-
- url = self._build_url()
- if self.method in ('GET', 'HEAD', 'DELETE'):
- req = _Request(url, method=self.method)
- else:
-
- if self.files:
- register_openers()
-
- if self.data:
- self.files.update(self.data)
-
- datagen, headers = multipart_encode(self.files)
- req = _Request(url, data=datagen, headers=headers, method=self.method)
-
- else:
- req = _Request(url, data=self._enc_data, method=self.method)
-
- if self.headers:
- for k,v in self.headers.iteritems():
- req.add_header(k, v)
-
- if not self.sent or anyway:
-
- try:
- opener = self._get_opener()
- try:
-
- resp = opener(req, timeout=self.timeout)
-
- except TypeError, err:
- # timeout argument is new since Python v2.6
- if not 'timeout' in str(err):
- raise
-
- if settings.timeout_fallback:
- # fall-back and use global socket timeout (This is not thread-safe!)
- old_timeout = socket.getdefaulttimeout()
- socket.setdefaulttimeout(self.timeout)
-
- resp = opener(req)
-
- if settings.timeout_fallback:
- # restore gobal timeout
- socket.setdefaulttimeout(old_timeout)
-
- if self.cookiejar is not None:
- self.cookiejar.extract_cookies(resp, req)
-
- except (urllib2.HTTPError, urllib2.URLError), why:
- if hasattr(why, 'reason'):
- if isinstance(why.reason, socket.timeout):
- why = Timeout(why)
-
- self._build_response(why, is_error=True)
-
-
- else:
- self._build_response(resp)
- self.response.ok = True
-
-
- self.sent = self.response.ok
-
- return self.sent
-
-
-
-class Response(object):
- """The core :class:`Response <models.Response>` object. All
- :class:`Request <models.Request>` objects contain a
- :class:`response <models.Response>` attribute, which is an instance
- of this class.
- """
-
- def __init__(self):
- #: Raw content of the response, in bytes.
- #: If ``content-encoding`` of response was set to ``gzip``, the
- #: response data will be automatically deflated.
- self._content = None
- #: Integer Code of responded HTTP Status.
- self.status_code = None
- #: Case-insensitive Dictionary of Response Headers.
- #: For example, ``headers['content-encoding']`` will return the
- #: value of a ``'Content-Encoding'`` response header.
- self.headers = CaseInsensitiveDict()
- #: Final URL location of Response.
- self.url = None
- #: True if no :attr:`error` occured.
- self.ok = False
- #: Resulting :class:`HTTPError` of request, if one occured.
- self.error = None
- #: A list of :class:`Response <models.Response>` objects from
- #: the history of the Request. Any redirect responses will end
- #: up here.
- self.history = []
- #: The Request that created the Response.
- self.request = None
- #: A dictionary of Cookies the server sent back.
- self.cookies = None
-
-
- def __repr__(self):
- return '<Response [%s]>' % (self.status_code)
-
-
- def __nonzero__(self):
- """Returns true if :attr:`status_code` is 'OK'."""
- return not self.error
-
-
- def __getattr__(self, name):
- """Read and returns the full stream when accessing to :attr: `content`"""
- if name == 'content':
- if self._content is not None:
- return self._content
- self._content = self.read()
- if self.headers.get('content-encoding', '') == 'gzip':
- try:
- self._content = zlib.decompress(self._content, 16+zlib.MAX_WBITS)
- except zlib.error:
- pass
- return self._content
- else:
- raise AttributeError
-
- def raise_for_status(self):
- """Raises stored :class:`HTTPError` or :class:`URLError`, if one occured."""
- if self.error:
- raise self.error
-
-
- def close(self):
- if self._resp.fp is not None and hasattr(self._resp.fp, '_sock'):
- self._resp.fp._sock.recv = None
- self._close()
-
-class AuthManager(object):
- """Requests Authentication Manager."""
-
- def __new__(cls):
- singleton = cls.__dict__.get('__singleton__')
- if singleton is not None:
- return singleton
-
- cls.__singleton__ = singleton = object.__new__(cls)
-
- return singleton
-
-
- def __init__(self):
- self.passwd = {}
- self._auth = {}
-
-
- def __repr__(self):
- return '<AuthManager [%s]>' % (self.method)
-
-
- def add_auth(self, uri, auth):
- """Registers AuthObject to AuthManager."""
-
- uri = self.reduce_uri(uri, False)
-
- # try to make it an AuthObject
- if not isinstance(auth, AuthObject):
- try:
- auth = AuthObject(*auth)
- except TypeError:
- pass
-
- self._auth[uri] = auth
-
-
- def add_password(self, realm, uri, user, passwd):
- """Adds password to AuthManager."""
- # uri could be a single URI or a sequence
- if isinstance(uri, basestring):
- uri = [uri]
-
- reduced_uri = tuple([self.reduce_uri(u, False) for u in uri])
-
- if reduced_uri not in self.passwd:
- self.passwd[reduced_uri] = {}
- self.passwd[reduced_uri] = (user, passwd)
-
-
- def find_user_password(self, realm, authuri):
- for uris, authinfo in self.passwd.iteritems():
- reduced_authuri = self.reduce_uri(authuri, False)
- for uri in uris:
- if self.is_suburi(uri, reduced_authuri):
- return authinfo
-
- return (None, None)
-
-
- def get_auth(self, uri):
- (in_domain, in_path) = self.reduce_uri(uri, False)
-
- for domain, path, authority in (
- (i[0][0], i[0][1], i[1]) for i in self._auth.iteritems()
- ):
- if in_domain == domain:
- if path in in_path:
- return authority
-
-
- def reduce_uri(self, uri, default_port=True):
- """Accept authority or URI and extract only the authority and path."""
-
- # note HTTP URLs do not have a userinfo component
- parts = urllib2.urlparse.urlsplit(uri)
-
- if parts[1]:
- # URI
- scheme = parts[0]
- authority = parts[1]
- path = parts[2] or '/'
- else:
- # host or host:port
- scheme = None
- authority = uri
- path = '/'
-
- host, port = urllib2.splitport(authority)
-
- if default_port and port is None and scheme is not None:
- dport = {"http": 80,
- "https": 443,
- }.get(scheme)
- if dport is not None:
- authority = "%s:%d" % (host, dport)
-
- return authority, path
-
-
- def is_suburi(self, base, test):
- """Check if test is below base in a URI tree
-
- Both args must be URIs in reduced form.
- """
- if base == test:
- return True
- if base[0] != test[0]:
- return False
- common = urllib2.posixpath.commonprefix((base[1], test[1]))
- if len(common) == len(base[1]):
- return True
- return False
-
-
- def empty(self):
- self.passwd = {}
-
-
- def remove(self, uri, realm=None):
- # uri could be a single URI or a sequence
- if isinstance(uri, basestring):
- uri = [uri]
-
- for default_port in True, False:
- reduced_uri = tuple([self.reduce_uri(u, default_port) for u in uri])
- del self.passwd[reduced_uri][realm]
-
-
- def __contains__(self, uri):
- # uri could be a single URI or a sequence
- if isinstance(uri, basestring):
- uri = [uri]
-
- uri = tuple([self.reduce_uri(u, False) for u in uri])
-
- if uri in self.passwd:
- return True
-
- return False
-
-auth_manager = AuthManager()
-
-
-
-class AuthObject(object):
- """The :class:`AuthObject` is a simple HTTP Authentication token. When
- given to a Requests function, it enables Basic HTTP Authentication for that
- Request. You can also enable Authorization for domain realms with AutoAuth.
- See AutoAuth for more details.
-
- :param username: Username to authenticate with.
- :param password: Password for given username.
- :param realm: (optional) the realm this auth applies to
- :param handler: (optional) basic || digest || proxy_basic || proxy_digest
- """
-
- _handlers = {
- 'basic': HTTPBasicAuthHandler,
- 'forced_basic': HTTPForcedBasicAuthHandler,
- 'digest': HTTPDigestAuthHandler,
- 'proxy_basic': urllib2.ProxyBasicAuthHandler,
- 'proxy_digest': urllib2.ProxyDigestAuthHandler
- }
-
- def __init__(self, username, password, handler='forced_basic', realm=None):
- self.username = username
- self.password = password
- self.realm = realm
-
- if isinstance(handler, basestring):
- self.handler = self._handlers.get(handler.lower(), HTTPForcedBasicAuthHandler)
- else:
- self.handler = handler