app/django/core/handlers/modpython.py
changeset 323 ff1a9aa48cfd
parent 54 03e267d67478
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
     2 from pprint import pformat
     2 from pprint import pformat
     3 
     3 
     4 from django import http
     4 from django import http
     5 from django.core import signals
     5 from django.core import signals
     6 from django.core.handlers.base import BaseHandler
     6 from django.core.handlers.base import BaseHandler
     7 from django.dispatch import dispatcher
     7 from django.core.urlresolvers import set_script_prefix
     8 from django.utils import datastructures
     8 from django.utils import datastructures
     9 from django.utils.encoding import force_unicode, smart_str
     9 from django.utils.encoding import force_unicode, smart_str, iri_to_uri
    10 
    10 
    11 # NOTE: do *not* import settings (or any module which eventually imports
    11 # NOTE: do *not* import settings (or any module which eventually imports
    12 # settings) until after ModPythonHandler has been called; otherwise os.environ
    12 # settings) until after ModPythonHandler has been called; otherwise os.environ
    13 # won't be set up correctly (with respect to settings).
    13 # won't be set up correctly (with respect to settings).
    14 
    14 
    15 class ModPythonRequest(http.HttpRequest):
    15 class ModPythonRequest(http.HttpRequest):
    16     def __init__(self, req):
    16     def __init__(self, req):
    17         self._req = req
    17         self._req = req
       
    18         # FIXME: This isn't ideal. The request URI may be encoded (it's
       
    19         # non-normalized) slightly differently to the "real" SCRIPT_NAME
       
    20         # and PATH_INFO values. This causes problems when we compute path_info,
       
    21         # below. For now, don't use script names that will be subject to
       
    22         # encoding/decoding.
    18         self.path = force_unicode(req.uri)
    23         self.path = force_unicode(req.uri)
       
    24         root = req.get_options().get('django.root', '')
       
    25         self.django_root = root
       
    26         # req.path_info isn't necessarily computed correctly in all
       
    27         # circumstances (it's out of mod_python's control a bit), so we use
       
    28         # req.uri and some string manipulations to get the right value.
       
    29         if root and req.uri.startswith(root):
       
    30             self.path_info = force_unicode(req.uri[len(root):])
       
    31         else:
       
    32             self.path_info = self.path
       
    33         if not self.path_info:
       
    34             # Django prefers empty paths to be '/', rather than '', to give us
       
    35             # a common start character for URL patterns. So this is a little
       
    36             # naughty, but also pretty harmless.
       
    37             self.path_info = u'/'
       
    38         self._post_parse_error = False
    19 
    39 
    20     def __repr__(self):
    40     def __repr__(self):
    21         # Since this is called as part of error handling, we need to be very
    41         # Since this is called as part of error handling, we need to be very
    22         # robust against potentially malformed input.
    42         # robust against potentially malformed input.
    23         try:
    43         try:
    24             get = pformat(self.GET)
    44             get = pformat(self.GET)
    25         except:
    45         except:
    26             get = '<could not parse>'
    46             get = '<could not parse>'
    27         try:
    47         if self._post_parse_error:
    28             post = pformat(self.POST)
       
    29         except:
       
    30             post = '<could not parse>'
    48             post = '<could not parse>'
       
    49         else:
       
    50             try:
       
    51                 post = pformat(self.POST)
       
    52             except:
       
    53                 post = '<could not parse>'
    31         try:
    54         try:
    32             cookies = pformat(self.COOKIES)
    55             cookies = pformat(self.COOKIES)
    33         except:
    56         except:
    34             cookies = '<could not parse>'
    57             cookies = '<could not parse>'
    35         try:
    58         try:
    39         return smart_str(u'<ModPythonRequest\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' %
    62         return smart_str(u'<ModPythonRequest\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' %
    40                          (self.path, unicode(get), unicode(post),
    63                          (self.path, unicode(get), unicode(post),
    41                           unicode(cookies), unicode(meta)))
    64                           unicode(cookies), unicode(meta)))
    42 
    65 
    43     def get_full_path(self):
    66     def get_full_path(self):
    44         return '%s%s' % (self.path, self._req.args and ('?' + self._req.args) or '')
    67         # RFC 3986 requires self._req.args to be in the ASCII range, but this
       
    68         # doesn't always happen, so rather than crash, we defensively encode it.
       
    69         return '%s%s' % (self.path, self._req.args and ('?' + iri_to_uri(self._req.args)) or '')
    45 
    70 
    46     def is_secure(self):
    71     def is_secure(self):
    47         try:
    72         try:
    48             return self._req.is_https()
    73             return self._req.is_https()
    49         except AttributeError:
    74         except AttributeError:
    50             # mod_python < 3.2.10 doesn't have req.is_https().
    75             # mod_python < 3.2.10 doesn't have req.is_https().
    51             return self._req.subprocess_env.get('HTTPS', '').lower() in ('on', '1')
    76             return self._req.subprocess_env.get('HTTPS', '').lower() in ('on', '1')
    52 
    77 
    53     def _load_post_and_files(self):
    78     def _load_post_and_files(self):
    54         "Populates self._post and self._files"
    79         "Populates self._post and self._files"
       
    80         if self.method != 'POST':
       
    81             self._post, self._files = http.QueryDict('', encoding=self._encoding), datastructures.MultiValueDict()
       
    82             return
       
    83 
    55         if 'content-type' in self._req.headers_in and self._req.headers_in['content-type'].startswith('multipart'):
    84         if 'content-type' in self._req.headers_in and self._req.headers_in['content-type'].startswith('multipart'):
    56             self._post, self._files = http.parse_file_upload(self._req.headers_in, self.raw_post_data)
    85             self._raw_post_data = ''
       
    86             try:
       
    87                 self._post, self._files = self.parse_file_upload(self.META, self._req)
       
    88             except:
       
    89                 # See django.core.handlers.wsgi.WSGIHandler for an explanation
       
    90                 # of what's going on here.
       
    91                 self._post = http.QueryDict('')
       
    92                 self._files = datastructures.MultiValueDict()
       
    93                 self._post_parse_error = True
       
    94                 raise
    57         else:
    95         else:
    58             self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict()
    96             self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict()
    59 
    97 
    60     def _get_request(self):
    98     def _get_request(self):
    61         if not hasattr(self, '_request'):
    99         if not hasattr(self, '_request'):
    97             self._meta = {
   135             self._meta = {
    98                 'AUTH_TYPE':         self._req.ap_auth_type,
   136                 'AUTH_TYPE':         self._req.ap_auth_type,
    99                 'CONTENT_LENGTH':    self._req.clength, # This may be wrong
   137                 'CONTENT_LENGTH':    self._req.clength, # This may be wrong
   100                 'CONTENT_TYPE':      self._req.content_type, # This may be wrong
   138                 'CONTENT_TYPE':      self._req.content_type, # This may be wrong
   101                 'GATEWAY_INTERFACE': 'CGI/1.1',
   139                 'GATEWAY_INTERFACE': 'CGI/1.1',
   102                 'PATH_INFO':         self._req.path_info,
   140                 'PATH_INFO':         self.path_info,
   103                 'PATH_TRANSLATED':   None, # Not supported
   141                 'PATH_TRANSLATED':   None, # Not supported
   104                 'QUERY_STRING':      self._req.args,
   142                 'QUERY_STRING':      self._req.args,
   105                 'REMOTE_ADDR':       self._req.connection.remote_ip,
   143                 'REMOTE_ADDR':       self._req.connection.remote_ip,
   106                 'REMOTE_HOST':       None, # DNS lookups not supported
   144                 'REMOTE_HOST':       None, # DNS lookups not supported
   107                 'REMOTE_IDENT':      self._req.connection.remote_logname,
   145                 'REMOTE_IDENT':      self._req.connection.remote_logname,
   108                 'REMOTE_USER':       self._req.user,
   146                 'REMOTE_USER':       self._req.user,
   109                 'REQUEST_METHOD':    self._req.method,
   147                 'REQUEST_METHOD':    self._req.method,
   110                 'SCRIPT_NAME':       None, # Not supported
   148                 'SCRIPT_NAME':       self.django_root,
   111                 'SERVER_NAME':       self._req.server.server_hostname,
   149                 'SERVER_NAME':       self._req.server.server_hostname,
   112                 'SERVER_PORT':       self._req.server.port,
   150                 'SERVER_PORT':       self._req.server.port,
   113                 'SERVER_PROTOCOL':   self._req.protocol,
   151                 'SERVER_PROTOCOL':   self._req.protocol,
   114                 'SERVER_SOFTWARE':   'mod_python'
   152                 'SERVER_SOFTWARE':   'mod_python'
   115             }
   153             }
   150 
   188 
   151         # if we need to set up middleware, now that settings works we can do it now.
   189         # if we need to set up middleware, now that settings works we can do it now.
   152         if self._request_middleware is None:
   190         if self._request_middleware is None:
   153             self.load_middleware()
   191             self.load_middleware()
   154 
   192 
   155         dispatcher.send(signal=signals.request_started)
   193         set_script_prefix(req.get_options().get('django.root', ''))
       
   194         signals.request_started.send(sender=self.__class__)
   156         try:
   195         try:
   157             try:
   196             try:
   158                 request = self.request_class(req)
   197                 request = self.request_class(req)
   159             except UnicodeDecodeError:
   198             except UnicodeDecodeError:
   160                 response = http.HttpResponseBadRequest()
   199                 response = http.HttpResponseBadRequest()
   164                 # Apply response middleware
   203                 # Apply response middleware
   165                 for middleware_method in self._response_middleware:
   204                 for middleware_method in self._response_middleware:
   166                     response = middleware_method(request, response)
   205                     response = middleware_method(request, response)
   167                 response = self.apply_response_fixes(request, response)
   206                 response = self.apply_response_fixes(request, response)
   168         finally:
   207         finally:
   169             dispatcher.send(signal=signals.request_finished)
   208             signals.request_finished.send(sender=self.__class__)
   170 
   209 
   171         # Convert our custom HttpResponse object back into the mod_python req.
   210         # Convert our custom HttpResponse object back into the mod_python req.
   172         req.content_type = response['Content-Type']
   211         req.content_type = response['Content-Type']
   173         for key, value in response.items():
   212         for key, value in response.items():
   174             if key != 'content-type':
   213             if key != 'content-type':