Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: django/django
...
head fork: loic/django
compare: ticket20619
Checking mergeability… Don’t worry, you can still create the pull request.
  • 1 commit
  • 3 files changed
  • 0 commit comments
  • 1 contributor
View
128 django/core/handlers/wsgi.py
@@ -11,7 +11,8 @@
from django.core.handlers import base
from django.core.urlresolvers import set_script_prefix
from django.utils import datastructures
-from django.utils.encoding import force_str, force_text, iri_to_uri
+from django.utils.datastructures import MultiValueDict
+from django.utils.encoding import force_str
# For backwards compatibility -- lots of code uses this in the wild!
from django.http.response import REASON_PHRASES as STATUS_CODE_TEXT
@@ -72,7 +73,24 @@ def readline(self, size=None):
class WSGIRequest(http.HttpRequest):
+ _post_loaded = False
+
+ @property
+ def encoding(self):
+ return self._encoding
+
+ @encoding.setter
+ def encoding(self, val):
+ self._encoding = val
+
+ self._load_get()
+ self._post_loaded = False
+
def __init__(self, environ):
+ self.environ = environ
+
+ super(WSGIRequest, self).__init__()
+
script_name = base.get_script_name(environ)
path_info = base.get_path_info(environ)
if not path_info:
@@ -81,14 +99,16 @@ def __init__(self, environ):
# operate as if they'd requested '/'. Not amazingly nice to force
# the path like this, but should be harmless.
path_info = '/'
- self.environ = environ
self.path_info = path_info
self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/'))
+
self.META = environ
- self.META['PATH_INFO'] = path_info
self.META['SCRIPT_NAME'] = script_name
+ self.META['PATH_INFO'] = path_info
+
self.method = environ['REQUEST_METHOD'].upper()
- _, content_params = self._parse_content_type(self.META.get('CONTENT_TYPE', ''))
+
+ content_params = self._parse_content_type(self.META.get('CONTENT_TYPE', ''))[1]
if 'charset' in content_params:
try:
codecs.lookup(content_params['charset'])
@@ -96,14 +116,69 @@ def __init__(self, environ):
pass
else:
self.encoding = content_params['charset']
- self._post_parse_error = False
+
try:
content_length = int(self.environ.get('CONTENT_LENGTH'))
except (ValueError, TypeError):
content_length = 0
self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
self._read_started = False
- self.resolver_match = None
+
+ # `self.GET` and `self.COOKIES` get their data from the environ, so we can
+ # safely load them here. `self.POST`, `self.FILES` and `self.REQUEST` need
+ # to consume the stream so we lazy load them in `__getattribute__()`.
+ self._load_get()
+ self._load_cookies()
+
+ def __getattribute__(self, name):
+ _post_loaded = super(WSGIRequest, self).__getattribute__('_post_loaded')
+ if _post_loaded is False and name in set(['POST', 'FILES', 'REQUEST']):
+ self._post_loaded = True
+ self._load_post_and_files()
+ self._load_request()
+ return super(WSGIRequest, self).__getattribute__(name)
+
+ def _load_get(self):
+ # The WSGI spec says 'QUERY_STRING' may be absent.
+ self.GET = http.QueryDict(self.environ.get('QUERY_STRING', ''), encoding=self.encoding)
+
+ 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 = http.QueryDict('', encoding=self.encoding), MultiValueDict()
+ return
+ if self._read_started and not hasattr(self, '_body'):
+ self._post_parse_error = True
+ self.POST, self.FILES = http.QueryDict(''), MultiValueDict()
+ 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.
+ # Mark that an error occured. This allows self.__repr__ to
+ # be explicit about it instead of simply representing an
+ # empty POST
+ self._post_parse_error = True
+ self.POST, self.FILES = http.QueryDict(''), MultiValueDict()
+ raise
+ elif self.META.get('CONTENT_TYPE', '').startswith('application/x-www-form-urlencoded'):
+ self.POST, self.FILES = http.QueryDict(self.body, encoding=self.encoding), MultiValueDict()
+ else:
+ self.POST, self.FILES = http.QueryDict('', encoding=self.encoding), MultiValueDict()
+
+ def _load_request(self):
+ self.REQUEST = datastructures.MergeDict(self.POST, self.GET)
+
+ def _load_cookies(self):
+ self.COOKIES = http.parse_cookie(self.environ.get('HTTP_COOKIE', ''))
def _is_secure(self):
return 'wsgi.url_scheme' in self.environ and self.environ['wsgi.url_scheme'] == 'https'
@@ -123,47 +198,6 @@ def _parse_content_type(self, ctype):
content_params[k] = v
return content_type, content_params
- def _get_request(self):
- if not hasattr(self, '_request'):
- self._request = datastructures.MergeDict(self.POST, self.GET)
- return self._request
-
- def _get_get(self):
- if not hasattr(self, '_get'):
- # The WSGI spec says 'QUERY_STRING' may be absent.
- self._get = http.QueryDict(self.environ.get('QUERY_STRING', ''), encoding=self._encoding)
- return self._get
-
- def _set_get(self, get):
- self._get = get
-
- def _get_post(self):
- if not hasattr(self, '_post'):
- self._load_post_and_files()
- return self._post
-
- def _set_post(self, post):
- self._post = post
-
- def _get_cookies(self):
- if not hasattr(self, '_cookies'):
- self._cookies = http.parse_cookie(self.environ.get('HTTP_COOKIE', ''))
- return self._cookies
-
- def _set_cookies(self, cookies):
- self._cookies = cookies
-
- def _get_files(self):
- if not hasattr(self, '_files'):
- self._load_post_and_files()
- return self._files
-
- GET = property(_get_get, _set_get)
- POST = property(_get_post, _set_post)
- COOKIES = property(_get_cookies, _set_cookies)
- FILES = property(_get_files)
- REQUEST = property(_get_request)
-
class WSGIHandler(base.BaseHandler):
initLock = Lock()
View
86 django/http/request.py
@@ -34,20 +34,19 @@ class UnreadablePostError(IOError):
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.META = {}
+
+ self.GET, self.POST, self.FILES, self.REQUEST, self.COOKIES = {}, {}, {}, {}, {}
- self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {}
self.path = ''
self.path_info = ''
self.method = None
self.resolver_match = None
+ self.encoding = None
+ self.upload_handlers = [uploadhandler.load_handler(handler, self)
+ for handler in settings.FILE_UPLOAD_HANDLERS]
+
self._post_parse_error = False
def __repr__(self):
@@ -140,40 +139,6 @@ def is_secure(self):
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(
@@ -195,43 +160,6 @@ def body(self):
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
View
1  tests/requests/tests.py
@@ -94,6 +94,7 @@ def test_wsgirequest_repr(self):
request.POST = {'post-key': 'post-value'}
request.COOKIES = {'post-key': 'post-value'}
request.META = {'post-key': 'post-value'}
+ request._post_loaded = True
self.assertEqual(repr(request), str_prefix("<WSGIRequest\npath:/somepath/,\nGET:{%(_)s'get-key': %(_)s'get-value'},\nPOST:{%(_)s'post-key': %(_)s'post-value'},\nCOOKIES:{%(_)s'post-key': %(_)s'post-value'},\nMETA:{%(_)s'post-key': %(_)s'post-value'}>"))
self.assertEqual(build_request_repr(request), repr(request))
self.assertEqual(build_request_repr(request, path_override='/otherpath/', GET_override={'a': 'b'}, POST_override={'c': 'd'}, COOKIES_override={'e': 'f'}, META_override={'g': 'h'}),

No commit comments for this range

Something went wrong with that request. Please try again.