web/lib/django/core/handlers/modpython.py
changeset 0 0d40e90630ef
equal deleted inserted replaced
-1:000000000000 0:0d40e90630ef
       
     1 import os
       
     2 from pprint import pformat
       
     3 
       
     4 from django import http
       
     5 from django.core import signals
       
     6 from django.core.handlers.base import BaseHandler
       
     7 from django.core.urlresolvers import set_script_prefix
       
     8 from django.utils import datastructures
       
     9 from django.utils.encoding import force_unicode, smart_str, iri_to_uri
       
    10 
       
    11 # NOTE: do *not* import settings (or any module which eventually imports
       
    12 # settings) until after ModPythonHandler has been called; otherwise os.environ
       
    13 # won't be set up correctly (with respect to settings).
       
    14 
       
    15 class ModPythonRequest(http.HttpRequest):
       
    16     def __init__(self, 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.
       
    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
       
    39 
       
    40     def __repr__(self):
       
    41         # Since this is called as part of error handling, we need to be very
       
    42         # robust against potentially malformed input.
       
    43         try:
       
    44             get = pformat(self.GET)
       
    45         except:
       
    46             get = '<could not parse>'
       
    47         if self._post_parse_error:
       
    48             post = '<could not parse>'
       
    49         else:
       
    50             try:
       
    51                 post = pformat(self.POST)
       
    52             except:
       
    53                 post = '<could not parse>'
       
    54         try:
       
    55             cookies = pformat(self.COOKIES)
       
    56         except:
       
    57             cookies = '<could not parse>'
       
    58         try:
       
    59             meta = pformat(self.META)
       
    60         except:
       
    61             meta = '<could not parse>'
       
    62         return smart_str(u'<ModPythonRequest\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' %
       
    63                          (self.path, unicode(get), unicode(post),
       
    64                           unicode(cookies), unicode(meta)))
       
    65 
       
    66     def get_full_path(self):
       
    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 '')
       
    70 
       
    71     def is_secure(self):
       
    72         try:
       
    73             return self._req.is_https()
       
    74         except AttributeError:
       
    75             # mod_python < 3.2.10 doesn't have req.is_https().
       
    76             return self._req.subprocess_env.get('HTTPS', '').lower() in ('on', '1')
       
    77 
       
    78     def _load_post_and_files(self):
       
    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 
       
    84         if 'content-type' in self._req.headers_in and self._req.headers_in['content-type'].startswith('multipart'):
       
    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
       
    95         else:
       
    96             self._post, self._files = http.QueryDict(self.raw_post_data, encoding=self._encoding), datastructures.MultiValueDict()
       
    97 
       
    98     def _get_request(self):
       
    99         if not hasattr(self, '_request'):
       
   100             self._request = datastructures.MergeDict(self.POST, self.GET)
       
   101         return self._request
       
   102 
       
   103     def _get_get(self):
       
   104         if not hasattr(self, '_get'):
       
   105             self._get = http.QueryDict(self._req.args, encoding=self._encoding)
       
   106         return self._get
       
   107 
       
   108     def _set_get(self, get):
       
   109         self._get = get
       
   110 
       
   111     def _get_post(self):
       
   112         if not hasattr(self, '_post'):
       
   113             self._load_post_and_files()
       
   114         return self._post
       
   115 
       
   116     def _set_post(self, post):
       
   117         self._post = post
       
   118 
       
   119     def _get_cookies(self):
       
   120         if not hasattr(self, '_cookies'):
       
   121             self._cookies = http.parse_cookie(self._req.headers_in.get('cookie', ''))
       
   122         return self._cookies
       
   123 
       
   124     def _set_cookies(self, cookies):
       
   125         self._cookies = cookies
       
   126 
       
   127     def _get_files(self):
       
   128         if not hasattr(self, '_files'):
       
   129             self._load_post_and_files()
       
   130         return self._files
       
   131 
       
   132     def _get_meta(self):
       
   133         "Lazy loader that returns self.META dictionary"
       
   134         if not hasattr(self, '_meta'):
       
   135             self._meta = {
       
   136                 'AUTH_TYPE':         self._req.ap_auth_type,
       
   137                 'CONTENT_LENGTH':    self._req.headers_in.get('content-length', 0),
       
   138                 'CONTENT_TYPE':      self._req.headers_in.get('content-type'),
       
   139                 'GATEWAY_INTERFACE': 'CGI/1.1',
       
   140                 'PATH_INFO':         self.path_info,
       
   141                 'PATH_TRANSLATED':   None, # Not supported
       
   142                 'QUERY_STRING':      self._req.args,
       
   143                 'REMOTE_ADDR':       self._req.connection.remote_ip,
       
   144                 'REMOTE_HOST':       None, # DNS lookups not supported
       
   145                 'REMOTE_IDENT':      self._req.connection.remote_logname,
       
   146                 'REMOTE_USER':       self._req.user,
       
   147                 'REQUEST_METHOD':    self._req.method,
       
   148                 'SCRIPT_NAME':       self.django_root,
       
   149                 'SERVER_NAME':       self._req.server.server_hostname,
       
   150                 'SERVER_PORT':       self._req.connection.local_addr[1],
       
   151                 'SERVER_PROTOCOL':   self._req.protocol,
       
   152                 'SERVER_SOFTWARE':   'mod_python'
       
   153             }
       
   154             for key, value in self._req.headers_in.items():
       
   155                 key = 'HTTP_' + key.upper().replace('-', '_')
       
   156                 self._meta[key] = value
       
   157         return self._meta
       
   158 
       
   159     def _get_raw_post_data(self):
       
   160         try:
       
   161             return self._raw_post_data
       
   162         except AttributeError:
       
   163             self._raw_post_data = self._req.read()
       
   164             return self._raw_post_data
       
   165 
       
   166     def _get_method(self):
       
   167         return self.META['REQUEST_METHOD'].upper()
       
   168 
       
   169     GET = property(_get_get, _set_get)
       
   170     POST = property(_get_post, _set_post)
       
   171     COOKIES = property(_get_cookies, _set_cookies)
       
   172     FILES = property(_get_files)
       
   173     META = property(_get_meta)
       
   174     REQUEST = property(_get_request)
       
   175     raw_post_data = property(_get_raw_post_data)
       
   176     method = property(_get_method)
       
   177 
       
   178 class ModPythonHandler(BaseHandler):
       
   179     request_class = ModPythonRequest
       
   180 
       
   181     def __call__(self, req):
       
   182         # mod_python fakes the environ, and thus doesn't process SetEnv.  This fixes that
       
   183         os.environ.update(req.subprocess_env)
       
   184 
       
   185         # now that the environ works we can see the correct settings, so imports
       
   186         # that use settings now can work
       
   187         from django.conf import settings
       
   188 
       
   189         # if we need to set up middleware, now that settings works we can do it now.
       
   190         if self._request_middleware is None:
       
   191             self.load_middleware()
       
   192 
       
   193         set_script_prefix(req.get_options().get('django.root', ''))
       
   194         signals.request_started.send(sender=self.__class__)
       
   195         try:
       
   196             try:
       
   197                 request = self.request_class(req)
       
   198             except UnicodeDecodeError:
       
   199                 response = http.HttpResponseBadRequest()
       
   200             else:
       
   201                 response = self.get_response(request)
       
   202 
       
   203                 # Apply response middleware
       
   204                 for middleware_method in self._response_middleware:
       
   205                     response = middleware_method(request, response)
       
   206                 response = self.apply_response_fixes(request, response)
       
   207         finally:
       
   208             signals.request_finished.send(sender=self.__class__)
       
   209 
       
   210         # Convert our custom HttpResponse object back into the mod_python req.
       
   211         req.content_type = response['Content-Type']
       
   212         for key, value in response.items():
       
   213             if key != 'content-type':
       
   214                 req.headers_out[str(key)] = str(value)
       
   215         for c in response.cookies.values():
       
   216             req.headers_out.add('Set-Cookie', c.output(header=''))
       
   217         req.status = response.status_code
       
   218         try:
       
   219             for chunk in response:
       
   220                 req.write(chunk)
       
   221         finally:
       
   222             response.close()
       
   223 
       
   224         return 0 # mod_python.apache.OK
       
   225 
       
   226 def handler(req):
       
   227     # mod_python hooks into this function.
       
   228     return ModPythonHandler()(req)