summaryrefslogtreecommitdiff
path: root/lib/python2.7/site-packages/django/http/request.py
diff options
context:
space:
mode:
authorttt2017-05-13 00:29:47 +0530
committerttt2017-05-13 00:29:47 +0530
commit4336f5f06f61de30ae3fa54650fce63a9d5ef5be (patch)
tree23b4ee9b8e8f24bf732acf2f7ad22ed50cdd5670 /lib/python2.7/site-packages/django/http/request.py
downloadSBHS-2018-Rpi-4336f5f06f61de30ae3fa54650fce63a9d5ef5be.tar.gz
SBHS-2018-Rpi-4336f5f06f61de30ae3fa54650fce63a9d5ef5be.tar.bz2
SBHS-2018-Rpi-4336f5f06f61de30ae3fa54650fce63a9d5ef5be.zip
added all server files
Diffstat (limited to 'lib/python2.7/site-packages/django/http/request.py')
-rw-r--r--lib/python2.7/site-packages/django/http/request.py514
1 files changed, 514 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/django/http/request.py b/lib/python2.7/site-packages/django/http/request.py
new file mode 100644
index 0000000..dee6910
--- /dev/null
+++ b/lib/python2.7/site-packages/django/http/request.py
@@ -0,0 +1,514 @@
+from __future__ import absolute_import, unicode_literals
+
+import copy
+import os
+import re
+import sys
+from io import BytesIO
+from pprint import pformat
+
+from django.conf import settings
+from django.core import signing
+from django.core.exceptions import DisallowedHost, ImproperlyConfigured
+from django.core.files import uploadhandler
+from django.http.multipartparser import MultiPartParser
+from django.utils import six
+from django.utils.datastructures import MultiValueDict, ImmutableList
+from django.utils.encoding import force_bytes, force_text, force_str, iri_to_uri
+from django.utils.six.moves.urllib.parse import parse_qsl, urlencode, quote, urljoin
+
+
+RAISE_ERROR = object()
+absolute_http_url_re = re.compile(r"^https?://", re.I)
+host_validation_re = re.compile(r"^([a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9:]+\])(:\d+)?$")
+
+
+class UnreadablePostError(IOError):
+ pass
+
+
+class HttpRequest(object):
+ """A basic HTTP request."""
+
+ # The encoding used in GET/POST dicts. None means use default setting.
+ _encoding = None
+ _upload_handlers = []
+
+ def __init__(self):
+ # WARNING: The `WSGIRequest` subclass doesn't call `super`.
+ # Any variable assignment made here should also happen in
+ # `WSGIRequest.__init__()`.
+
+ self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {}
+ self.path = ''
+ self.path_info = ''
+ self.method = None
+ self.resolver_match = None
+ self._post_parse_error = False
+
+ def __repr__(self):
+ return build_request_repr(self)
+
+ def get_host(self):
+ """Returns the HTTP host using the environment or request headers."""
+ # We try three options, in order of decreasing preference.
+ if settings.USE_X_FORWARDED_HOST and (
+ 'HTTP_X_FORWARDED_HOST' in self.META):
+ host = self.META['HTTP_X_FORWARDED_HOST']
+ elif 'HTTP_HOST' in self.META:
+ host = self.META['HTTP_HOST']
+ else:
+ # Reconstruct the host using the algorithm from PEP 333.
+ host = self.META['SERVER_NAME']
+ server_port = str(self.META['SERVER_PORT'])
+ if server_port != ('443' if self.is_secure() else '80'):
+ host = '%s:%s' % (host, server_port)
+
+ allowed_hosts = ['*'] if settings.DEBUG else settings.ALLOWED_HOSTS
+ domain, port = split_domain_port(host)
+ if domain and validate_host(domain, allowed_hosts):
+ return host
+ else:
+ msg = "Invalid HTTP_HOST header: %r." % host
+ if domain:
+ msg += "You may need to add %r to ALLOWED_HOSTS." % domain
+ raise DisallowedHost(msg)
+
+ def get_full_path(self):
+ # RFC 3986 requires query string arguments to be in the ASCII range.
+ # Rather than crash if this doesn't happen, we encode defensively.
+ return '%s%s' % (self.path, ('?' + iri_to_uri(self.META.get('QUERY_STRING', ''))) if self.META.get('QUERY_STRING', '') else '')
+
+ def get_signed_cookie(self, key, default=RAISE_ERROR, salt='', max_age=None):
+ """
+ Attempts to return a signed cookie. If the signature fails or the
+ cookie has expired, raises an exception... unless you provide the
+ default argument in which case that value will be returned instead.
+ """
+ try:
+ cookie_value = self.COOKIES[key]
+ except KeyError:
+ if default is not RAISE_ERROR:
+ return default
+ else:
+ raise
+ try:
+ value = signing.get_cookie_signer(salt=key + salt).unsign(
+ cookie_value, max_age=max_age)
+ except signing.BadSignature:
+ if default is not RAISE_ERROR:
+ return default
+ else:
+ raise
+ return value
+
+ def build_absolute_uri(self, location=None):
+ """
+ Builds an absolute URI from the location and the variables available in
+ this request. If no location is specified, the absolute URI is built on
+ ``request.get_full_path()``.
+ """
+ if not location:
+ location = self.get_full_path()
+ if not absolute_http_url_re.match(location):
+ current_uri = '%s://%s%s' % ('https' if self.is_secure() else 'http',
+ self.get_host(), self.path)
+ location = urljoin(current_uri, location)
+ return iri_to_uri(location)
+
+ def _is_secure(self):
+ return os.environ.get("HTTPS") == "on"
+
+ def is_secure(self):
+ # First, check the SECURE_PROXY_SSL_HEADER setting.
+ if settings.SECURE_PROXY_SSL_HEADER:
+ try:
+ header, value = settings.SECURE_PROXY_SSL_HEADER
+ except ValueError:
+ raise ImproperlyConfigured('The SECURE_PROXY_SSL_HEADER setting must be a tuple containing two values.')
+ if self.META.get(header, None) == value:
+ return True
+
+ # Failing that, fall back to _is_secure(), which is a hook for
+ # subclasses to implement.
+ return self._is_secure()
+
+ def is_ajax(self):
+ return self.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest'
+
+ @property
+ def encoding(self):
+ return self._encoding
+
+ @encoding.setter
+ def encoding(self, val):
+ """
+ Sets the encoding used for GET/POST accesses. If the GET or POST
+ dictionary has already been created, it is removed and recreated on the
+ next access (so that it is decoded correctly).
+ """
+ self._encoding = val
+ if hasattr(self, '_get'):
+ del self._get
+ if hasattr(self, '_post'):
+ del self._post
+
+ def _initialize_handlers(self):
+ self._upload_handlers = [uploadhandler.load_handler(handler, self)
+ for handler in settings.FILE_UPLOAD_HANDLERS]
+
+ @property
+ def upload_handlers(self):
+ if not self._upload_handlers:
+ # If there are no upload handlers defined, initialize them from settings.
+ self._initialize_handlers()
+ return self._upload_handlers
+
+ @upload_handlers.setter
+ def upload_handlers(self, upload_handlers):
+ if hasattr(self, '_files'):
+ raise AttributeError("You cannot set the upload handlers after the upload has been processed.")
+ self._upload_handlers = upload_handlers
+
+ def parse_file_upload(self, META, post_data):
+ """Returns a tuple of (POST QueryDict, FILES MultiValueDict)."""
+ self.upload_handlers = ImmutableList(
+ self.upload_handlers,
+ warning="You cannot alter upload handlers after the upload has been processed."
+ )
+ parser = MultiPartParser(META, post_data, self.upload_handlers, self.encoding)
+ return parser.parse()
+
+ @property
+ def body(self):
+ if not hasattr(self, '_body'):
+ if self._read_started:
+ raise Exception("You cannot access body after reading from request's data stream")
+ try:
+ self._body = self.read()
+ except IOError as e:
+ six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2])
+ self._stream = BytesIO(self._body)
+ return self._body
+
+ def _mark_post_parse_error(self):
+ self._post = QueryDict('')
+ self._files = MultiValueDict()
+ self._post_parse_error = True
+
+ def _load_post_and_files(self):
+ """Populate self._post and self._files if the content-type is a form type"""
+ if self.method != 'POST':
+ self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
+ return
+ if self._read_started and not hasattr(self, '_body'):
+ self._mark_post_parse_error()
+ return
+
+ if self.META.get('CONTENT_TYPE', '').startswith('multipart/form-data'):
+ if hasattr(self, '_body'):
+ # Use already read data
+ data = BytesIO(self._body)
+ else:
+ data = self
+ try:
+ self._post, self._files = self.parse_file_upload(self.META, data)
+ except:
+ # An error occured while parsing POST data. Since when
+ # formatting the error the request handler might access
+ # self.POST, set self._post and self._file to prevent
+ # attempts to parse POST data again.
+ # Mark that an error occured. This allows self.__repr__ to
+ # be explicit about it instead of simply representing an
+ # empty POST
+ self._mark_post_parse_error()
+ raise
+ elif self.META.get('CONTENT_TYPE', '').startswith('application/x-www-form-urlencoded'):
+ self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
+ else:
+ self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
+
+ ## File-like and iterator interface.
+ ##
+ ## Expects self._stream to be set to an appropriate source of bytes by
+ ## a corresponding request subclass (e.g. WSGIRequest).
+ ## Also when request data has already been read by request.POST or
+ ## request.body, self._stream points to a BytesIO instance
+ ## containing that data.
+
+ def read(self, *args, **kwargs):
+ self._read_started = True
+ try:
+ return self._stream.read(*args, **kwargs)
+ except IOError as e:
+ six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2])
+
+ def readline(self, *args, **kwargs):
+ self._read_started = True
+ try:
+ return self._stream.readline(*args, **kwargs)
+ except IOError as e:
+ six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2])
+
+ def xreadlines(self):
+ while True:
+ buf = self.readline()
+ if not buf:
+ break
+ yield buf
+
+ __iter__ = xreadlines
+
+ def readlines(self):
+ return list(iter(self))
+
+
+class QueryDict(MultiValueDict):
+ """
+ A specialized MultiValueDict that takes a query string when initialized.
+ This is immutable unless you create a copy of it.
+
+ Values retrieved from this class are converted from the given encoding
+ (DEFAULT_CHARSET by default) to unicode.
+ """
+ # These are both reset in __init__, but is specified here at the class
+ # level so that unpickling will have valid values
+ _mutable = True
+ _encoding = None
+
+ def __init__(self, query_string, mutable=False, encoding=None):
+ super(QueryDict, self).__init__()
+ if not encoding:
+ encoding = settings.DEFAULT_CHARSET
+ self.encoding = encoding
+ if six.PY3:
+ if isinstance(query_string, bytes):
+ # query_string contains URL-encoded data, a subset of ASCII.
+ query_string = query_string.decode()
+ for key, value in parse_qsl(query_string or '',
+ keep_blank_values=True,
+ encoding=encoding):
+ self.appendlist(key, value)
+ else:
+ for key, value in parse_qsl(query_string or '',
+ keep_blank_values=True):
+ self.appendlist(force_text(key, encoding, errors='replace'),
+ force_text(value, encoding, errors='replace'))
+ self._mutable = mutable
+
+ @property
+ def encoding(self):
+ if self._encoding is None:
+ self._encoding = settings.DEFAULT_CHARSET
+ return self._encoding
+
+ @encoding.setter
+ def encoding(self, value):
+ self._encoding = value
+
+ def _assert_mutable(self):
+ if not self._mutable:
+ raise AttributeError("This QueryDict instance is immutable")
+
+ def __setitem__(self, key, value):
+ self._assert_mutable()
+ key = bytes_to_text(key, self.encoding)
+ value = bytes_to_text(value, self.encoding)
+ super(QueryDict, self).__setitem__(key, value)
+
+ def __delitem__(self, key):
+ self._assert_mutable()
+ super(QueryDict, self).__delitem__(key)
+
+ def __copy__(self):
+ result = self.__class__('', mutable=True, encoding=self.encoding)
+ for key, value in six.iterlists(self):
+ result.setlist(key, value)
+ return result
+
+ def __deepcopy__(self, memo):
+ result = self.__class__('', mutable=True, encoding=self.encoding)
+ memo[id(self)] = result
+ for key, value in six.iterlists(self):
+ result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo))
+ return result
+
+ def setlist(self, key, list_):
+ self._assert_mutable()
+ key = bytes_to_text(key, self.encoding)
+ list_ = [bytes_to_text(elt, self.encoding) for elt in list_]
+ super(QueryDict, self).setlist(key, list_)
+
+ def setlistdefault(self, key, default_list=None):
+ self._assert_mutable()
+ return super(QueryDict, self).setlistdefault(key, default_list)
+
+ def appendlist(self, key, value):
+ self._assert_mutable()
+ key = bytes_to_text(key, self.encoding)
+ value = bytes_to_text(value, self.encoding)
+ super(QueryDict, self).appendlist(key, value)
+
+ def pop(self, key, *args):
+ self._assert_mutable()
+ return super(QueryDict, self).pop(key, *args)
+
+ def popitem(self):
+ self._assert_mutable()
+ return super(QueryDict, self).popitem()
+
+ def clear(self):
+ self._assert_mutable()
+ super(QueryDict, self).clear()
+
+ def setdefault(self, key, default=None):
+ self._assert_mutable()
+ key = bytes_to_text(key, self.encoding)
+ default = bytes_to_text(default, self.encoding)
+ return super(QueryDict, self).setdefault(key, default)
+
+ def copy(self):
+ """Returns a mutable copy of this object."""
+ return self.__deepcopy__({})
+
+ def urlencode(self, safe=None):
+ """
+ Returns an encoded string of all query string arguments.
+
+ :arg safe: Used to specify characters which do not require quoting, for
+ example::
+
+ >>> q = QueryDict('', mutable=True)
+ >>> q['next'] = '/a&b/'
+ >>> q.urlencode()
+ 'next=%2Fa%26b%2F'
+ >>> q.urlencode(safe='/')
+ 'next=/a%26b/'
+
+ """
+ output = []
+ if safe:
+ safe = force_bytes(safe, self.encoding)
+ encode = lambda k, v: '%s=%s' % ((quote(k, safe), quote(v, safe)))
+ else:
+ encode = lambda k, v: urlencode({k: v})
+ for k, list_ in self.lists():
+ k = force_bytes(k, self.encoding)
+ output.extend([encode(k, force_bytes(v, self.encoding))
+ for v in list_])
+ return '&'.join(output)
+
+
+def build_request_repr(request, path_override=None, GET_override=None,
+ POST_override=None, COOKIES_override=None,
+ META_override=None):
+ """
+ Builds and returns the request's representation string. The request's
+ attributes may be overridden by pre-processed values.
+ """
+ # Since this is called as part of error handling, we need to be very
+ # robust against potentially malformed input.
+ try:
+ get = (pformat(GET_override)
+ if GET_override is not None
+ else pformat(request.GET))
+ except Exception:
+ get = '<could not parse>'
+ if request._post_parse_error:
+ post = '<could not parse>'
+ else:
+ try:
+ post = (pformat(POST_override)
+ if POST_override is not None
+ else pformat(request.POST))
+ except Exception:
+ post = '<could not parse>'
+ try:
+ cookies = (pformat(COOKIES_override)
+ if COOKIES_override is not None
+ else pformat(request.COOKIES))
+ except Exception:
+ cookies = '<could not parse>'
+ try:
+ meta = (pformat(META_override)
+ if META_override is not None
+ else pformat(request.META))
+ except Exception:
+ meta = '<could not parse>'
+ path = path_override if path_override is not None else request.path
+ return force_str('<%s\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' %
+ (request.__class__.__name__,
+ path,
+ six.text_type(get),
+ six.text_type(post),
+ six.text_type(cookies),
+ six.text_type(meta)))
+
+
+# It's neither necessary nor appropriate to use
+# django.utils.encoding.smart_text for parsing URLs and form inputs. Thus,
+# this slightly more restricted function, used by QueryDict.
+def bytes_to_text(s, encoding):
+ """
+ Converts basestring objects to unicode, using the given encoding. Illegally
+ encoded input characters are replaced with Unicode "unknown" codepoint
+ (\ufffd).
+
+ Returns any non-basestring objects without change.
+ """
+ if isinstance(s, bytes):
+ return six.text_type(s, encoding, 'replace')
+ else:
+ return s
+
+
+def split_domain_port(host):
+ """
+ Return a (domain, port) tuple from a given host.
+
+ Returned domain is lower-cased. If the host is invalid, the domain will be
+ empty.
+ """
+ host = host.lower()
+
+ if not host_validation_re.match(host):
+ return '', ''
+
+ if host[-1] == ']':
+ # It's an IPv6 address without a port.
+ return host, ''
+ bits = host.rsplit(':', 1)
+ if len(bits) == 2:
+ return tuple(bits)
+ return bits[0], ''
+
+
+def validate_host(host, allowed_hosts):
+ """
+ Validate the given host for this site.
+
+ Check that the host looks valid and matches a host or host pattern in the
+ given list of ``allowed_hosts``. Any pattern beginning with a period
+ matches a domain and all its subdomains (e.g. ``.example.com`` matches
+ ``example.com`` and any subdomain), ``*`` matches anything, and anything
+ else must match exactly.
+
+ Note: This function assumes that the given host is lower-cased and has
+ already had the port, if any, stripped off.
+
+ Return ``True`` for a valid host, ``False`` otherwise.
+
+ """
+ for pattern in allowed_hosts:
+ pattern = pattern.lower()
+ match = (
+ pattern == '*' or
+ pattern.startswith('.') and (
+ host.endswith(pattern) or host == pattern[1:]
+ ) or
+ pattern == host
+ )
+ if match:
+ return True
+
+ return False