Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Cleaned up the the http module. Moved all of the code from __init__.p…

…y to request.py, response.py and utils.py
  • Loading branch information...
commit b4066d7d2149782cd694b98d1a6ed5c7ee885f55 1 parent 22471a4
authored October 21, 2012
943  django/http/__init__.py
... ...
@@ -1,933 +1,10 @@
1  
-from __future__ import absolute_import, unicode_literals
2  
-
3  
-import copy
4  
-import datetime
5  
-from email.header import Header
6  
-import os
7  
-import re
8  
-import sys
9  
-import time
10  
-import warnings
11  
-
12  
-from io import BytesIO
13  
-from pprint import pformat
14  
-try:
15  
-    from urllib.parse import quote, parse_qsl, urlencode, urljoin, urlparse
16  
-except ImportError:     # Python 2
17  
-    from urllib import quote, urlencode
18  
-    from urlparse import parse_qsl, urljoin, urlparse
19  
-
20  
-from django.utils.six.moves import http_cookies
21  
-# Some versions of Python 2.7 and later won't need this encoding bug fix:
22  
-_cookie_encodes_correctly = http_cookies.SimpleCookie().value_encode(';') == (';', '"\\073"')
23  
-# See ticket #13007, http://bugs.python.org/issue2193 and http://trac.edgewall.org/ticket/2256
24  
-_tc = http_cookies.SimpleCookie()
25  
-try:
26  
-    _tc.load(str('foo:bar=1'))
27  
-    _cookie_allows_colon_in_names = True
28  
-except http_cookies.CookieError:
29  
-    _cookie_allows_colon_in_names = False
30  
-
31  
-if _cookie_encodes_correctly and _cookie_allows_colon_in_names:
32  
-    SimpleCookie = http_cookies.SimpleCookie
33  
-else:
34  
-    Morsel = http_cookies.Morsel
35  
-
36  
-    class SimpleCookie(http_cookies.SimpleCookie):
37  
-        if not _cookie_encodes_correctly:
38  
-            def value_encode(self, val):
39  
-                # Some browsers do not support quoted-string from RFC 2109,
40  
-                # including some versions of Safari and Internet Explorer.
41  
-                # These browsers split on ';', and some versions of Safari
42  
-                # are known to split on ', '. Therefore, we encode ';' and ','
43  
-
44  
-                # SimpleCookie already does the hard work of encoding and decoding.
45  
-                # It uses octal sequences like '\\012' for newline etc.
46  
-                # and non-ASCII chars. We just make use of this mechanism, to
47  
-                # avoid introducing two encoding schemes which would be confusing
48  
-                # and especially awkward for javascript.
49  
-
50  
-                # NB, contrary to Python docs, value_encode returns a tuple containing
51  
-                # (real val, encoded_val)
52  
-                val, encoded = super(SimpleCookie, self).value_encode(val)
53  
-
54  
-                encoded = encoded.replace(";", "\\073").replace(",","\\054")
55  
-                # If encoded now contains any quoted chars, we need double quotes
56  
-                # around the whole string.
57  
-                if "\\" in encoded and not encoded.startswith('"'):
58  
-                    encoded = '"' + encoded + '"'
59  
-
60  
-                return val, encoded
61  
-
62  
-        if not _cookie_allows_colon_in_names:
63  
-            def load(self, rawdata):
64  
-                self.bad_cookies = set()
65  
-                super(SimpleCookie, self).load(force_str(rawdata))
66  
-                for key in self.bad_cookies:
67  
-                    del self[key]
68  
-
69  
-            # override private __set() method:
70  
-            # (needed for using our Morsel, and for laxness with CookieError
71  
-            def _BaseCookie__set(self, key, real_value, coded_value):
72  
-                key = force_str(key)
73  
-                try:
74  
-                    M = self.get(key, Morsel())
75  
-                    M.set(key, real_value, coded_value)
76  
-                    dict.__setitem__(self, key, M)
77  
-                except http_cookies.CookieError:
78  
-                    self.bad_cookies.add(key)
79  
-                    dict.__setitem__(self, key, http_cookies.Morsel())
80  
-
81  
-
82  
-from django.conf import settings
83  
-from django.core import signing
84  
-from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
85  
-from django.core.files import uploadhandler
86  
-from django.http.multipartparser import MultiPartParser
87  
-from django.http.utils import *
88  
-from django.utils.datastructures import MultiValueDict, ImmutableList
89  
-from django.utils.encoding import force_bytes, force_str, force_text, iri_to_uri
90  
-from django.utils.http import cookie_date
91  
-from django.utils import six
92  
-from django.utils import timezone
93  
-
94  
-RESERVED_CHARS="!*'();:@&=+$,/?%#[]"
95  
-
96  
-absolute_http_url_re = re.compile(r"^https?://", re.I)
97  
-
98  
-class Http404(Exception):
99  
-    pass
100  
-
101  
-RAISE_ERROR = object()
102  
-
103  
-
104  
-def build_request_repr(request, path_override=None, GET_override=None,
105  
-                       POST_override=None, COOKIES_override=None,
106  
-                       META_override=None):
107  
-    """
108  
-    Builds and returns the request's representation string. The request's
109  
-    attributes may be overridden by pre-processed values.
110  
-    """
111  
-    # Since this is called as part of error handling, we need to be very
112  
-    # robust against potentially malformed input.
113  
-    try:
114  
-        get = (pformat(GET_override)
115  
-               if GET_override is not None
116  
-               else pformat(request.GET))
117  
-    except Exception:
118  
-        get = '<could not parse>'
119  
-    if request._post_parse_error:
120  
-        post = '<could not parse>'
121  
-    else:
122  
-        try:
123  
-            post = (pformat(POST_override)
124  
-                    if POST_override is not None
125  
-                    else pformat(request.POST))
126  
-        except Exception:
127  
-            post = '<could not parse>'
128  
-    try:
129  
-        cookies = (pformat(COOKIES_override)
130  
-                   if COOKIES_override is not None
131  
-                   else pformat(request.COOKIES))
132  
-    except Exception:
133  
-        cookies = '<could not parse>'
134  
-    try:
135  
-        meta = (pformat(META_override)
136  
-                if META_override is not None
137  
-                else pformat(request.META))
138  
-    except Exception:
139  
-        meta = '<could not parse>'
140  
-    path = path_override if path_override is not None else request.path
141  
-    return force_str('<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' %
142  
-                     (request.__class__.__name__,
143  
-                      path,
144  
-                      six.text_type(get),
145  
-                      six.text_type(post),
146  
-                      six.text_type(cookies),
147  
-                      six.text_type(meta)))
148  
-
149  
-class UnreadablePostError(IOError):
150  
-    pass
151  
-
152  
-class HttpRequest(object):
153  
-    """A basic HTTP request."""
154  
-
155  
-    # The encoding used in GET/POST dicts. None means use default setting.
156  
-    _encoding = None
157  
-    _upload_handlers = []
158  
-
159  
-    def __init__(self):
160  
-        self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {}
161  
-        self.path = ''
162  
-        self.path_info = ''
163  
-        self.method = None
164  
-        self._post_parse_error = False
165  
-
166  
-    def __repr__(self):
167  
-        return build_request_repr(self)
168  
-
169  
-    def get_host(self):
170  
-        """Returns the HTTP host using the environment or request headers."""
171  
-        # We try three options, in order of decreasing preference.
172  
-        if settings.USE_X_FORWARDED_HOST and (
173  
-            'HTTP_X_FORWARDED_HOST' in self.META):
174  
-            host = self.META['HTTP_X_FORWARDED_HOST']
175  
-        elif 'HTTP_HOST' in self.META:
176  
-            host = self.META['HTTP_HOST']
177  
-        else:
178  
-            # Reconstruct the host using the algorithm from PEP 333.
179  
-            host = self.META['SERVER_NAME']
180  
-            server_port = str(self.META['SERVER_PORT'])
181  
-            if server_port != ('443' if self.is_secure() else '80'):
182  
-                host = '%s:%s' % (host, server_port)
183  
-
184  
-        # Disallow potentially poisoned hostnames.
185  
-        if set(';/?@&=+$,').intersection(host):
186  
-            raise SuspiciousOperation('Invalid HTTP_HOST header: %s' % host)
187  
-
188  
-        return host
189  
-
190  
-    def get_full_path(self):
191  
-        # RFC 3986 requires query string arguments to be in the ASCII range.
192  
-        # Rather than crash if this doesn't happen, we encode defensively.
193  
-        return '%s%s' % (self.path, ('?' + iri_to_uri(self.META.get('QUERY_STRING', ''))) if self.META.get('QUERY_STRING', '') else '')
194  
-
195  
-    def get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None):
196  
-        """
197  
-        Attempts to return a signed cookie. If the signature fails or the
198  
-        cookie has expired, raises an exception... unless you provide the
199  
-        default argument in which case that value will be returned instead.
200  
-        """
201  
-        try:
202  
-            cookie_value = self.COOKIES[key]
203  
-        except KeyError:
204  
-            if default is not RAISE_ERROR:
205  
-                return default
206  
-            else:
207  
-                raise
208  
-        try:
209  
-            value = signing.get_cookie_signer(salt=key + salt).unsign(
210  
-                cookie_value, max_age=max_age)
211  
-        except signing.BadSignature:
212  
-            if default is not RAISE_ERROR:
213  
-                return default
214  
-            else:
215  
-                raise
216  
-        return value
217  
-
218  
-    def build_absolute_uri(self, location=None):
219  
-        """
220  
-        Builds an absolute URI from the location and the variables available in
221  
-        this request. If no location is specified, the absolute URI is built on
222  
-        ``request.get_full_path()``.
223  
-        """
224  
-        if not location:
225  
-            location = self.get_full_path()
226  
-        if not absolute_http_url_re.match(location):
227  
-            current_uri = '%s://%s%s' % ('https' if self.is_secure() else 'http',
228  
-                                         self.get_host(), self.path)
229  
-            location = urljoin(current_uri, location)
230  
-        return iri_to_uri(location)
231  
-
232  
-    def _is_secure(self):
233  
-        return os.environ.get("HTTPS") == "on"
234  
-
235  
-    def is_secure(self):
236  
-        # First, check the SECURE_PROXY_SSL_HEADER setting.
237  
-        if settings.SECURE_PROXY_SSL_HEADER:
238  
-            try:
239  
-                header, value = settings.SECURE_PROXY_SSL_HEADER
240  
-            except ValueError:
241  
-                raise ImproperlyConfigured('The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.')
242  
-            if self.META.get(header, None) == value:
243  
-                return True
244  
-
245  
-        # Failing that, fall back to _is_secure(), which is a hook for
246  
-        # subclasses to implement.
247  
-        return self._is_secure()
248  
-
249  
-    def is_ajax(self):
250  
-        return self.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
251  
-
252  
-    @property
253  
-    def encoding(self):
254  
-        return self._encoding
255  
-
256  
-    @encoding.setter
257  
-    def encoding(self, val):
258  
-        """
259  
-        Sets the encoding used for GET/POST accesses. If the GET or POST
260  
-        dictionary has already been created, it is removed and recreated on the
261  
-        next access (so that it is decoded correctly).
262  
-        """
263  
-        self._encoding = val
264  
-        if hasattr(self, '_get'):
265  
-            del self._get
266  
-        if hasattr(self, '_post'):
267  
-            del self._post
268  
-
269  
-    def _initialize_handlers(self):
270  
-        self._upload_handlers = [uploadhandler.load_handler(handler, self)
271  
-                                 for handler in settings.FILE_UPLOAD_HANDLERS]
272  
-
273  
-    @property
274  
-    def upload_handlers(self):
275  
-        if not self._upload_handlers:
276  
-            # If there are no upload handlers defined, initialize them from settings.
277  
-            self._initialize_handlers()
278  
-        return self._upload_handlers
279  
-
280  
-    @upload_handlers.setter
281  
-    def upload_handlers(self, upload_handlers):
282  
-        if hasattr(self, '_files'):
283  
-            raise AttributeError("You cannot set the upload handlers after the upload has been processed.")
284  
-        self._upload_handlers = upload_handlers
285  
-
286  
-    def parse_file_upload(self, META, post_data):
287  
-        """Returns a tuple of (POST QueryDict, FILES MultiValueDict)."""
288  
-        self.upload_handlers = ImmutableList(
289  
-            self.upload_handlers,
290  
-            warning="You cannot alter upload handlers after the upload has been processed."
291  
-        )
292  
-        parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding)
293  
-        return parser.parse()
294  
-
295  
-    @property
296  
-    def body(self):
297  
-        if not hasattr(self, '_body'):
298  
-            if self._read_started:
299  
-                raise Exception("You cannot access body after reading from request's data stream")
300  
-            try:
301  
-                self._body = self.read()
302  
-            except IOError as e:
303  
-                six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2])
304  
-            self._stream = BytesIO(self._body)
305  
-        return self._body
306  
-
307  
-    @property
308  
-    def raw_post_data(self):
309  
-        warnings.warn('HttpRequest.raw_post_data has been deprecated. Use HttpRequest.body instead.', DeprecationWarning)
310  
-        return self.body
311  
-
312  
-    def _mark_post_parse_error(self):
313  
-        self._post = QueryDict('')
314  
-        self._files = MultiValueDict()
315  
-        self._post_parse_error = True
316  
-
317  
-    def _load_post_and_files(self):
318  
-        """Populate self._post and self._files if the content-type is a form type"""
319  
-        if self.method != 'POST':
320  
-            self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
321  
-            return
322  
-        if self._read_started and not hasattr(self, '_body'):
323  
-            self._mark_post_parse_error()
324  
-            return
325  
-
326  
-        if self.META.get('CONTENT_TYPE', '').startswith('multipart/form-data'):
327  
-            if hasattr(self, '_body'):
328  
-                # Use already read data
329  
-                data = BytesIO(self._body)
330  
-            else:
331  
-                data = self
332  
-            try:
333  
-                self._post, self._files = self.parse_file_upload(self.META, data)
334  
-            except:
335  
-                # An error occured while parsing POST data. Since when
336  
-                # formatting the error the request handler might access
337  
-                # self.POST, set self._post and self._file to prevent
338  
-                # attempts to parse POST data again.
339  
-                # Mark that an error occured. This allows self.__repr__ to
340  
-                # be explicit about it instead of simply representing an
341  
-                # empty POST
342  
-                self._mark_post_parse_error()
343  
-                raise
344  
-        elif self.META.get('CONTENT_TYPE', '').startswith('application/x-www-form-urlencoded'):
345  
-            self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
346  
-        else:
347  
-            self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
348  
-
349  
-    ## File-like and iterator interface.
350  
-    ##
351  
-    ## Expects self._stream to be set to an appropriate source of bytes by
352  
-    ## a corresponding request subclass (e.g. WSGIRequest).
353  
-    ## Also when request data has already been read by request.POST or
354  
-    ## request.body, self._stream points to a BytesIO instance
355  
-    ## containing that data.
356  
-
357  
-    def read(self, *args, **kwargs):
358  
-        self._read_started = True
359  
-        return self._stream.read(*args, **kwargs)
360  
-
361  
-    def readline(self, *args, **kwargs):
362  
-        self._read_started = True
363  
-        return self._stream.readline(*args, **kwargs)
364  
-
365  
-    def xreadlines(self):
366  
-        while True:
367  
-            buf = self.readline()
368  
-            if not buf:
369  
-                break
370  
-            yield buf
371  
-
372  
-    __iter__ = xreadlines
373  
-
374  
-    def readlines(self):
375  
-        return list(iter(self))
376  
-
377  
-
378  
-class QueryDict(MultiValueDict):
379  
-    """
380  
-    A specialized MultiValueDict that takes a query string when initialized.
381  
-    This is immutable unless you create a copy of it.
382  
-
383  
-    Values retrieved from this class are converted from the given encoding
384  
-    (DEFAULT_CHARSET by default) to unicode.
385  
-    """
386  
-    # These are both reset in __init__, but is specified here at the class
387  
-    # level so that unpickling will have valid values
388  
-    _mutable = True
389  
-    _encoding = None
390  
-
391  
-    def __init__(self, query_string, mutable=False, encoding=None):
392  
-        super(QueryDict, self).__init__()
393  
-        if not encoding:
394  
-            encoding = settings.DEFAULT_CHARSET
395  
-        self.encoding = encoding
396  
-        if six.PY3:
397  
-            for key, value in parse_qsl(query_string or '',
398  
-                                        keep_blank_values=True,
399  
-                                        encoding=encoding):
400  
-                self.appendlist(key, value)
401  
-        else:
402  
-            for key, value in parse_qsl(query_string or '',
403  
-                                        keep_blank_values=True):
404  
-                self.appendlist(force_text(key, encoding, errors='replace'),
405  
-                                force_text(value, encoding, errors='replace'))
406  
-        self._mutable = mutable
407  
-
408  
-    @property
409  
-    def encoding(self):
410  
-        if self._encoding is None:
411  
-            self._encoding = settings.DEFAULT_CHARSET
412  
-        return self._encoding
413  
-
414  
-    @encoding.setter
415  
-    def encoding(self, value):
416  
-        self._encoding = value
417  
-
418  
-    def _assert_mutable(self):
419  
-        if not self._mutable:
420  
-            raise AttributeError("This QueryDict instance is immutable")
421  
-
422  
-    def __setitem__(self, key, value):
423  
-        self._assert_mutable()
424  
-        key = bytes_to_text(key, self.encoding)
425  
-        value = bytes_to_text(value, self.encoding)
426  
-        super(QueryDict, self).__setitem__(key, value)
427  
-
428  
-    def __delitem__(self, key):
429  
-        self._assert_mutable()
430  
-        super(QueryDict, self).__delitem__(key)
431  
-
432  
-    def __copy__(self):
433  
-        result = self.__class__('', mutable=True, encoding=self.encoding)
434  
-        for key, value in six.iterlists(self):
435  
-            result.setlist(key, value)
436  
-        return result
437  
-
438  
-    def __deepcopy__(self, memo):
439  
-        result = self.__class__('', mutable=True, encoding=self.encoding)
440  
-        memo[id(self)] = result
441  
-        for key, value in six.iterlists(self):
442  
-            result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo))
443  
-        return result
444  
-
445  
-    def setlist(self, key, list_):
446  
-        self._assert_mutable()
447  
-        key = bytes_to_text(key, self.encoding)
448  
-        list_ = [bytes_to_text(elt, self.encoding) for elt in list_]
449  
-        super(QueryDict, self).setlist(key, list_)
450  
-
451  
-    def setlistdefault(self, key, default_list=None):
452  
-        self._assert_mutable()
453  
-        return super(QueryDict, self).setlistdefault(key, default_list)
454  
-
455  
-    def appendlist(self, key, value):
456  
-        self._assert_mutable()
457  
-        key = bytes_to_text(key, self.encoding)
458  
-        value = bytes_to_text(value, self.encoding)
459  
-        super(QueryDict, self).appendlist(key, value)
460  
-
461  
-    def pop(self, key, *args):
462  
-        self._assert_mutable()
463  
-        return super(QueryDict, self).pop(key, *args)
464  
-
465  
-    def popitem(self):
466  
-        self._assert_mutable()
467  
-        return super(QueryDict, self).popitem()
468  
-
469  
-    def clear(self):
470  
-        self._assert_mutable()
471  
-        super(QueryDict, self).clear()
472  
-
473  
-    def setdefault(self, key, default=None):
474  
-        self._assert_mutable()
475  
-        key = bytes_to_text(key, self.encoding)
476  
-        default = bytes_to_text(default, self.encoding)
477  
-        return super(QueryDict, self).setdefault(key, default)
478  
-
479  
-    def copy(self):
480  
-        """Returns a mutable copy of this object."""
481  
-        return self.__deepcopy__({})
482  
-
483  
-    def urlencode(self, safe=None):
484  
-        """
485  
-        Returns an encoded string of all query string arguments.
486  
-
487  
-        :arg safe: Used to specify characters which do not require quoting, for
488  
-            example::
489  
-
490  
-                >>> q = QueryDict('', mutable=True)
491  
-                >>> q['next'] = '/a&b/'
492  
-                >>> q.urlencode()
493  
-                'next=%2Fa%26b%2F'
494  
-                >>> q.urlencode(safe='/')
495  
-                'next=/a%26b/'
496  
-
497  
-        """
498  
-        output = []
499  
-        if safe:
500  
-            safe = force_bytes(safe, self.encoding)
501  
-            encode = lambda k, v: '%s=%s' % ((quote(k, safe), quote(v, safe)))
502  
-        else:
503  
-            encode = lambda k, v: urlencode({k: v})
504  
-        for k, list_ in self.lists():
505  
-            k = force_bytes(k, self.encoding)
506  
-            output.extend([encode(k, force_bytes(v, self.encoding))
507  
-                           for v in list_])
508  
-        return '&'.join(output)
509  
-
510  
-
511  
-def parse_cookie(cookie):
512  
-    if cookie == '':
513  
-        return {}
514  
-    if not isinstance(cookie, http_cookies.BaseCookie):
515  
-        try:
516  
-            c = SimpleCookie()
517  
-            c.load(cookie)
518  
-        except http_cookies.CookieError:
519  
-            # Invalid cookie
520  
-            return {}
521  
-    else:
522  
-        c = cookie
523  
-    cookiedict = {}
524  
-    for key in c.keys():
525  
-        cookiedict[key] = c.get(key).value
526  
-    return cookiedict
527  
-
528  
-class BadHeaderError(ValueError):
529  
-    pass
530  
-
531  
-class HttpResponseBase(object):
532  
-    """
533  
-    An HTTP response base class with dictionary-accessed headers.
534  
-
535  
-    This class doesn't handle content. It should not be used directly.
536  
-    Use the HttpResponse and StreamingHttpResponse subclasses instead.
537  
-    """
538  
-
539  
-    status_code = 200
540  
-
541  
-    def __init__(self, content_type=None, status=None, mimetype=None):
542  
-        # _headers is a mapping of the lower-case name to the original case of
543  
-        # the header (required for working with legacy systems) and the header
544  
-        # value. Both the name of the header and its value are ASCII strings.
545  
-        self._headers = {}
546  
-        self._charset = settings.DEFAULT_CHARSET
547  
-        self._closable_objects = []
548  
-        if mimetype:
549  
-            warnings.warn("Using mimetype keyword argument is deprecated, use"
550  
-                          " content_type instead", PendingDeprecationWarning)
551  
-            content_type = mimetype
552  
-        if not content_type:
553  
-            content_type = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE,
554  
-                    self._charset)
555  
-        self.cookies = SimpleCookie()
556  
-        if status:
557  
-            self.status_code = status
558  
-
559  
-        self['Content-Type'] = content_type
560  
-
561  
-    def serialize_headers(self):
562  
-        """HTTP headers as a bytestring."""
563  
-        headers = [
564  
-            ('%s: %s' % (key, value)).encode('us-ascii')
565  
-            for key, value in self._headers.values()
566  
-        ]
567  
-        return b'\r\n'.join(headers)
568  
-
569  
-    if six.PY3:
570  
-        __bytes__ = serialize_headers
571  
-    else:
572  
-        __str__ = serialize_headers
573  
-
574  
-    def _convert_to_charset(self, value, charset, mime_encode=False):
575  
-        """Converts headers key/value to ascii/latin1 native strings.
576  
-
577  
-        `charset` must be 'ascii' or 'latin-1'. If `mime_encode` is True and
578  
-        `value` value can't be represented in the given charset, MIME-encoding
579  
-        is applied.
580  
-        """
581  
-        if not isinstance(value, (bytes, six.text_type)):
582  
-            value = str(value)
583  
-        try:
584  
-            if six.PY3:
585  
-                if isinstance(value, str):
586  
-                    # Ensure string is valid in given charset
587  
-                    value.encode(charset)
588  
-                else:
589  
-                    # Convert bytestring using given charset
590  
-                    value = value.decode(charset)
591  
-            else:
592  
-                if isinstance(value, str):
593  
-                    # Ensure string is valid in given charset
594  
-                    value.decode(charset)
595  
-                else:
596  
-                    # Convert unicode string to given charset
597  
-                    value = value.encode(charset)
598  
-        except UnicodeError as e:
599  
-            if mime_encode:
600  
-                # Wrapping in str() is a workaround for #12422 under Python 2.
601  
-                value = str(Header(value, 'utf-8').encode())
602  
-            else:
603  
-                e.reason += ', HTTP response headers must be in %s format' % charset
604  
-                raise
605  
-        if str('\n') in value or str('\r') in value:
606  
-            raise BadHeaderError("Header values can't contain newlines (got %r)" % value)
607  
-        return value
608  
-
609  
-    def __setitem__(self, header, value):
610  
-        header = self._convert_to_charset(header, 'ascii')
611  
-        value = self._convert_to_charset(value, 'latin1', mime_encode=True)
612  
-        self._headers[header.lower()] = (header, value)
613  
-
614  
-    def __delitem__(self, header):
615  
-        try:
616  
-            del self._headers[header.lower()]
617  
-        except KeyError:
618  
-            pass
619  
-
620  
-    def __getitem__(self, header):
621  
-        return self._headers[header.lower()][1]
622  
-
623  
-    def __getstate__(self):
624  
-        # SimpleCookie is not pickeable with pickle.HIGHEST_PROTOCOL, so we
625  
-        # serialise to a string instead
626  
-        state = self.__dict__.copy()
627  
-        state['cookies'] = str(state['cookies'])
628  
-        return state
629  
-
630  
-    def __setstate__(self, state):
631  
-        self.__dict__.update(state)
632  
-        self.cookies = SimpleCookie(self.cookies)
633  
-
634  
-    def has_header(self, header):
635  
-        """Case-insensitive check for a header."""
636  
-        return header.lower() in self._headers
637  
-
638  
-    __contains__ = has_header
639  
-
640  
-    def items(self):
641  
-        return self._headers.values()
642  
-
643  
-    def get(self, header, alternate=None):
644  
-        return self._headers.get(header.lower(), (None, alternate))[1]
645  
-
646  
-    def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
647  
-                   domain=None, secure=False, httponly=False):
648  
-        """
649  
-        Sets a cookie.
650  
-
651  
-        ``expires`` can be:
652  
-        - a string in the correct format,
653  
-        - a naive ``datetime.datetime`` object in UTC,
654  
-        - an aware ``datetime.datetime`` object in any time zone.
655  
-        If it is a ``datetime.datetime`` object then ``max_age`` will be calculated.
656  
-
657  
-        """
658  
-        self.cookies[key] = value
659  
-        if expires is not None:
660  
-            if isinstance(expires, datetime.datetime):
661  
-                if timezone.is_aware(expires):
662  
-                    expires = timezone.make_naive(expires, timezone.utc)
663  
-                delta = expires - expires.utcnow()
664  
-                # Add one second so the date matches exactly (a fraction of
665  
-                # time gets lost between converting to a timedelta and
666  
-                # then the date string).
667  
-                delta = delta + datetime.timedelta(seconds=1)
668  
-                # Just set max_age - the max_age logic will set expires.
669  
-                expires = None
670  
-                max_age = max(0, delta.days * 86400 + delta.seconds)
671  
-            else:
672  
-                self.cookies[key]['expires'] = expires
673  
-        if max_age is not None:
674  
-            self.cookies[key]['max-age'] = max_age
675  
-            # IE requires expires, so set it if hasn't been already.
676  
-            if not expires:
677  
-                self.cookies[key]['expires'] = cookie_date(time.time() +
678  
-                                                           max_age)
679  
-        if path is not None:
680  
-            self.cookies[key]['path'] = path
681  
-        if domain is not None:
682  
-            self.cookies[key]['domain'] = domain
683  
-        if secure:
684  
-            self.cookies[key]['secure'] = True
685  
-        if httponly:
686  
-            self.cookies[key]['httponly'] = True
687  
-
688  
-    def set_signed_cookie(self, key, value, salt='', **kwargs):
689  
-        value = signing.get_cookie_signer(salt=key + salt).sign(value)
690  
-        return self.set_cookie(key, value, **kwargs)
691  
-
692  
-    def delete_cookie(self, key, path='/', domain=None):
693  
-        self.set_cookie(key, max_age=0, path=path, domain=domain,
694  
-                        expires='Thu, 01-Jan-1970 00:00:00 GMT')
695  
-
696  
-    # Common methods used by subclasses
697  
-
698  
-    def make_bytes(self, value):
699  
-        """Turn a value into a bytestring encoded in the output charset."""
700  
-        # For backwards compatibility, this method supports values that are
701  
-        # unlikely to occur in real applications. It has grown complex and
702  
-        # should be refactored. It also overlaps __next__. See #18796.
703  
-        if self.has_header('Content-Encoding'):
704  
-            if isinstance(value, int):
705  
-                value = six.text_type(value)
706  
-            if isinstance(value, six.text_type):
707  
-                value = value.encode('ascii')
708  
-            # force conversion to bytes in case chunk is a subclass
709  
-            return bytes(value)
710  
-        else:
711  
-            return force_bytes(value, self._charset)
712  
-
713  
-    # These methods partially implement the file-like object interface.
714  
-    # See http://docs.python.org/lib/bltin-file-objects.html
715  
-
716  
-    # The WSGI server must call this method upon completion of the request.
717  
-    # See http://blog.dscpl.com.au/2012/10/obligations-for-calling-close-on.html
718  
-    def close(self):
719  
-        for closable in self._closable_objects:
720  
-            closable.close()
721  
-
722  
-    def write(self, content):
723  
-        raise Exception("This %s instance is not writable" % self.__class__.__name__)
724  
-
725  
-    def flush(self):
726  
-        pass
727  
-
728  
-    def tell(self):
729  
-        raise Exception("This %s instance cannot tell its position" % self.__class__.__name__)
730  
-
731  
-class HttpResponse(HttpResponseBase):
732  
-    """
733  
-    An HTTP response class with a string as content.
734  
-
735  
-    This content that can be read, appended to or replaced.
736  
-    """
737  
-
738  
-    streaming = False
739  
-
740  
-    def __init__(self, content='', *args, **kwargs):
741  
-        super(HttpResponse, self).__init__(*args, **kwargs)
742  
-        # Content is a bytestring. See the `content` property methods.
743  
-        self.content = content
744  
-
745  
-    def serialize(self):
746  
-        """Full HTTP message, including headers, as a bytestring."""
747  
-        return self.serialize_headers() + b'\r\n\r\n' + self.content
748  
-
749  
-    if six.PY3:
750  
-        __bytes__ = serialize
751  
-    else:
752  
-        __str__ = serialize
753  
-
754  
-    @property
755  
-    def content(self):
756  
-        return b''.join(self.make_bytes(e) for e in self._container)
757  
-
758  
-    @content.setter
759  
-    def content(self, value):
760  
-        if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)):
761  
-            self._container = value
762  
-            self._base_content_is_iter = True
763  
-            if hasattr(value, 'close'):
764  
-                self._closable_objects.append(value)
765  
-        else:
766  
-            self._container = [value]
767  
-            self._base_content_is_iter = False
768  
-
769  
-    def __iter__(self):
770  
-        self._iterator = iter(self._container)
771  
-        return self
772  
-
773  
-    def __next__(self):
774  
-        chunk = next(self._iterator)
775  
-        if isinstance(chunk, int):
776  
-            chunk = six.text_type(chunk)
777  
-        if isinstance(chunk, six.text_type):
778  
-            chunk = chunk.encode(self._charset)
779  
-        # force conversion to bytes in case chunk is a subclass
780  
-        return bytes(chunk)
781  
-
782  
-    next = __next__             # Python 2 compatibility
783  
-
784  
-    def write(self, content):
785  
-        if self._base_content_is_iter:
786  
-            raise Exception("This %s instance is not writable" % self.__class__.__name__)
787  
-        self._container.append(content)
788  
-
789  
-    def tell(self):
790  
-        if self._base_content_is_iter:
791  
-            raise Exception("This %s instance cannot tell its position" % self.__class__.__name__)
792  
-        return sum([len(chunk) for chunk in self])
793  
-
794  
-class StreamingHttpResponse(HttpResponseBase):
795  
-    """
796  
-    A streaming HTTP response class with an iterator as content.
797  
-
798  
-    This should only be iterated once, when the response is streamed to the
799  
-    client. However, it can be appended to or replaced with a new iterator
800  
-    that wraps the original content (or yields entirely new content).
801  
-    """
802  
-
803  
-    streaming = True
804  
-
805  
-    def __init__(self, streaming_content=(), *args, **kwargs):
806  
-        super(StreamingHttpResponse, self).__init__(*args, **kwargs)
807  
-        # `streaming_content` should be an iterable of bytestrings.
808  
-        # See the `streaming_content` property methods.
809  
-        self.streaming_content = streaming_content
810  
-
811  
-    @property
812  
-    def content(self):
813  
-        raise AttributeError("This %s instance has no `content` attribute. "
814  
-            "Use `streaming_content` instead." % self.__class__.__name__)
815  
-
816  
-    @property
817  
-    def streaming_content(self):
818  
-        return self._iterator
819  
-
820  
-    @streaming_content.setter
821  
-    def streaming_content(self, value):
822  
-        # Ensure we can never iterate on "value" more than once.
823  
-        self._iterator = iter(value)
824  
-        if hasattr(value, 'close'):
825  
-            self._closable_objects.append(value)
826  
-
827  
-    def __iter__(self):
828  
-        return self
829  
-
830  
-    def __next__(self):
831  
-        return self.make_bytes(next(self._iterator))
832  
-
833  
-    next = __next__             # Python 2 compatibility
834  
-
835  
-class CompatibleStreamingHttpResponse(StreamingHttpResponse):
836  
-    """
837  
-    This class maintains compatibility with middleware that doesn't know how
838  
-    to handle the content of a streaming response by exposing a `content`
839  
-    attribute that will consume and cache the content iterator when accessed.
840  
-
841  
-    These responses will stream only if no middleware attempts to access the
842  
-    `content` attribute. Otherwise, they will behave like a regular response,
843  
-    and raise a `PendingDeprecationWarning`.
844  
-    """
845  
-    @property
846  
-    def content(self):
847  
-        warnings.warn(
848  
-            'Accessing the `content` attribute on a streaming response is '
849  
-            'deprecated. Use the `streaming_content` attribute instead.',
850  
-            PendingDeprecationWarning)
851  
-        content = b''.join(self)
852  
-        self.streaming_content = [content]
853  
-        return content
854  
-
855  
-    @content.setter
856  
-    def content(self, content):
857  
-        warnings.warn(
858  
-            'Accessing the `content` attribute on a streaming response is '
859  
-            'deprecated. Use the `streaming_content` attribute instead.',
860  
-            PendingDeprecationWarning)
861  
-        self.streaming_content = [content]
862  
-
863  
-class HttpResponseRedirectBase(HttpResponse):
864  
-    allowed_schemes = ['http', 'https', 'ftp']
865  
-
866  
-    def __init__(self, redirect_to, *args, **kwargs):
867  
-        parsed = urlparse(redirect_to)
868  
-        if parsed.scheme and parsed.scheme not in self.allowed_schemes:
869  
-            raise SuspiciousOperation("Unsafe redirect to URL with protocol '%s'" % parsed.scheme)
870  
-        super(HttpResponseRedirectBase, self).__init__(*args, **kwargs)
871  
-        self['Location'] = iri_to_uri(redirect_to)
872  
-
873  
-class HttpResponseRedirect(HttpResponseRedirectBase):
874  
-    status_code = 302
875  
-
876  
-class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
877  
-    status_code = 301
878  
-
879  
-class HttpResponseNotModified(HttpResponse):
880  
-    status_code = 304
881  
-
882  
-    def __init__(self, *args, **kwargs):
883  
-        super(HttpResponseNotModified, self).__init__(*args, **kwargs)
884  
-        del self['content-type']
885  
-
886  
-    @HttpResponse.content.setter
887  
-    def content(self, value):
888  
-        if value:
889  
-            raise AttributeError("You cannot set content to a 304 (Not Modified) response")
890  
-        self._container = []
891  
-
892  
-class HttpResponseBadRequest(HttpResponse):
893  
-    status_code = 400
894  
-
895  
-class HttpResponseNotFound(HttpResponse):
896  
-    status_code = 404
897  
-
898  
-class HttpResponseForbidden(HttpResponse):
899  
-    status_code = 403
900  
-
901  
-class HttpResponseNotAllowed(HttpResponse):
902  
-    status_code = 405
903  
-
904  
-    def __init__(self, permitted_methods, *args, **kwargs):
905  
-        super(HttpResponseNotAllowed, self).__init__(*args, **kwargs)
906  
-        self['Allow'] = ', '.join(permitted_methods)
907  
-
908  
-class HttpResponseGone(HttpResponse):
909  
-    status_code = 410
910  
-
911  
-class HttpResponseServerError(HttpResponse):
912  
-    status_code = 500
913  
-
914  
-# A backwards compatible alias for HttpRequest.get_host.
915  
-def get_host(request):
916  
-    return request.get_host()
917  
-
918  
-# It's neither necessary nor appropriate to use
919  
-# django.utils.encoding.smart_text for parsing URLs and form inputs. Thus,
920  
-# this slightly more restricted function, used by QueryDict.
921  
-def bytes_to_text(s, encoding):
922  
-    """
923  
-    Converts basestring objects to unicode, using the given encoding. Illegally
924  
-    encoded input characters are replaced with Unicode "unknown" codepoint
925  
-    (\ufffd).
926  
-
927  
-    Returns any non-basestring objects without change.
928  
-    """
929  
-    if isinstance(s, bytes):
930  
-        return six.text_type(s, encoding, 'replace')
931  
-    else:
932  
-        return s
933  
-
  1
+from django.http.cookie import SimpleCookie, parse_cookie
  2
+from django.http.request import (HttpRequest, QueryDict, UnreadablePostError,
  3
+    build_request_repr)
  4
+from django.http.response import (HttpResponse, StreamingHttpResponse,
  5
+    CompatibleStreamingHttpResponse, HttpResponsePermanentRedirect,
  6
+    HttpResponseRedirect, HttpResponseNotModified, HttpResponseBadRequest,
  7
+    HttpResponseForbidden, HttpResponseNotFound, HttpResponseNotAllowed,
  8
+    HttpResponseGone, HttpResponseServerError, Http404, BadHeaderError)
  9
+from django.http.utils import (fix_location_header, conditional_content_removal,
  10
+    fix_IE_for_attach, fix_IE_for_vary)
83  django/http/cookie.py
... ...
@@ -0,0 +1,83 @@
453  django/http/request.py
... ...
@@ -0,0 +1,453 @@
  1
+from __future__ import absolute_import, unicode_literals
  2
+
  3
+import copy
  4
+import os
  5
+import re
  6
+import sys
  7
+import warnings
  8
+from io import BytesIO
  9
+from pprint import pformat
  10
+try:
  11
+    from urllib.parse import parse_qsl, urlencode, quote, urljoin
  12
+except ImportError:
  13
+    from urllib import urlencode, quote
  14
+    from urlparse import parse_qsl, urljoin
  15
+
  16
+from django.conf import settings
  17
+from django.core import signing
  18
+from django.core.exceptions import SuspiciousOperation, ImproperlyConfigured
  19
+from django.core.files import uploadhandler
  20
+from django.http.multipartparser import MultiPartParser
  21
+from django.utils import six
  22
+from django.utils.datastructures import MultiValueDict, ImmutableList
  23
+from django.utils.encoding import force_bytes, force_text, force_str, iri_to_uri
  24
+
  25
+
  26
+RAISE_ERROR = object()
  27
+absolute_http_url_re = re.compile(r"^https?://", re.I)
  28
+
  29
+
  30
+class UnreadablePostError(IOError):
  31
+    pass
  32
+
  33
+
  34
+class HttpRequest(object):
  35
+    """A basic HTTP request."""
  36
+
  37
+    # The encoding used in GET/POST dicts. None means use default setting.
  38
+    _encoding = None
  39
+    _upload_handlers = []
  40
+
  41
+    def __init__(self):
  42
+        self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {}
  43
+        self.path = ''
  44
+        self.path_info = ''
  45
+        self.method = None
  46
+        self._post_parse_error = False
  47
+
  48
+    def __repr__(self):
  49
+        return build_request_repr(self)
  50
+
  51
+    def get_host(self):
  52
+        """Returns the HTTP host using the environment or request headers."""
  53
+        # We try three options, in order of decreasing preference.
  54
+        if settings.USE_X_FORWARDED_HOST and (
  55
+            'HTTP_X_FORWARDED_HOST' in self.META):
  56
+            host = self.META['HTTP_X_FORWARDED_HOST']
  57
+        elif 'HTTP_HOST' in self.META:
  58
+            host = self.META['HTTP_HOST']
  59
+        else:
  60
+            # Reconstruct the host using the algorithm from PEP 333.
  61
+            host = self.META['SERVER_NAME']
  62
+            server_port = str(self.META['SERVER_PORT'])
  63
+            if server_port != ('443' if self.is_secure() else '80'):
  64
+                host = '%s:%s' % (host, server_port)
  65
+
  66
+        # Disallow potentially poisoned hostnames.
  67
+        if set(';/?@&=+$,').intersection(host):
  68
+            raise SuspiciousOperation('Invalid HTTP_HOST header: %s' % host)
  69
+
  70
+        return host
  71
+
  72
+    def get_full_path(self):
  73
+        # RFC 3986 requires query string arguments to be in the ASCII range.
  74
+        # Rather than crash if this doesn't happen, we encode defensively.
  75
+        return '%s%s' % (self.path, ('?' + iri_to_uri(self.META.get('QUERY_STRING', ''))) if self.META.get('QUERY_STRING', '') else '')
  76
+
  77
+    def get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None):
  78
+        """
  79
+        Attempts to return a signed cookie. If the signature fails or the
  80
+        cookie has expired, raises an exception... unless you provide the
  81
+        default argument in which case that value will be returned instead.
  82
+        """
  83
+        try:
  84
+            cookie_value = self.COOKIES[key]
  85
+        except KeyError:
  86
+            if default is not RAISE_ERROR:
  87
+                return default
  88
+            else:
  89
+                raise
  90
+        try:
  91
+            value = signing.get_cookie_signer(salt=key + salt).unsign(
  92
+                cookie_value, max_age=max_age)
  93
+        except signing.BadSignature:
  94
+            if default is not RAISE_ERROR:
  95
+                return default
  96
+            else:
  97
+                raise
  98
+        return value
  99
+
  100
+    def build_absolute_uri(self, location=None):
  101
+        """
  102
+        Builds an absolute URI from the location and the variables available in
  103
+        this request. If no location is specified, the absolute URI is built on
  104
+        ``request.get_full_path()``.
  105
+        """
  106
+        if not location:
  107
+            location = self.get_full_path()
  108
+        if not absolute_http_url_re.match(location):
  109
+            current_uri = '%s://%s%s' % ('https' if self.is_secure() else 'http',
  110
+                                         self.get_host(), self.path)
  111
+            location = urljoin(current_uri, location)
  112
+        return iri_to_uri(location)
  113
+
  114
+    def _is_secure(self):
  115
+        return os.environ.get("HTTPS") == "on"
  116
+
  117
+    def is_secure(self):
  118
+        # First, check the SECURE_PROXY_SSL_HEADER setting.
  119
+        if settings.SECURE_PROXY_SSL_HEADER:
  120
+            try:
  121
+                header, value = settings.SECURE_PROXY_SSL_HEADER
  122
+            except ValueError:
  123
+                raise ImproperlyConfigured('The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.')
  124
+            if self.META.get(header, None) == value:
  125
+                return True
  126
+
  127
+        # Failing that, fall back to _is_secure(), which is a hook for
  128
+        # subclasses to implement.
  129
+        return self._is_secure()
  130
+
  131
+    def is_ajax(self):
  132
+        return self.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
  133
+
  134
+    @property
  135
+    def encoding(self):
  136
+        return self._encoding
  137
+
  138
+    @encoding.setter
  139
+    def encoding(self, val):
  140
+        """
  141
+        Sets the encoding used for GET/POST accesses. If the GET or POST
  142
+        dictionary has already been created, it is removed and recreated on the
  143
+        next access (so that it is decoded correctly).
  144
+        """
  145
+        self._encoding = val
  146
+        if hasattr(self, '_get'):
  147
+            del self._get
  148
+        if hasattr(self, '_post'):
  149
+            del self._post
  150
+
  151
+    def _initialize_handlers(self):
  152
+        self._upload_handlers = [uploadhandler.load_handler(handler, self)