Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add django-browserid 0.2, requests 0.9.1, certifi 0.0.4

  • Loading branch information...
commit 483c736b8353b4e02bca4647c6ed414d11b74bd8 1 parent 66cca24
@camd camd authored
Showing with 8,351 additions and 0 deletions.
  1. +1 −0  lib/python/certifi/__init__.py
  2. +3,376 −0 lib/python/certifi/cacert.pem
  3. +34 −0 lib/python/certifi/core.py
  4. +2 −0  lib/python/django_browserid/__init__.py
  5. +117 −0 lib/python/django_browserid/auth.py
  6. +131 −0 lib/python/django_browserid/base.py
  7. +8 −0 lib/python/django_browserid/context_processors.py
  8. +8 −0 lib/python/django_browserid/forms.py
  9. +9 −0 lib/python/django_browserid/urls.py
  10. +68 −0 lib/python/django_browserid/views.py
  11. +34 −0 lib/python/requests/__init__.py
  12. +115 −0 lib/python/requests/api.py
  13. +96 −0 lib/python/requests/async.py
  14. +136 −0 lib/python/requests/auth.py
  15. +44 −0 lib/python/requests/defaults.py
  16. +31 −0 lib/python/requests/exceptions.py
  17. +40 −0 lib/python/requests/hooks.py
  18. +708 −0 lib/python/requests/models.py
  19. +3 −0  lib/python/requests/packages/__init__.py
  20. +3 −0  lib/python/requests/packages/oreos/__init__.py
  21. +24 −0 lib/python/requests/packages/oreos/core.py
  22. +770 −0 lib/python/requests/packages/oreos/monkeys.py
  23. +399 −0 lib/python/requests/packages/oreos/structures.py
  24. +48 −0 lib/python/requests/packages/urllib3/__init__.py
  25. +131 −0 lib/python/requests/packages/urllib3/_collections.py
  26. +552 −0 lib/python/requests/packages/urllib3/connectionpool.py
  27. +35 −0 lib/python/requests/packages/urllib3/exceptions.py
  28. +71 −0 lib/python/requests/packages/urllib3/filepost.py
  29. +4 −0 lib/python/requests/packages/urllib3/packages/__init__.py
  30. +61 −0 lib/python/requests/packages/urllib3/packages/ssl_match_hostname/__init__.py
  31. +128 −0 lib/python/requests/packages/urllib3/poolmanager.py
  32. +145 −0 lib/python/requests/packages/urllib3/request.py
  33. +181 −0 lib/python/requests/packages/urllib3/response.py
  34. +288 −0 lib/python/requests/sessions.py
  35. +86 −0 lib/python/requests/status_codes.py
  36. +66 −0 lib/python/requests/structures.py
  37. +398 −0 lib/python/requests/utils.py
View
1  lib/python/certifi/__init__.py
@@ -0,0 +1 @@
+from .core import *
View
3,376 lib/python/certifi/cacert.pem
3,376 additions, 0 deletions not shown
View
34 lib/python/certifi/core.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+ceritfi.py
+~~~~~~~~~~
+
+This module returns the installation location of cacert.pem.
+"""
+
+import os
+
+_content = None
+
+
+def where():
+ f = os.path.split(__file__)[0]
+
+ return os.path.join(f, 'cacert.pem')
+
+def content():
+ global _content
+
+ if not _content:
+ # global _content
+
+ with open(where()) as f:
+ _content = f.read()
+
+ return _content
+
+
+if __name__ == '__main__':
+ print where()
View
2  lib/python/django_browserid/__init__.py
@@ -0,0 +1,2 @@
+from django_browserid.auth import BrowserIDBackend
+from django_browserid.base import get_audience, verify
View
117 lib/python/django_browserid/auth.py
@@ -0,0 +1,117 @@
+import base64
+import hashlib
+import logging
+from warnings import warn
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.core.exceptions import ImproperlyConfigured
+from django.utils.importlib import import_module
+
+from django_browserid.base import get_audience as base_get_audience, verify
+
+log = logging.getLogger(__name__)
+
+
+def get_audience(*args):
+ warn('Deprecated, please use the standalone function '
+ 'django_browserid.get_audience instead.', DeprecationWarning)
+ return base_get_audience(*args)
+
+
+def default_username_algo(email):
+ # store the username as a base64 encoded sha1 of the email address
+ # this protects against data leakage because usernames are often
+ # treated as public identifiers (so we can't use the email address).
+ username = base64.urlsafe_b64encode(
+ hashlib.sha1(email).digest()).rstrip('=')
+ return username
+
+
+class BrowserIDBackend(object):
+ supports_anonymous_user = False
+ supports_object_permissions = False
+
+ def verify(self, *args):
+ warn('Deprecated, please use the standalone function '
+ 'django_browserid.verify instead.', DeprecationWarning)
+ return verify(*args)
+
+ def filter_users_by_email(self, email):
+ """Return all users matching the specified email."""
+ return User.objects.filter(email=email)
+
+ def create_user(self, email):
+ """Return object for a newly created user account."""
+ username_algo = getattr(settings, 'BROWSERID_USERNAME_ALGO', None)
+ if username_algo is not None:
+ username = username_algo(email)
+ else:
+ username = default_username_algo(email)
+
+ return User.objects.create_user(username, email)
+
+ def authenticate(self, assertion=None, audience=None):
+ """``django.contrib.auth`` compatible authentication method.
+
+ Given a BrowserID assertion and an audience, it attempts to
+ verify them and then extract the email address for the authenticated
+ user.
+
+ An audience should be in the form ``https://example.com`` or
+ ``http://localhost:8001``.
+
+ See django_browserid.base.get_audience()
+ """
+ result = verify(assertion, audience)
+ if result is None:
+ return None
+
+ email = result['email']
+
+ # in the rare case that two user accounts have the same email address,
+ # log and bail. randomly selecting one seems really wrong.
+ users = self.filter_users_by_email(email=email)
+ if len(users) > 1:
+ log.warn('%d users with email address %s.' % (len(users), email))
+ return None
+ if len(users) == 1:
+ return users[0]
+
+ create_user = getattr(settings, 'BROWSERID_CREATE_USER', False)
+ if not create_user:
+ return None
+ elif create_user == True:
+ return self.create_user(email)
+ else:
+ # Find the function to call, call it and throw in the email.
+ return self._load_module(create_user)(email)
+
+ def get_user(self, user_id):
+ try:
+ return User.objects.get(pk=user_id)
+ except User.DoesNotExist:
+ return None
+
+ def _load_module(self, path):
+ """Code to load create user module. Based off django's load_backend"""
+
+ i = path.rfind('.')
+ module, attr = path[:i], path[i + 1:]
+
+ try:
+ mod = import_module(module)
+ except ImportError, e:
+ raise ImproperlyConfigured('Error importing BROWSERID_CREATE_USER'
+ ' function.')
+ except ValueError, e:
+ raise ImproperlyConfigured('Error importing BROWSERID_CREATE_USER'
+ ' function. Is BROWSERID_CREATE_USER a'
+ ' string?')
+
+ try:
+ create_user = getattr(mod, attr)
+ except AttributeError:
+ raise ImproperlyConfigured('Module "%s" does not define a "%s" '
+ 'function.' % (module, attr))
+ return create_user
View
131 lib/python/django_browserid/base.py
@@ -0,0 +1,131 @@
+import logging
+import urllib
+from warnings import warn
+try:
+ import json
+except ImportError:
+ import simplejson as json
+
+
+from django.conf import settings
+
+import requests
+
+
+log = logging.getLogger(__name__)
+
+
+DEFAULT_HTTP_TIMEOUT = 5
+DEFAULT_VERIFICATION_URL = 'https://browserid.org/verify'
+OKAY_RESPONSE = 'okay'
+
+
+def get_audience(request):
+ """Uses Django settings to format the audience.
+
+ To use this function, make sure there is either a SITE_URL in
+ your settings.py file or PROTOCOL and DOMAIN.
+
+ Examples using SITE_URL:
+ SITE_URL = 'http://127.0.0.1:8001'
+ SITE_URL = 'https://example.com'
+ SITE_URL = 'http://example.com'
+
+ If you don't have a SITE_URL you can also use these varables:
+ PROTOCOL, DOMAIN, and (optionally) PORT.
+ Example 1:
+ PROTOCOL = 'https://'
+ DOMAIN = 'example.com'
+
+ Example 2:
+ PROTOCOL = 'http://'
+ DOMAIN = '127.0.0.1'
+ PORT = '8001'
+
+ If none are set, we trust the request to populate the audience.
+ This is *not secure*!
+ """
+ site_url = getattr(settings, 'SITE_URL', False)
+
+ # Note audience based on request for developer warnings
+ if request.is_secure():
+ req_proto = 'https://'
+ else:
+ req_proto = 'http://'
+ req_domain = request.get_host()
+
+ # If we don't define it explicitly
+ if not site_url:
+ warn('Using DOMAIN and PROTOCOL to specify your BrowserID audience is '
+ 'deprecated. Please use the SITE_URL setting instead.',
+ DeprecationWarning)
+
+ # DOMAIN is example.com req_domain is example.com:8001
+ domain = getattr(settings, 'DOMAIN', req_domain.split(':')[0])
+ protocol = getattr(settings, 'PROTOCOL', req_proto)
+
+ standards = {'https://': 443, 'http://': 80}
+ if ':' in req_domain:
+ req_port = req_domain.split(':')[1]
+ else:
+ req_port = None
+ port = getattr(settings, 'PORT', req_port or standards[protocol])
+ if port == standards[protocol]:
+ site_url = ''.join(map(str, (protocol, domain)))
+ else:
+ site_url = ''.join(map(str, (protocol, domain, ':', port)))
+
+ req_url = "%s%s" % (req_proto, req_domain)
+ if site_url != "%s%s" % (req_proto, req_domain):
+ log.warning('Misconfigured SITE_URL? settings has [%s], but '
+ 'actual request was [%s] BrowserID may fail on '
+ 'audience' % (site_url, req_url))
+ return site_url
+
+
+def _verify_http_request(url, qs):
+ parameters = {
+ 'data': qs,
+ 'proxies': getattr(settings, 'BROWSERID_PROXY_INFO', None),
+ 'verify': not getattr(settings, 'BROWSERID_DISABLE_CERT_CHECK', False),
+ 'headers': {'Content-type': 'application/x-www-form-urlencoded'},
+ 'params': {
+ 'timeout': getattr(settings, 'BROWSERID_HTTP_TIMEOUT',
+ DEFAULT_HTTP_TIMEOUT)
+ }
+ }
+
+ if parameters['verify']:
+ parameters['verify'] = getattr(settings, 'BROWSERID_CACERT_FILE', True)
+
+ r = requests.post(url, **parameters)
+
+ try:
+ rv = json.loads(r.content)
+ except ValueError:
+ log.debug('Failed to decode JSON. Resp: %s, Content: %s' %
+ (r.status_code, r.content))
+ return dict(status='failure')
+
+ return rv
+
+
+def verify(assertion, audience):
+ """Verify assertion using an external verification service."""
+ verify_url = getattr(settings, 'BROWSERID_VERIFICATION_URL',
+ DEFAULT_VERIFICATION_URL)
+
+ log.info("Verification URL: %s" % verify_url)
+
+ result = _verify_http_request(verify_url, urllib.urlencode({
+ 'assertion': assertion,
+ 'audience': audience
+ }))
+
+ if result['status'] == OKAY_RESPONSE:
+ return result
+
+ log.error('BrowserID verification failure. Response: %r '
+ 'Audience: %r' % (result, audience))
+ log.error("BID assert: %r" % assertion)
+ return False
View
8 lib/python/django_browserid/context_processors.py
@@ -0,0 +1,8 @@
+from django_browserid.forms import BrowserIDForm
+
+
+def browserid_form(request):
+ """
+ A context processor that adds a BrowserID form to the request
+ """
+ return {'browserid_form': BrowserIDForm()}
View
8 lib/python/django_browserid/forms.py
@@ -0,0 +1,8 @@
+from django import forms
+
+
+class BrowserIDForm(forms.Form):
+ assertion = forms.CharField(widget=forms.HiddenInput())
+
+ class Media:
+ js = ('browserid/browserid.js', 'https://browserid.org/include.js')
View
9 lib/python/django_browserid/urls.py
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import patterns, url
+
+from django_browserid.views import Verify
+
+
+urlpatterns = patterns('',
+ url('^browserid/verify/', Verify.as_view(),
+ name='browserid_verify')
+)
View
68 lib/python/django_browserid/views.py
@@ -0,0 +1,68 @@
+from django.conf import settings
+from django.contrib import auth
+from django.core.exceptions import ImproperlyConfigured
+from django.shortcuts import redirect
+from django.views.generic.edit import BaseFormView
+
+from django_browserid.forms import BrowserIDForm
+from django_browserid.base import get_audience
+
+
+class Verify(BaseFormView):
+ form_class = BrowserIDForm
+ failure_url = getattr(settings, 'LOGIN_REDIRECT_URL_FAILURE', '/')
+ success_url = getattr(settings, 'LOGIN_REDIRECT_URL', '/')
+
+ def login_success(self):
+ """Handle a successful login. Use this to perform complex redirects
+ post-login.
+ """
+ auth.login(self.request, self.user)
+ redirect_field_name = self.kwargs.get('redirect_field_name',
+ auth.REDIRECT_FIELD_NAME)
+ redirect_to = self.request.REQUEST.get(redirect_field_name, None)
+
+ if redirect_to is not None:
+ return redirect(redirect_to)
+ else:
+ return redirect(self.get_success_url())
+
+ def login_failure(self):
+ """Handle a failed login. Use this to perform complex redirects
+ post-login.
+ """
+ return redirect(self.get_failure_url())
+
+ def form_valid(self, form):
+ """Handles the return post request from the browserID form and puts
+ interesting variables into the class. If everything checks out, then
+ we call handle_user to decide how to handle a valid user
+ """
+ self.assertion = form.cleaned_data['assertion']
+ self.audience = get_audience(self.request)
+ self.user = auth.authenticate(
+ assertion=self.assertion,
+ audience=self.audience)
+
+ if self.user and self.user.is_active:
+ return self.login_success()
+
+ return self.login_failure()
+
+ def form_invalid(self, *args, **kwargs):
+ return self.login_failure()
+
+ def get(self, *args, **kwargs):
+ return redirect(self.get_failure_url())
+
+ def get_failure_url(self):
+ """
+ This is just the django version of get_success_url
+ https://github.com/django/django/blob/master/django/views/generic/edit.py#L51
+ """
+ if self.failure_url:
+ url = self.failure_url
+ else:
+ raise ImproperlyConfigured(
+ "No URL to redirect to. Provide a failure_url.")
+ return url
View
34 lib/python/requests/__init__.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+# __
+# /__) _ _ _ _ _/ _
+# / ( (- (/ (/ (- _) / _)
+# /
+
+"""
+requests
+~~~~~~~~
+
+:copyright: (c) 2012 by Kenneth Reitz.
+:license: ISC, see LICENSE for more details.
+
+"""
+
+__title__ = 'requests'
+__version__ = '0.9.1'
+__build__ = 0x000901
+__author__ = 'Kenneth Reitz'
+__license__ = 'ISC'
+__copyright__ = 'Copyright 2012 Kenneth Reitz'
+
+
+
+from . import utils
+from .models import Request, Response
+from .api import request, get, head, post, patch, put, delete, options
+from .sessions import session, Session
+from .status_codes import codes
+from .exceptions import (
+ RequestException, Timeout, URLRequired,
+ TooManyRedirects, HTTPError, ConnectionError
+)
View
115 lib/python/requests/api.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.api
+~~~~~~~~~~~~
+
+This module implements the Requests API.
+
+:copyright: (c) 2012 by Kenneth Reitz.
+:license: ISC, see LICENSE for more details.
+
+"""
+
+from . import sessions
+
+def request(method, url, **kwargs):
+ """Constructs and sends a :class:`Request <Request>`.
+ Returns :class:`Response <Response>` object.
+
+ :param method: method for the new :class:`Request` object.
+ :param url: URL for the new :class:`Request` object.
+ :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
+ :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
+ :param files: (optional) Dictionary of 'name': file-like-objects (or {'name': ('filename', fileobj)}) for multipart encoding upload.
+ :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
+ :param timeout: (optional) Float describing the timeout of the request.
+ :param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed.
+ :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
+ :param return_response: (optional) If False, an un-sent Request object will returned.
+ :param session: (optional) A :class:`Session` object to be used for the request.
+ :param config: (optional) A configuration dictionary.
+ :param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided.
+ """
+
+ s = kwargs.get('session') or sessions.session()
+ return s.request(method=method, url=url, **kwargs)
+
+
+
+def get(url, **kwargs):
+ """Sends a GET request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param **kwargs: Optional arguments that ``request`` takes.
+ """
+
+ kwargs.setdefault('allow_redirects', True)
+ return request('get', url, **kwargs)
+
+
+def options(url, **kwargs):
+ """Sends a OPTIONS request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param **kwargs: Optional arguments that ``request`` takes.
+ """
+
+ kwargs.setdefault('allow_redirects', True)
+ return request('options', url, **kwargs)
+
+
+def head(url, **kwargs):
+ """Sends a HEAD request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param **kwargs: Optional arguments that ``request`` takes.
+ """
+
+ kwargs.setdefault('allow_redirects', True)
+ return request('head', url, **kwargs)
+
+
+def post(url, data=None, **kwargs):
+ """Sends a POST request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param **kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return request('post', url, data=data, **kwargs)
+
+
+def put(url, data=None, **kwargs):
+ """Sends a PUT request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param **kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return request('put', url, data=data, **kwargs)
+
+
+def patch(url, data=None, **kwargs):
+ """Sends a PATCH request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param data: (optional) Dictionary or bytes to send in the body of the :class:`Request`.
+ :param **kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return request('patch', url, data=data, **kwargs)
+
+
+def delete(url, **kwargs):
+ """Sends a DELETE request. Returns :class:`Response` object.
+
+ :param url: URL for the new :class:`Request` object.
+ :param **kwargs: Optional arguments that ``request`` takes.
+ """
+
+ return request('delete', url, **kwargs)
View
96 lib/python/requests/async.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.async
+~~~~~~~~~~~~~~
+
+This module contains an asynchronous replica of ``requests.api``, powered
+by gevent. All API methods return a ``Request`` instance (as opposed to
+``Response``). A list of requests can be sent with ``map()``.
+"""
+
+try:
+ import gevent
+ from gevent import monkey as curious_george
+ from gevent.pool import Pool
+except ImportError:
+ raise RuntimeError('Gevent is required for requests.async.')
+
+# Monkey-patch.
+curious_george.patch_all(thread=False)
+
+from . import api
+
+
+__all__ = (
+ 'map',
+ 'get', 'options', 'head', 'post', 'put', 'patch', 'delete', 'request'
+)
+
+
+def patched(f):
+ """Patches a given API function to not send."""
+
+ def wrapped(*args, **kwargs):
+
+ kwargs['return_response'] = False
+ kwargs['prefetch'] = True
+
+ config = kwargs.get('config', {})
+ config.update(safe_mode=True)
+
+ kwargs['config'] = config
+
+ return f(*args, **kwargs)
+
+ return wrapped
+
+
+def send(r, pools=None):
+ """Sends a given Request object."""
+
+ if pools:
+ r._pools = pools
+
+ r.send()
+
+ return r.response
+
+
+# Patched requests.api functions.
+get = patched(api.get)
+options = patched(api.options)
+head = patched(api.head)
+post = patched(api.post)
+put = patched(api.put)
+patch = patched(api.patch)
+delete = patched(api.delete)
+request = patched(api.request)
+
+
+def map(requests, prefetch=True, size=None):
+ """Concurrently converts a list of Requests to Responses.
+
+ :param requests: a collection of Request objects.
+ :param prefetch: If False, the content will not be downloaded immediately.
+ :param size: Specifies the number of requests to make at a time. If None, no throttling occurs.
+ """
+
+ requests = list(requests)
+
+ if size:
+ pool = Pool(size)
+ pool.map(send, requests)
+ pool.join()
+ else:
+ jobs = [gevent.spawn(send, r) for r in requests]
+ gevent.joinall(jobs)
+
+ if prefetch:
+ [r.response.content for r in requests]
+
+ return [r.response for r in requests]
+
+
+
+
View
136 lib/python/requests/auth.py
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.auth
+~~~~~~~~~~~~~
+
+This module contains the authentication handlers for Requests.
+"""
+
+import time
+import hashlib
+
+from base64 import b64encode
+from urlparse import urlparse
+
+from .utils import randombytes, parse_dict_header
+
+
+
+def _basic_auth_str(username, password):
+ """Returns a Basic Auth string."""
+ return 'Basic %s' % b64encode('%s:%s' % (username, password))
+
+
+class AuthBase(object):
+ """Base class that all auth implementations derive from"""
+
+ def __call__(self, r):
+ raise NotImplementedError('Auth hooks must be callable.')
+
+
+class HTTPBasicAuth(AuthBase):
+ """Attaches HTTP Basic Authentication to the given Request object."""
+ def __init__(self, username, password):
+ self.username = str(username)
+ self.password = str(password)
+
+ def __call__(self, r):
+ r.headers['Authorization'] = _basic_auth_str(self.username, self.password)
+ return r
+
+
+class HTTPProxyAuth(HTTPBasicAuth):
+ """Attaches HTTP Proxy Authenetication to a given Request object."""
+ def __call__(self, r):
+ r.headers['Proxy-Authorization'] = _basic_auth_str(self.username, self.password)
+ return r
+
+
+class HTTPDigestAuth(AuthBase):
+ """Attaches HTTP Digest Authentication to the given Request object."""
+ def __init__(self, username, password):
+ self.username = username
+ self.password = password
+
+ def handle_401(self, r):
+ """Takes the given response and tries digest-auth, if needed."""
+
+ s_auth = r.headers.get('www-authenticate', '')
+
+ if 'digest' in s_auth.lower():
+
+ last_nonce = ''
+ nonce_count = 0
+
+ chal = parse_dict_header(s_auth.replace('Digest ', ''))
+
+ realm = chal['realm']
+ nonce = chal['nonce']
+ qop = chal.get('qop')
+ algorithm = chal.get('algorithm', 'MD5')
+ opaque = chal.get('opaque', None)
+
+ algorithm = algorithm.upper()
+ # lambdas assume digest modules are imported at the top level
+ if algorithm == 'MD5':
+ H = lambda x: hashlib.md5(x).hexdigest()
+ elif algorithm == 'SHA':
+ H = lambda x: hashlib.sha1(x).hexdigest()
+ # XXX MD5-sess
+ KD = lambda s, d: H("%s:%s" % (s, d))
+
+ if H is None:
+ return None
+
+ # XXX not implemented yet
+ entdig = None
+ p_parsed = urlparse(r.request.url)
+ path = p_parsed.path + p_parsed.query
+
+ A1 = '%s:%s:%s' % (self.username, realm, self.password)
+ A2 = '%s:%s' % (r.request.method, path)
+
+ if qop == 'auth':
+ if nonce == last_nonce:
+ nonce_count += 1
+ else:
+ nonce_count = 1
+ last_nonce = nonce
+
+ ncvalue = '%08x' % nonce_count
+ cnonce = (hashlib.sha1("%s:%s:%s:%s" % (
+ nonce_count, nonce, time.ctime(), randombytes(8)))
+ .hexdigest()[:16]
+ )
+ noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, H(A2))
+ respdig = KD(H(A1), noncebit)
+ elif qop is None:
+ respdig = KD(H(A1), "%s:%s" % (nonce, H(A2)))
+ else:
+ # XXX handle auth-int.
+ return None
+
+ # XXX should the partial digests be encoded too?
+ base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \
+ 'response="%s"' % (self.username, realm, nonce, path, respdig)
+ if opaque:
+ base += ', opaque="%s"' % opaque
+ if entdig:
+ base += ', digest="%s"' % entdig
+ base += ', algorithm="%s"' % algorithm
+ if qop:
+ base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce)
+
+ r.request.headers['Authorization'] = 'Digest %s' % (base)
+ r.request.send(anyway=True)
+ _r = r.request.response
+ _r.history.append(r)
+
+ return _r
+
+ return r
+
+ def __call__(self, r):
+ r.hooks['response'] = self.handle_401
+ return r
View
44 lib/python/requests/defaults.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.defaults
+~~~~~~~~~~~~~~~~~
+
+This module provides the Requests configuration defaults.
+
+Configurations:
+
+:base_headers: Default HTTP headers.
+:verbose: Stream to write request logging to.
+:timeout: Seconds until request timeout.
+:max_redirects: Maximum number of redirects allowed within a request.
+:decode_unicode: Decode unicode responses automatically?
+:keep_alive: Reuse HTTP Connections?
+:max_retries: The number of times a request should be retried in the event of a connection failure.
+:danger_mode: If true, Requests will raise errors immediately.
+:safe_mode: If true, Requests will catch all errors.
+:pool_maxsize: The maximium size of an HTTP connection pool.
+:pool_connections: The number of active HTTP connection pools to use.
+
+"""
+
+from . import __version__
+
+defaults = dict()
+
+
+defaults['base_headers'] = {
+ 'User-Agent': 'python-requests/%s' % __version__,
+ 'Accept-Encoding': ', '.join(('identity', 'deflate', 'compress', 'gzip')),
+ 'Accept': '*/*'
+}
+
+defaults['verbose'] = None
+defaults['max_redirects'] = 30
+defaults['decode_unicode'] = True
+defaults['pool_connections'] = 10
+defaults['pool_maxsize'] = 10
+defaults['max_retries'] = 0
+defaults['danger_mode'] = False
+defaults['safe_mode'] = False
+defaults['keep_alive'] = True
View
31 lib/python/requests/exceptions.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.exceptions
+~~~~~~~~~~~~~~~~~~~
+
+This module contains the set of Requests' exceptions.
+
+"""
+
+class RequestException(Exception):
+ """There was an ambiguous exception that occurred while handling your
+ request."""
+
+class HTTPError(RequestException):
+ """An HTTP error occurred."""
+
+class ConnectionError(RequestException):
+ """A Connection error occurred."""
+
+class SSLError(ConnectionError):
+ """An SSL error occurred."""
+
+class Timeout(RequestException):
+ """The request timed out."""
+
+class URLRequired(RequestException):
+ """A valid URL is required to make a request."""
+
+class TooManyRedirects(RequestException):
+ """Too many redirects."""
View
40 lib/python/requests/hooks.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.hooks
+~~~~~~~~~~~~~~
+
+This module provides the capabilities for the Requests hooks system.
+
+Available hooks:
+
+``args``:
+ A dictionary of the arguments being sent to Request().
+
+``pre_request``:
+ The Request object, directly before being sent.
+
+``post_request``:
+ The Request object, directly after being sent.
+
+``response``:
+ The response generated from a Request.
+
+"""
+
+import warnings
+
+
+def dispatch_hook(key, hooks, hook_data):
+ """Dispatches a hook dictionary on a given piece of data."""
+
+ hooks = hooks or dict()
+
+ if key in hooks:
+ try:
+ return hooks.get(key).__call__(hook_data) or hook_data
+
+ except Exception, why:
+ warnings.warn(str(why))
+
+ return hook_data
View
708 lib/python/requests/models.py
@@ -0,0 +1,708 @@
+# -*- coding: utf-8 -*-
+
+"""
+requests.models
+~~~~~~~~~~~~~~~
+
+This module contains the primary objects that power Requests.
+"""
+
+import os
+import urllib
+
+from urlparse import urlparse, urlunparse, urljoin, urlsplit
+from datetime import datetime
+
+from .hooks import dispatch_hook
+from .structures import CaseInsensitiveDict
+from .status_codes import codes
+from .packages import oreos
+from .auth import HTTPBasicAuth, HTTPProxyAuth
+from .packages.urllib3.response import HTTPResponse
+from .packages.urllib3.exceptions import MaxRetryError
+from .packages.urllib3.exceptions import SSLError as _SSLError
+from .packages.urllib3.exceptions import HTTPError as _HTTPError
+from .packages.urllib3 import connectionpool, poolmanager
+from .packages.urllib3.filepost import encode_multipart_formdata
+from .exceptions import (
+ ConnectionError, HTTPError, RequestException, Timeout, TooManyRedirects,
+ URLRequired, SSLError)
+from .utils import (
+ get_encoding_from_headers, stream_decode_response_unicode,
+ stream_decompress, guess_filename, requote_path)
+
+
+REDIRECT_STATI = (codes.moved, codes.found, codes.other, codes.temporary_moved)
+
+
+
+class Request(object):
+ """The :class:`Request <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,
+ cookies=None,
+ timeout=None,
+ redirect=False,
+ allow_redirects=False,
+ proxies=None,
+ hooks=None,
+ config=None,
+ _poolmanager=None,
+ verify=None):
+
+ #: Float describes the timeout of the request.
+ # (Use socket.setdefaulttimeout() as fallback)
+ self.timeout = timeout
+
+ #: Request URL.
+ self.url = url
+
+ #: Dictionary of HTTP Headers to attach to the :class:`Request <Request>`.
+ self.headers = dict(headers or [])
+
+ #: Dictionary of files to multipart upload (``{filename: content}``).
+ self.files = files
+
+ #: HTTP Method to use.
+ self.method = method
+
+ #: Dictionary or byte of request body data to attach to the
+ #: :class:`Request <Request>`.
+ self.data = None
+
+ #: Dictionary or byte of querystring data to attach to the
+ #: :class:`Request <Request>`.
+ self.params = None
+ self.params = dict(params or [])
+
+ #: True if :class:`Request <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 = dict(proxies or [])
+
+ self.data, self._enc_data = self._encode_params(data)
+ self.params, self._enc_params = self._encode_params(params)
+
+ #: :class:`Response <Response>` instance, containing
+ #: content and metadata of HTTP Response, once :attr:`sent <send>`.
+ self.response = Response()
+
+ #: Authentication tuple or object to attach to :class:`Request <Request>`.
+ self.auth = auth
+
+ #: CookieJar to attach to :class:`Request <Request>`.
+ self.cookies = dict(cookies or [])
+
+ #: Dictionary of configurations for this request.
+ self.config = dict(config or [])
+
+ #: True if Request has been sent.
+ self.sent = False
+
+ #: Event-handling hooks.
+ self.hooks = hooks
+
+ #: Session.
+ self.session = None
+
+ #: SSL Verification.
+ self.verify = verify
+
+ if headers:
+ headers = CaseInsensitiveDict(self.headers)
+ else:
+ headers = CaseInsensitiveDict()
+
+ # Add configured base headers.
+ for (k, v) in self.config.get('base_headers', {}).items():
+ if k not in headers:
+ headers[k] = v
+
+ self.headers = headers
+ self._poolmanager = _poolmanager
+
+ # Pre-request hook.
+ r = dispatch_hook('pre_request', hooks, self)
+ self.__dict__.update(r.__dict__)
+
+
+ def __repr__(self):
+ return '<Request [%s]>' % (self.method)
+
+
+ def _build_response(self, resp, is_error=False):
+ """Build internal :class:`Response <Response>` object
+ from given response.
+ """
+
+ def build(resp):
+
+ response = Response()
+
+ # Pass settings over.
+ response.config = self.config
+
+ if resp:
+
+ # Fallback to None if there's no status_code, for whatever reason.
+ response.status_code = getattr(resp, 'status', None)
+
+ # Make headers case-insensitive.
+ response.headers = CaseInsensitiveDict(getattr(resp, 'headers', None))
+
+ # Set encoding.
+ response.encoding = get_encoding_from_headers(response.headers)
+
+ # Start off with our local cookies.
+ cookies = self.cookies or dict()
+
+ # Add new cookies from the server.
+ if 'set-cookie' in response.headers:
+ cookie_header = response.headers['set-cookie']
+ cookies = oreos.dict_from_string(cookie_header)
+
+ # Save cookies in Response.
+ response.cookies = cookies
+
+ # No exceptions were harmed in the making of this request.
+ response.error = getattr(resp, 'error', None)
+
+ # Save original response for later.
+ response.raw = resp
+
+ if is_error:
+ response.error = resp
+
+ response.url = self.full_url
+
+ return response
+
+ history = []
+
+ r = build(resp)
+ cookies = self.cookies
+ self.cookies.update(r.cookies)
+
+ 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))
+ ):
+
+ if not len(history) < self.config.get('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, 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
+
+ # Remove the cookie headers that were sent.
+ headers = self.headers
+ try:
+ del headers['Cookie']
+ except KeyError:
+ pass
+
+ request = Request(
+ url=url,
+ headers=headers,
+ files=self.files,
+ method=method,
+ params=self.session.params,
+ auth=self.auth,
+ cookies=cookies,
+ redirect=True,
+ config=self.config,
+ timeout=self.timeout,
+ _poolmanager=self._poolmanager,
+ proxies = self.proxies,
+ verify = self.verify
+ )
+
+ request.send()
+ cookies.update(request.response.cookies)
+ r = request.response
+ self.cookies.update(r.cookies)
+
+ r.history = history
+
+ self.response = r
+ self.response.request = self
+ self.response.cookies.update(self.cookies)
+
+
+ @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, '__iter__'):
+ data = dict(data)
+
+ 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
+
+ @property
+ def full_url(self):
+ """Build the actual URL to use."""
+
+ if not self.url:
+ raise URLRequired()
+
+ # Support for unicode domain names and paths.
+ scheme, netloc, path, params, query, fragment = urlparse(self.url)
+
+ if not scheme:
+ raise ValueError()
+
+ netloc = netloc.encode('idna')
+
+ if isinstance(path, unicode):
+ path = path.encode('utf-8')
+
+ path = requote_path(path)
+
+ url = str(urlunparse([ scheme, netloc, path, params, query, fragment ]))
+
+ if self._enc_params:
+ if urlparse(url).query:
+ return '%s&%s' % (url, self._enc_params)
+ else:
+ return '%s?%s' % (url, self._enc_params)
+ else:
+ return url
+
+ @property
+ def path_url(self):
+ """Build the path URL to use."""
+
+ url = []
+
+ p = urlsplit(self.full_url)
+
+ # Proxies use full URLs.
+ if p.scheme in self.proxies:
+ return self.full_url
+
+ path = p.path
+ if not path:
+ path = '/'
+ url.append(path)
+
+ query = p.query
+ if query:
+ url.append('?')
+ url.append(query)
+
+ return ''.join(url)
+
+
+
+ def send(self, anyway=False, prefetch=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.
+ """
+
+ # Build the URL
+ url = self.full_url
+
+ # Logging
+ if self.config.get('verbose'):
+ self.config.get('verbose').write('%s %s %s\n' % (
+ datetime.now().isoformat(), self.method, url
+ ))
+
+ # Nottin' on you.
+ body = None
+ content_type = None
+
+ # Multi-part file uploads.
+ if self.files:
+ if not isinstance(self.data, basestring):
+
+ try:
+ fields = self.data.copy()
+ except AttributeError:
+ fields = dict(self.data)
+
+ for (k, v) in self.files.items():
+ # support for explicit filename
+ if isinstance(v, (tuple, list)):
+ fn, fp = v
+ else:
+ fn = guess_filename(v) or k
+ fp = v
+ fields.update({k: (fn, fp.read())})
+
+ (body, content_type) = encode_multipart_formdata(fields)
+ else:
+ pass
+ # TODO: Conflict?
+ else:
+ if self.data:
+
+ body = self._enc_data
+ if isinstance(self.data, basestring):
+ content_type = None
+ else:
+ content_type = 'application/x-www-form-urlencoded'
+
+ # Add content-type if it wasn't explicitly provided.
+ if (content_type) and (not 'content-type' in self.headers):
+ self.headers['Content-Type'] = content_type
+
+ if self.auth:
+ if isinstance(self.auth, tuple) and len(self.auth) == 2:
+ # special-case basic HTTP auth
+ self.auth = HTTPBasicAuth(*self.auth)
+
+ # Allow auth to make its changes.
+ r = self.auth(self)
+
+ # Update self to reflect the auth changes.
+ self.__dict__.update(r.__dict__)
+
+ _p = urlparse(url)
+ proxy = self.proxies.get(_p.scheme)
+
+ if proxy:
+ conn = poolmanager.proxy_from_url(proxy)
+ _proxy = urlparse(proxy)
+ if '@' in _proxy.netloc:
+ auth, url = _proxy.netloc.split('@', 1)
+ self.proxy_auth = HTTPProxyAuth(*auth.split(':', 1))
+ r = self.proxy_auth(self)
+ self.__dict__.update(r.__dict__)
+ else:
+ # Check to see if keep_alive is allowed.
+ if self.config.get('keep_alive'):
+ conn = self._poolmanager.connection_from_url(url)
+ else:
+ conn = connectionpool.connection_from_url(url)
+
+ if url.startswith('https') and self.verify:
+
+ cert_loc = None
+
+ # Allow self-specified cert location.
+ if self.verify is not True:
+ cert_loc = self.verify
+
+
+ # Look for configuration.
+ if not cert_loc:
+ cert_loc = os.environ.get('REQUESTS_CA_BUNDLE')
+
+ # Curl compatiblity.
+ if not cert_loc:
+ cert_loc = os.environ.get('CURL_CA_BUNDLE')
+
+ # Use the awesome certifi list.
+ if not cert_loc:
+ cert_loc = __import__('certifi').where()
+
+ conn.cert_reqs = 'CERT_REQUIRED'
+ conn.ca_certs = cert_loc
+
+ if not self.sent or anyway:
+
+ if self.cookies:
+
+ # Skip if 'cookie' header is explicitly set.
+ if 'cookie' not in self.headers:
+
+ # Simple cookie with our dict.
+ c = oreos.monkeys.SimpleCookie()
+ for (k, v) in self.cookies.items():
+ c[k] = v
+
+ # Turn it into a header.
+ cookie_header = c.output(header='', sep='; ').strip()
+
+ # Attach Cookie header to request.
+ self.headers['Cookie'] = cookie_header
+
+ try:
+ # The inner try .. except re-raises certain exceptions as
+ # internal exception types; the outer suppresses exceptions
+ # when safe mode is set.
+ try:
+ # Send the request.
+ r = conn.urlopen(
+ method=self.method,
+ url=self.path_url,
+ body=body,
+ headers=self.headers,
+ redirect=False,
+ assert_same_host=False,
+ preload_content=False,
+ decode_content=True,
+ retries=self.config.get('max_retries', 0),
+ timeout=self.timeout,
+ )
+ self.sent = True
+
+ except MaxRetryError, e:
+ raise ConnectionError(e)
+
+ except (_SSLError, _HTTPError), e:
+ if self.verify and isinstance(e, _SSLError):
+ raise SSLError(e)
+
+ raise Timeout('Request timed out.')
+
+ except RequestException, e:
+ if self.config.get('safe_mode', False):
+ # In safe mode, catch the exception and attach it to
+ # a blank urllib3.HTTPResponse object.
+ r = HTTPResponse()
+ r.error = e
+ else:
+ raise
+
+ self._build_response(r)
+
+ # Response manipulation hook.
+ self.response = dispatch_hook('response', self.hooks, self.response)
+
+ # Post-request hook.
+ r = dispatch_hook('post_request', self.hooks, self)
+ self.__dict__.update(r.__dict__)
+
+ # If prefetch is True, mark content as consumed.
+ if prefetch:
+ # Save the response.
+ self.response.content
+
+ if self.config.get('danger_mode'):
+ self.response.raise_for_status()
+
+ return self.sent
+
+
+class Response(object):
+ """The core :class:`Response <Response>` object. All
+ :class:`Request <Request>` objects contain a
+ :class:`response <Response>` attribute, which is an instance
+ of this class.
+ """
+
+ def __init__(self):
+
+ self._content = None
+ self._content_consumed = False
+
+ #: 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()
+
+ #: File-like object representation of response (for advanced usage).
+ self.raw = None
+
+ #: Final URL location of Response.
+ self.url = None
+
+ #: Resulting :class:`HTTPError` of request, if one occurred.
+ self.error = None
+
+ #: Encoding to decode with when accessing r.content.
+ self.encoding = None
+
+ #: A list of :class:`Response <Response>` objects from
+ #: the history of the Request. Any redirect responses will end
+ #: up here.
+ self.history = []
+
+ #: The :class:`Request <Request>` that created the Response.
+ self.request = None
+
+ #: A dictionary of Cookies the server sent back.
+ self.cookies = {}
+
+ #: Dictionary of configurations for this request.
+ self.config = {}
+
+
+ def __repr__(self):
+ return '<Response [%s]>' % (self.status_code)
+
+ def __nonzero__(self):
+ """Returns true if :attr:`status_code` is 'OK'."""
+ return self.ok
+
+ @property
+ def ok(self):
+ try:
+ self.raise_for_status()
+ except HTTPError:
+ return False
+ return True
+
+
+ def iter_content(self, chunk_size=10 * 1024, decode_unicode=None):
+ """Iterates over the response data. This avoids reading the content
+ at once into memory for large responses. The chunk size is the number
+ of bytes it should read into memory. This is not necessarily the
+ length of each item returned as decoding can take place.
+ """
+ if self._content_consumed:
+ raise RuntimeError(
+ 'The content for this response was already consumed'
+ )
+
+ def generate():
+ while 1:
+ chunk = self.raw.read(chunk_size)
+ if not chunk:
+ break
+ yield chunk
+ self._content_consumed = True
+
+ gen = generate()
+
+ if 'gzip' in self.headers.get('content-encoding', ''):
+ gen = stream_decompress(gen, mode='gzip')
+ elif 'deflate' in self.headers.get('content-encoding', ''):
+ gen = stream_decompress(gen, mode='deflate')
+
+ if decode_unicode is None:
+ decode_unicode = self.config.get('decode_unicode')
+
+ if decode_unicode:
+ gen = stream_decode_response_unicode(gen, self)
+
+ return gen
+
+
+ def iter_lines(self, chunk_size=10 * 1024, decode_unicode=None):
+ """Iterates over the response data, one line at a time. This
+ avoids reading the content at once into memory for large
+ responses.
+ """
+
+ pending = None
+ for chunk in self.iter_content(chunk_size, decode_unicode=decode_unicode):
+ if pending is not None:
+ chunk = pending + chunk
+ lines = chunk.splitlines(True)
+ for line in lines[:-1]:
+ yield line.rstrip()
+ # Save the last part of the chunk for next iteration, to keep full line together
+ pending = lines[-1]
+
+ # Yield the last line
+ if pending is not None:
+ yield pending.rstrip()
+
+
+ @property
+ def content(self):
+ """Content of the response, in bytes or unicode
+ (if available).
+ """
+
+ if self._content is None:
+ # Read the contents.
+ try:
+ if self._content_consumed:
+ raise RuntimeError(
+ 'The content for this response was already consumed')
+
+ self._content = self.raw.read()
+ except AttributeError:
+ self._content = None
+
+ content = self._content
+
+ # Decode unicode content.
+ if self.config.get('decode_unicode'):
+
+ # Try charset from content-type
+
+ if self.encoding:
+ try:
+ content = unicode(content, self.encoding)
+ except UnicodeError:
+ pass
+
+ # Fall back:
+ try:
+ content = unicode(content, self.encoding, errors='replace')
+ except TypeError:
+ pass
+
+ self._content_consumed = True
+ return content
+
+
+ def raise_for_status(self):
+ """Raises stored :class:`HTTPError` or :class:`URLError`, if one occurred."""
+
+ if self.error:
+ raise self.error
+
+ if (self.status_code >= 300) and (self.status_code < 400):
+ raise HTTPError('%s Redirection' % self.status_code)
+
+ elif (self.status_code >= 400) and (self.status_code < 500):
+ raise HTTPError('%s Client Error' % self.status_code)
+
+ elif (self.status_code >= 500) and (self.status_code < 600):
+ raise HTTPError('%s Server Error' % self.status_code)
+
+
View
3  lib/python/requests/packages/__init__.py
@@ -0,0 +1,3 @@
+from __future__ import absolute_import
+
+from . import urllib3
View
3  lib/python/requests/packages/oreos/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from .core import dict_from_string
View
24 lib/python/requests/packages/oreos/core.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+
+"""
+oreos.core
+~~~~~~~~~~
+
+The creamy white center.
+"""
+
+from .monkeys import SimpleCookie
+
+
+def dict_from_string(s):
+ """Returns a MultiDict with Cookies."""
+
+ cookies = dict()
+
+ c = SimpleCookie()
+ c.load(s)
+
+ for k,v in c.items():
+ cookies.update({k: v.value})
+
+ return cookies
View
770 lib/python/requests/packages/oreos/monkeys.py
@@ -0,0 +1,770 @@
+# -*- coding: utf-8 -*-
+
+"""
+oreos.monkeys
+~~~~~~~~~~~~~
+
+Monkeypatches.
+"""
+#!/usr/bin/env python
+#
+
+####
+# Copyright 2000 by Timothy O'Malley <timo@alum.mit.edu>
+#
+# All Rights Reserved
+#
+# Permission to use, copy, modify, and distribute this software
+# and its documentation for any purpose and without fee is hereby
+# granted, provided that the above copyright notice appear in all
+# copies and that both that copyright notice and this permission
+# notice appear in supporting documentation, and that the name of
+# Timothy O'Malley not be used in advertising or publicity
+# pertaining to distribution of the software without specific, written
+# prior permission.
+#
+# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR
+# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+#
+####
+#
+# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp
+# by Timothy O'Malley <timo@alum.mit.edu>
+#
+# Cookie.py is a Python module for the handling of HTTP
+# cookies as a Python dictionary. See RFC 2109 for more
+# information on cookies.
+#
+# The original idea to treat Cookies as a dictionary came from
+# Dave Mitchell (davem@magnet.com) in 1995, when he released the
+# first version of nscookie.py.
+#
+####
+
+r"""
+Here's a sample session to show how to use this module.
+At the moment, this is the only documentation.
+
+The Basics
+----------
+
+Importing is easy..
+
+ >>> import Cookie
+
+Most of the time you start by creating a cookie. Cookies come in
+three flavors, each with slightly different encoding semantics, but
+more on that later.
+
+ >>> C = Cookie.SimpleCookie()
+ >>> C = Cookie.SerialCookie()
+ >>> C = Cookie.SmartCookie()
+
+[Note: Long-time users of Cookie.py will remember using
+Cookie.Cookie() to create an Cookie object. Although deprecated, it
+is still supported by the code. See the Backward Compatibility notes
+for more information.]
+
+Once you've created your Cookie, you can add values just as if it were
+a dictionary.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C["fig"] = "newton"
+ >>> C["sugar"] = "wafer"
+ >>> C.output()
+ 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer'
+
+Notice that the printable representation of a Cookie is the
+appropriate format for a Set-Cookie: header. This is the
+default behavior. You can change the header and printed
+attributes by using the .output() function
+
+ >>> C = Cookie.SmartCookie()
+ >>> C["rocky"] = "road"
+ >>> C["rocky"]["path"] = "/cookie"
+ >>> print C.output(header="Cookie:")
+ Cookie: rocky=road; Path=/cookie
+ >>> print C.output(attrs=[], header="Cookie:")
+ Cookie: rocky=road
+
+The load() method of a Cookie extracts cookies from a string. In a
+CGI script, you would use this method to extract the cookies from the
+HTTP_COOKIE environment variable.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C.load("chips=ahoy; vienna=finger")
+ >>> C.output()
+ 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger'
+
+The load() method is darn-tootin smart about identifying cookies
+within a string. Escaped quotation marks, nested semicolons, and other
+such trickeries do not confuse it.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
+ >>> print C
+ Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
+
+Each element of the Cookie also supports all of the RFC 2109
+Cookie attributes. Here's an example which sets the Path
+attribute.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C["oreo"] = "doublestuff"
+ >>> C["oreo"]["path"] = "/"
+ >>> print C
+ Set-Cookie: oreo=doublestuff; Path=/
+
+Each dictionary element has a 'value' attribute, which gives you
+back the value associated with the key.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C["twix"] = "none for you"
+ >>> C["twix"].value
+ 'none for you'
+
+
+A Bit More Advanced
+-------------------
+
+As mentioned before, there are three different flavors of Cookie
+objects, each with different encoding/decoding semantics. This
+section briefly discusses the differences.
+
+SimpleCookie
+
+The SimpleCookie expects that all values should be standard strings.
+Just to be sure, SimpleCookie invokes the str() builtin to convert
+the value to a string, when the values are set dictionary-style.
+
+ >>> C = Cookie.SimpleCookie()
+ >>> C["number"] = 7
+ >>> C["string"] = "seven"
+ >>> C["number"].value
+ '7'
+ >>> C["string"].value
+ 'seven'
+ >>> C.output()
+ 'Set-Cookie: number=7\r\nSet-Cookie: string=seven'
+
+
+SerialCookie
+
+The SerialCookie expects that all values should be serialized using
+cPickle (or pickle, if cPickle isn't available). As a result of
+serializing, SerialCookie can save almost any Python object to a
+value, and recover the exact same object when the cookie has been
+returned. (SerialCookie can yield some strange-looking cookie
+values, however.)
+
+ >>> C = Cookie.SerialCookie()
+ >>> C["number"] = 7
+ >>> C["string"] = "seven"
+ >>> C["number"].value
+ 7
+ >>> C["string"].value
+ 'seven'
+ >>> C.output()
+ 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string="S\'seven\'\\012p1\\012."'
+
+Be warned, however, if SerialCookie cannot de-serialize a value (because
+it isn't a valid pickle'd object), IT WILL RAISE AN EXCEPTION.
+
+
+SmartCookie
+
+The SmartCookie combines aspects of each of the other two flavors.
+When setting a value in a dictionary-fashion, the SmartCookie will
+serialize (ala cPickle) the value *if and only if* it isn't a
+Python string. String objects are *not* serialized. Similarly,
+when the load() method parses out values, it attempts to de-serialize
+the value. If it fails, then it fallsback to treating the value
+as a string.
+
+ >>> C = Cookie.SmartCookie()
+ >>> C["number"] = 7
+ >>> C["string"] = "seven"
+ >>> C["number"].value
+ 7
+ >>> C["string"].value
+ 'seven'
+ >>> C.output()
+ 'Set-Cookie: number="I7\\012."\r\nSet-Cookie: string=seven'
+
+
+Backwards Compatibility
+-----------------------
+
+In order to keep compatibilty with earlier versions of Cookie.py,
+it is still possible to use Cookie.Cookie() to create a Cookie. In
+fact, this simply returns a SmartCookie.
+
+ >>> C = Cookie.Cookie()
+ >>> print C.__class__.__name__
+ SmartCookie
+
+
+Finis.
+""" #"
+# ^
+# |----helps out font-lock
+
+#
+# Import our required modules
+#
+import string
+
+try:
+ from cPickle import dumps, loads
+except ImportError:
+ from pickle import dumps, loads
+
+import re, warnings
+
+__all__ = ["CookieError","BaseCookie","SimpleCookie","SerialCookie",
+ "SmartCookie","Cookie"]
+
+_nulljoin = ''.join
+_semispacejoin = '; '.join
+_spacejoin = ' '.join
+
+#
+# Define an exception visible to External modules
+#
+class CookieError(Exception):
+ pass
+
+
+# These quoting routines conform to the RFC2109 specification, which in
+# turn references the character definitions from RFC2068. They provide
+# a two-way quoting algorithm. Any non-text character is translated
+# into a 4 character sequence: a forward-slash followed by the
+# three-digit octal equivalent of the character. Any '\' or '"' is
+# quoted with a preceeding '\' slash.
+#
+# These are taken from RFC2068 and RFC2109.
+# _LegalChars is the list of chars which don't require "'s
+# _Translator hash-table for fast quoting
+#
+_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~[]_"
+_Translator = {
+ '\000' : '\\000', '\001' : '\\001', '\002' : '\\002',
+ '\003' : '\\003', '\004' : '\\004', '\005' : '\\005',
+ '\006' : '\\006', '\007' : '\\007', '\010' : '\\010',
+ '\011' : '\\011', '\012' : '\\012', '\013' : '\\013',
+ '\014' : '\\014', '\015' : '\\015', '\016' : '\\016',
+ '\017' : '\\017', '\020' : '\\020', '\021' : '\\021',
+ '\022' : '\\022', '\023' : '\\023', '\024' : '\\024',
+ '\025' : '\\025', '\026' : '\\026', '\027' : '\\027',
+ '\030' : '\\030', '\031' : '\\031', '\032' : '\\032',
+ '\033' : '\\033', '\034' : '\\034', '\035' : '\\035',
+ '\036' : '\\036', '\037' : '\\037',
+
+ # Because of the way browsers really handle cookies (as opposed
+ # to what the RFC says) we also encode , and ;
+
+ ',' : '\\054', ';' : '\\073',
+
+ '"' : '\\"', '\\' : '\\\\',
+
+ '\177' : '\\177', '\200' : '\\200', '\201' : '\\201',
+ '\202' : '\\202', '\203' : '\\203', '\204' : '\\204',
+ '\205' : '\\205', '\206' : '\\206', '\207' : '\\207',
+ '\210' : '\\210', '\211' : '\\211', '\212' : '\\212',
+ '\213' : '\\213', '\214' : '\\214', '\215' : '\\215',
+ '\216' : '\\216', '\217' : '\\217', '\220' : '\\220',
+ '\221' : '\\221', '\222' : '\\222', '\223' : '\\223',
+ '\224' : '\\224', '\225' : '\\225', '\226' : '\\226',
+ '\227' : '\\227', '\230' : '\\230', '\231' : '\\231',
+ '\232' : '\\232', '\233' : '\\233', '\234' : '\\234',
+ '\235' : '\\235', '\236' : '\\236', '\237' : '\\237',
+ '\240' : '\\240', '\241' : '\\241', '\242' : '\\242',
+ '\243' : '\\243', '\244' : '\\244', '\245' : '\\245',
+ '\246' : '\\246', '\247' : '\\247', '\250' : '\\250',
+ '\251' : '\\251', '\252' : '\\252', '\253' : '\\253',
+ '\254' : '\\254', '\255' : '\\255', '\256' : '\\256',
+ '\257' : '\\257', '\260' : '\\260', '\261' : '\\261',
+ '\262' : '\\262', '\263' : '\\263', '\264' : '\\264',
+ '\265' : '\\265', '\266' : '\\266', '\267' : '\\267',
+ '\270' : '\\270', '\271' : '\\271', '\272' : '\\272',
+ '\273' : '\\273', '\274' : '\\274', '\275' : '\\275',
+ '\276' : '\\276', '\277' : '\\277', '\300' : '\\300',
+ '\301' : '\\301', '\302' : '\\302', '\303' : '\\303',
+ '\304' : '\\304', '\305' : '\\305', '\306' : '\\306',
+ '\307' : '\\307', '\310' : '\\310', '\311' : '\\311',
+ '\312' : '\\312', '\313' : '\\313', '\314' : '\\314',
+ '\315' : '\\315', '\316' : '\\316', '\317' : '\\317',
+ '\320' : '\\320', '\321' : '\\321', '\322' : '\\322',
+ '\323' : '\\323', '\324' : '\\324', '\325' : '\\325',
+ '\326' : '\\326', '\327' : '\\327', '\330' : '\\330',
+ '\331' : '\\331', '\332' : '\\332', '\333' : '\\333',
+ '\334' : '\\334', '\335' : '\\335', '\336' : '\\336',
+ '\337' : '\\337', '\340' : '\\340', '\341' : '\\341',
+ '\342' : '\\342', '\343' : '\\343', '\344' : '\\344',
+ '\345' : '\\345', '\346' : '\\346', '\347' : '\\347',
+ '\350' : '\\350', '\351' : '\\351', '\352' : '\\352',
+ '\353' : '\\353', '\354' : '\\354', '\355' : '\\355',
+ '\356' : '\\356', '\357' : '\\357', '\360' : '\\360',
+ '\361' : '\\361', '\362' : '\\362', '\363' : '\\363',
+ '\364' : '\\364', '\365' : '\\365', '\366' : '\\366',
+ '\367' : '\\367', '\370' : '\\370', '\371' : '\\371',
+ '\372' : '\\372', '\373' : '\\373', '\374' : '\\374',
+ '\375' : '\\375', '\376' : '\\376', '\377' : '\\377'
+ }
+
+_idmap = ''.join(chr(x) for x in xrange(256))
+
+def _quote(str, LegalChars=_LegalChars,
+ idmap=_idmap, translate=string.translate):
+ #
+ # If the string does not need to be double-quoted,
+ # then just return the string. Otherwise, surround
+ # the string in doublequotes and precede quote (with a \)
+ # special characters.
+ #
+ if "" == translate(str, idmap, LegalChars):
+ return str
+ else:
+ return '"' + _nulljoin( map(_Translator.get, str, str) ) + '"'
+# end _quote
+
+
+_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]")
+_QuotePatt = re.compile(r"[\\].")
+
+def _unquote(str):
+ # If there aren't any doublequotes,
+ # then there can't be any special characters. See RFC 2109.
+ if len(str) < 2:
+ return str
+ if str[0] != '"' or str[-1] != '"':
+ return str
+
+ # We have to assume that we must decode this string.
+ # Down to work.
+
+ # Remove the "s
+ str = str[1:-1]
+
+ # Check for special sequences. Examples:
+ # \012 --> \n
+ # \" --> "
+ #
+ i = 0
+ n = len(str)
+ res = []
+ while 0 <= i < n:
+ Omatch = _OctalPatt.search(str, i)
+ Qmatch = _QuotePatt.search(str, i)
+ if not Omatch and not Qmatch: # Neither matched
+ res.append(str[i:])
+ break
+ # else:
+ j = k = -1
+ if Omatch: j = Omatch.start(0)
+ if Qmatch: k = Qmatch.start(0)
+ if Qmatch and ( not Omatch or k < j ): # QuotePatt matched
+ res.append(str[i:k])
+ res.append(str[k+1])
+ i = k+2
+ else: # OctalPatt matched
+ res.append(str[i:j])
+ res.append( chr( int(str[j+1:j+4], 8) ) )
+ i = j+4
+ return _nulljoin(res)
+# end _unquote
+
+# The _getdate() routine is used to set the expiration time in
+# the cookie's HTTP header. By default, _getdate() returns the
+# current time in the appropriate "expires" format for a
+# Set-Cookie header. The one optional argument is an offset from
+# now, in seconds. For example, an offset of -3600 means "one hour ago".
+# The offset may be a floating point number.
+#
+
+_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+
+_monthname = [None,
+ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
+
+def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname):
+ from time import gmtime, time
+ now = time()
+ year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future)
+ return "%s, %02d-%3s-%4d %02d:%02d:%02d GMT" % \
+ (weekdayname[wd], day, monthname[month], year, hh, mm, ss)
+
+
+#
+# A class to hold ONE key,value pair.
+# In a cookie, each such pair may have several attributes.
+# so this class is used to keep the attributes associated
+# with the appropriate key,value pair.
+# This class also includes a coded_value attribute, which
+# is used to hold the network representation of the
+# value. This is most useful when Python objects are
+# pickled for network transit.
+#
+
+class Morsel(dict):
+ # RFC 2109 lists these attributes as reserved:
+ # path comment domain
+ # max-age secure version
+ #
+ # For historical reasons, these attributes are also reserved:
+ # expires
+ #
+ # This is an extension from Microsoft:
+ # httponly
+ #
+ # This dictionary provides a mapping from the lowercase
+ # variant on the left to the appropriate traditional
+ # formatting on the right.
+ _reserved = { "expires" : "expires",
+ "path" : "Path",
+ "comment" : "Comment",
+ "domain" : "Domain",
+ "max-age" : "Max-Age",
+ "secure" : "secure",
+ "httponly" : "httponly",
+ "version" : "Version",
+ }
+
+ def __init__(self):
+ # Set defaults
+ self.key = self.value = self.coded_value = None
+
+ # Set default attributes
+ for K in self._reserved:
+ dict.__setitem__(self, K, "")
+ # end __init__
+
+ def __setitem__(self, K, V):
+ K = K.lower()
+ if not K in self._reserved:
+ raise CookieError("Invalid Attribute %s" % K)
+ dict.__setitem__(self, K, V)
+ # end __setitem__
+
+ def isReservedKey(self, K):
+ return K.lower() in self._reserved
+ # end isReservedKey
+
+ def set(self, key, val, coded_val,
+ LegalChars=_LegalChars,
+ idmap=_idmap, translate=string.translate):
+ # First we verify that the key isn't a reserved word
+ # Second we make sure it only contains legal characters
+ if key.lower() in self._reserved:
+ raise CookieError("Attempt to set a reserved key: %s" % key)
+ if "" != translate(key, idmap, LegalChars):
+ raise CookieError("Illegal key value: %s" % key)
+
+ # It's a good key, so save it.
+ self.key = key
+ self.value = val
+ self.coded_value = coded_val
+ # end set
+
+ def output(self, attrs=None, header = "Set-Cookie:"):
+ return "%s %s" % ( header, self.OutputString(attrs) )
+
+ __str__ = output
+
+ def __repr__(self):
+ return '<%s: %s=%s>' % (self.__class__.__name__,
+ self.key, repr(self.value) )
+
+ def js_output(self, attrs=None):
+ # Print javascript
+ return """
+ <script type="text/javascript">
+ <!-- begin hiding
+ document.cookie = \"%s\";
+ // end hiding -->
+ </script>
+ """ % ( self.OutputString(attrs).replace('"',r'\"'), )
+ # end js_output()
+
+ def OutputString(self, attrs=None):
+ # Build up our result
+ #
+ result = []
+ RA = result.append
+
+ # First, the key=value pair
+ RA("%s=%s" % (self.key, self.coded_value))
+
+ # Now add any defined attributes
+ if attrs is None:
+ attrs = self._reserved
+ items = self.items()
+ items.sort()
+ for K,V in items:
+ if V == "": continue
+ if K not in attrs: continue
+ if K == "expires" and type(V) == type(1):
+ RA("%s=%s" % (self._reserved[K], _getdate(V)))
+ elif K == "max-age" and type(V) == type(1):
+ RA("%s=%d" % (self._reserved[K], V))
+ elif K == "secure":
+ RA(str(self._reserved[K]))
+ elif K == "httponly":
+ RA(str(self._reserved[K]))
+ else:
+ RA("%s=%s" % (self._reserved[K], V))
+
+ # Return the result
+ return _semispacejoin(result)
+ # end OutputString
+# end Morsel class
+
+
+
+#
+# Pattern for finding cookie
+#
+# This used to be strict parsing based on the RFC2109 and RFC2068
+# specifications. I have since discovered that MSIE 3.0x doesn't
+# follow the character rules outlined in those specs. As a
+# result, the parsing rules here are less strict.
+#
+
+_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=\[\]\_]"
+
+_CookiePattern = re.compile(
+ r"(?x)" # This is a Verbose pattern
+ r"(?P<key>" # Start of group 'key'
+ ""+ _LegalCharsPatt +"+?" # Any word of at least one letter, nongreedy
+ r")" # End of group 'key'
+ r"\s*=\s*" # Equal Sign
+ r"(?P<val>" # Start of group 'val'
+ r'"(?:[^\\"]|\\.)*"' # Any doublequoted string
+ r"|" # or
+ r"\w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr
+ r"|" # or
+ ""+ _LegalCharsPatt +"*" # Any word or empty string
+ r")" # End of group 'val'
+ r"\s*;?" # Probably ending in a semi-colon
+ )
+
+
+# At long last, here is the cookie class.
+# Using this class is almost just like using a dictionary.
+# See this module's docstring for example usage.
+#
+class BaseCookie(dict):
+ # A container class for a set of Morsels
+ #
+
+ def value_decode(self, val):
+ """real_value, coded_value = value_decode(STRING)
+ Called prior to setting a cookie's value from the network
+ representation. The VALUE is the value read from HTTP
+ header.
+ Override this function to modify the behavior of cookies.
+ """
+ return val, val
+ # end value_encode
+
+ def value_encode(self, val):
+ """real_value, coded_value = value_encode(VALUE)
+ Called prior to setting a cookie's value from the dictionary
+ representation. The VALUE is the value being assigned.
+ Override this function to modify the behavior of cookies.
+ """
+ strval = str(val)
+ return strval, strval
+ # end value_encode
+
+ def __init__(self, input=None):
+ if input: self.load(input)
+ # end __init__
+
+ def __set(self, key, real_value, coded_value):
+ """Private method for setting a cookie's value"""
+ M = self.get(key, Morsel())
+ M.set(key, real_value, coded_value)
+ dict.__setitem__(self, key, M)
+ # end __set
+
+ def __setitem__(self, key, value):
+ """Dictionary style assignment."""
+ rval, cval = self.value_encode(value)
+ self.__set(key, rval, cval)
+ # end __setitem__
+
+ def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"):
+ """Return a string suitable for HTTP."""
+ result = []
+ items = self.items()
+ items.sort()