/django/branches/soc2009/http-wsgi-improvements/django/core/handlers/base.py
Python | 230 lines | 160 code | 26 blank | 44 comment | 49 complexity | 138d75a069efab650d8e3e045739cfe7 MD5 | raw file
Possible License(s): BSD-3-Clause
- import sys
- from django import http
- from django.core import signals
- from django.utils.encoding import force_unicode
- from django.utils.importlib import import_module
- class BaseHandler(object):
- # Changes that are always applied to a response (in this order).
- response_fixes = [
- http.fix_location_header,
- http.conditional_content_removal,
- http.fix_IE_for_attach,
- http.fix_IE_for_vary,
- ]
- def __init__(self):
- self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
- def load_middleware(self):
- """
- Populate middleware lists from settings.MIDDLEWARE_CLASSES.
- Must be called after the environment is fixed (see __call__).
- """
- from django.conf import settings
- from django.core import exceptions
- self._view_middleware = []
- self._response_middleware = []
- self._exception_middleware = []
- request_middleware = []
- for middleware_path in settings.MIDDLEWARE_CLASSES:
- try:
- dot = middleware_path.rindex('.')
- except ValueError:
- raise exceptions.ImproperlyConfigured, '%s isn\'t a middleware module' % middleware_path
- mw_module, mw_classname = middleware_path[:dot], middleware_path[dot+1:]
- try:
- mod = import_module(mw_module)
- except ImportError, e:
- raise exceptions.ImproperlyConfigured, 'Error importing middleware %s: "%s"' % (mw_module, e)
- try:
- mw_class = getattr(mod, mw_classname)
- except AttributeError:
- raise exceptions.ImproperlyConfigured, 'Middleware module "%s" does not define a "%s" class' % (mw_module, mw_classname)
- try:
- mw_instance = mw_class()
- except exceptions.MiddlewareNotUsed:
- continue
- if hasattr(mw_instance, 'process_request'):
- request_middleware.append(mw_instance.process_request)
- if hasattr(mw_instance, 'process_view'):
- self._view_middleware.append(mw_instance.process_view)
- if hasattr(mw_instance, 'process_response'):
- self._response_middleware.insert(0, mw_instance.process_response)
- if hasattr(mw_instance, 'process_exception'):
- self._exception_middleware.insert(0, mw_instance.process_exception)
- # We only assign to this when initialization is complete as it is used
- # as a flag for initialization being complete.
- self._request_middleware = request_middleware
- def process_request(self, request_env):
- signals.request_started.send(sender=self.__class__)
- try:
- try:
- request = self.request_class(request_env)
- except UnicodeDecodeError:
- response = http.HttpResponseBadRequest()
- else:
- response = self.get_response(request)
- # Apply response middleware
- streaming = getattr(response, "content_generator", False)
- streaming_safe = lambda x: getattr(x.im_self, "streaming_safe", False)
- if not isinstance(response, http.HttpResponseSendFile):
- for middleware_method in self._response_middleware:
- if not streaming or streaming_safe(middleware_method):
- print middleware_method
- response = middleware_method(request, response)
- response = self.apply_response_fixes(request, response)
- finally:
- signals.request_finished.send(sender=self.__class__)
- return response
- def get_response(self, request):
- "Returns an HttpResponse object for the given HttpRequest"
- from django.core import exceptions, urlresolvers
- from django.conf import settings
- # Apply request middleware
- for middleware_method in self._request_middleware:
- response = middleware_method(request)
- if response:
- return response
- # Get urlconf from request object, if available. Otherwise use default.
- urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
- resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
- try:
- callback, callback_args, callback_kwargs = resolver.resolve(
- request.path_info)
- # Apply view middleware
- for middleware_method in self._view_middleware:
- response = middleware_method(request, callback, callback_args, callback_kwargs)
- if response:
- return response
- try:
- response = callback(request, *callback_args, **callback_kwargs)
- except Exception, e:
- # If the view raised an exception, run it through exception
- # middleware, and if the exception middleware returns a
- # response, use that. Otherwise, reraise the exception.
- for middleware_method in self._exception_middleware:
- response = middleware_method(request, e)
- if response:
- return response
- raise
- # Complain if the view returned None (a common error).
- if response is None:
- try:
- view_name = callback.func_name # If it's a function
- except AttributeError:
- view_name = callback.__class__.__name__ + '.__call__' # If it's a class
- raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, view_name)
- return response
- except http.Http404, e:
- if settings.DEBUG:
- from django.views import debug
- return debug.technical_404_response(request, e)
- else:
- try:
- callback, param_dict = resolver.resolve404()
- return callback(request, **param_dict)
- except:
- try:
- return self.handle_uncaught_exception(request, resolver, sys.exc_info())
- finally:
- receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
- except exceptions.PermissionDenied:
- return http.HttpResponseForbidden('<h1>Permission denied</h1>')
- except SystemExit:
- # Allow sys.exit() to actually exit. See tickets #1023 and #4701
- raise
- except: # Handle everything else, including SuspiciousOperation, etc.
- # Get the exception info now, in case another exception is thrown later.
- exc_info = sys.exc_info()
- receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
- return self.handle_uncaught_exception(request, resolver, exc_info)
- def handle_uncaught_exception(self, request, resolver, exc_info):
- """
- Processing for any otherwise uncaught exceptions (those that will
- generate HTTP 500 responses). Can be overridden by subclasses who want
- customised 500 handling.
- Be *very* careful when overriding this because the error could be
- caused by anything, so assuming something like the database is always
- available would be an error.
- """
- from django.conf import settings
- from django.core.mail import mail_admins
- if settings.DEBUG_PROPAGATE_EXCEPTIONS:
- raise
- if settings.DEBUG:
- from django.views import debug
- return debug.technical_500_response(request, *exc_info)
- # When DEBUG is False, send an error message to the admins.
- subject = 'Error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), request.path)
- try:
- request_repr = repr(request)
- except:
- request_repr = "Request repr() unavailable"
- message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr)
- mail_admins(subject, message, fail_silently=True)
- # Return an HttpResponse that displays a friendly error message.
- callback, param_dict = resolver.resolve500()
- return callback(request, **param_dict)
- def _get_traceback(self, exc_info=None):
- "Helper function to return the traceback as a string"
- import traceback
- return '\n'.join(traceback.format_exception(*(exc_info or sys.exc_info())))
- def apply_response_fixes(self, request, response):
- """
- Applies each of the functions in self.response_fixes to the request and
- response, modifying the response in the process. Returns the new
- response.
- """
- for func in self.response_fixes:
- response = func(request, response)
- return response
- def get_script_name(environ):
- """
- Returns the equivalent of the HTTP request's SCRIPT_NAME environment
- variable. If Apache mod_rewrite has been used, returns what would have been
- the script name prior to any rewriting (so it's the script name as seen
- from the client's perspective), unless DJANGO_USE_POST_REWRITE is set (to
- anything).
- """
- from django.conf import settings
- if settings.FORCE_SCRIPT_NAME is not None:
- return force_unicode(settings.FORCE_SCRIPT_NAME)
- # If Apache's mod_rewrite had a whack at the URL, Apache set either
- # SCRIPT_URL or REDIRECT_URL to the full resource URL before applying any
- # rewrites. Unfortunately not every webserver (lighttpd!) passes this
- # information through all the time, so FORCE_SCRIPT_NAME, above, is still
- # needed.
- script_url = environ.get('SCRIPT_URL', u'')
- if not script_url:
- script_url = environ.get('REDIRECT_URL', u'')
- if script_url:
- return force_unicode(script_url[:-len(environ.get('PATH_INFO', ''))])
- return force_unicode(environ.get('SCRIPT_NAME', u''))