app/django/core/handlers/wsgi.py
changeset 323 ff1a9aa48cfd
parent 54 03e267d67478
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
     5 except ImportError:
     5 except ImportError:
     6     from StringIO import StringIO
     6     from StringIO import StringIO
     7 
     7 
     8 from django import http
     8 from django import http
     9 from django.core import signals
     9 from django.core import signals
    10 from django.core.handlers.base import BaseHandler
    10 from django.core.handlers import base
    11 from django.dispatch import dispatcher
    11 from django.core.urlresolvers import set_script_prefix
    12 from django.utils import datastructures
    12 from django.utils import datastructures
    13 from django.utils.encoding import force_unicode
    13 from django.utils.encoding import force_unicode
    14 
    14 
    15 # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
    15 # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
    16 STATUS_CODE_TEXT = {
    16 STATUS_CODE_TEXT = {
    72         fdst.write(buf)
    72         fdst.write(buf)
    73         size -= len(buf)
    73         size -= len(buf)
    74 
    74 
    75 class WSGIRequest(http.HttpRequest):
    75 class WSGIRequest(http.HttpRequest):
    76     def __init__(self, environ):
    76     def __init__(self, environ):
       
    77         script_name = base.get_script_name(environ)
       
    78         path_info = force_unicode(environ.get('PATH_INFO', u'/'))
       
    79         if not path_info or path_info == script_name:
       
    80             # Sometimes PATH_INFO exists, but is empty (e.g. accessing
       
    81             # the SCRIPT_NAME URL without a trailing slash). We really need to
       
    82             # operate as if they'd requested '/'. Not amazingly nice to force
       
    83             # the path like this, but should be harmless.
       
    84             #
       
    85             # (The comparison of path_info to script_name is to work around an
       
    86             # apparent bug in flup 1.0.1. Se Django ticket #8490).
       
    87             path_info = u'/'
    77         self.environ = environ
    88         self.environ = environ
    78         self.path = force_unicode(environ['PATH_INFO'])
    89         self.path_info = path_info
       
    90         self.path = '%s%s' % (script_name, path_info)
    79         self.META = environ
    91         self.META = environ
       
    92         self.META['PATH_INFO'] = path_info
       
    93         self.META['SCRIPT_NAME'] = script_name
    80         self.method = environ['REQUEST_METHOD'].upper()
    94         self.method = environ['REQUEST_METHOD'].upper()
       
    95         self._post_parse_error = False
    81 
    96 
    82     def __repr__(self):
    97     def __repr__(self):
    83         # Since this is called as part of error handling, we need to be very
    98         # Since this is called as part of error handling, we need to be very
    84         # robust against potentially malformed input.
    99         # robust against potentially malformed input.
    85         try:
   100         try:
    86             get = pformat(self.GET)
   101             get = pformat(self.GET)
    87         except:
   102         except:
    88             get = '<could not parse>'
   103             get = '<could not parse>'
    89         try:
   104         if self._post_parse_error:
    90             post = pformat(self.POST)
       
    91         except:
       
    92             post = '<could not parse>'
   105             post = '<could not parse>'
       
   106         else:
       
   107             try:
       
   108                 post = pformat(self.POST)
       
   109             except:
       
   110                 post = '<could not parse>'
    93         try:
   111         try:
    94             cookies = pformat(self.COOKIES)
   112             cookies = pformat(self.COOKIES)
    95         except:
   113         except:
    96             cookies = '<could not parse>'
   114             cookies = '<could not parse>'
    97         try:
   115         try:
   110 
   128 
   111     def _load_post_and_files(self):
   129     def _load_post_and_files(self):
   112         # Populates self._post and self._files
   130         # Populates self._post and self._files
   113         if self.method == 'POST':
   131         if self.method == 'POST':
   114             if self.environ.get('CONTENT_TYPE', '').startswith('multipart'):
   132             if self.environ.get('CONTENT_TYPE', '').startswith('multipart'):
   115                 header_dict = dict([(k, v) for k, v in self.environ.items() if k.startswith('HTTP_')])
   133                 self._raw_post_data = ''
   116                 header_dict['Content-Type'] = self.environ.get('CONTENT_TYPE', '')
   134                 try:
   117                 self._post, self._files = http.parse_file_upload(header_dict, self.raw_post_data)
   135                     self._post, self._files = self.parse_file_upload(self.META, self.environ['wsgi.input'])
       
   136                 except:
       
   137                     # An error occured while parsing POST data.  Since when
       
   138                     # formatting the error the request handler might access
       
   139                     # self.POST, set self._post and self._file to prevent
       
   140                     # attempts to parse POST data again.
       
   141                     self._post = http.QueryDict('')
       
   142                     self._files = datastructures.MultiValueDict()
       
   143                     # Mark that an error occured.  This allows self.__repr__ to
       
   144                     # be explicit about it instead of simply representing an
       
   145                     # empty POST
       
   146                     self._post_parse_error = True
       
   147                     raise
   118             else:
   148             else:
   119                 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict()
   149                 self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict()
   120         else:
   150         else:
   121             self._post, self._files = http.QueryDict('', encoding=self._encoding), datastructures.MultiValueDict()
   151             self._post, self._files = http.QueryDict('', encoding=self._encoding), datastructures.MultiValueDict()
   122 
   152 
   161         except AttributeError:
   191         except AttributeError:
   162             buf = StringIO()
   192             buf = StringIO()
   163             try:
   193             try:
   164                 # CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd)
   194                 # CONTENT_LENGTH might be absent if POST doesn't have content at all (lighttpd)
   165                 content_length = int(self.environ.get('CONTENT_LENGTH', 0))
   195                 content_length = int(self.environ.get('CONTENT_LENGTH', 0))
   166             except ValueError: # if CONTENT_LENGTH was empty string or not an integer
   196             except (ValueError, TypeError):
       
   197                 # If CONTENT_LENGTH was empty string or not an integer, don't
       
   198                 # error out. We've also seen None passed in here (against all
       
   199                 # specs, but see ticket #8259), so we handle TypeError as well.
   167                 content_length = 0
   200                 content_length = 0
   168             if content_length > 0:
   201             if content_length > 0:
   169                 safe_copyfileobj(self.environ['wsgi.input'], buf,
   202                 safe_copyfileobj(self.environ['wsgi.input'], buf,
   170                         size=content_length)
   203                         size=content_length)
   171             self._raw_post_data = buf.getvalue()
   204             self._raw_post_data = buf.getvalue()
   177     COOKIES = property(_get_cookies, _set_cookies)
   210     COOKIES = property(_get_cookies, _set_cookies)
   178     FILES = property(_get_files)
   211     FILES = property(_get_files)
   179     REQUEST = property(_get_request)
   212     REQUEST = property(_get_request)
   180     raw_post_data = property(_get_raw_post_data)
   213     raw_post_data = property(_get_raw_post_data)
   181 
   214 
   182 class WSGIHandler(BaseHandler):
   215 class WSGIHandler(base.BaseHandler):
   183     initLock = Lock()
   216     initLock = Lock()
   184     request_class = WSGIRequest
   217     request_class = WSGIRequest
   185 
   218 
   186     def __call__(self, environ, start_response):
   219     def __call__(self, environ, start_response):
   187         from django.conf import settings
   220         from django.conf import settings
   193             # Check that middleware is still uninitialised.
   226             # Check that middleware is still uninitialised.
   194             if self._request_middleware is None:
   227             if self._request_middleware is None:
   195                 self.load_middleware()
   228                 self.load_middleware()
   196             self.initLock.release()
   229             self.initLock.release()
   197 
   230 
   198         dispatcher.send(signal=signals.request_started)
   231         set_script_prefix(base.get_script_name(environ))
       
   232         signals.request_started.send(sender=self.__class__)
   199         try:
   233         try:
   200             try:
   234             try:
   201                 request = self.request_class(environ)
   235                 request = self.request_class(environ)
   202             except UnicodeDecodeError:
   236             except UnicodeDecodeError:
   203                 response = http.HttpResponseBadRequest()
   237                 response = http.HttpResponseBadRequest()
   207                 # Apply response middleware
   241                 # Apply response middleware
   208                 for middleware_method in self._response_middleware:
   242                 for middleware_method in self._response_middleware:
   209                     response = middleware_method(request, response)
   243                     response = middleware_method(request, response)
   210                 response = self.apply_response_fixes(request, response)
   244                 response = self.apply_response_fixes(request, response)
   211         finally:
   245         finally:
   212             dispatcher.send(signal=signals.request_finished)
   246             signals.request_finished.send(sender=self.__class__)
   213 
   247 
   214         try:
   248         try:
   215             status_text = STATUS_CODE_TEXT[response.status_code]
   249             status_text = STATUS_CODE_TEXT[response.status_code]
   216         except KeyError:
   250         except KeyError:
   217             status_text = 'UNKNOWN STATUS CODE'
   251             status_text = 'UNKNOWN STATUS CODE'