PageRenderTime 124ms CodeModel.GetById 81ms app.highlight 14ms RepoModel.GetById 26ms app.codeStats 0ms

/django/middleware/common.py

https://code.google.com/p/mango-py/
Python | 158 lines | 153 code | 4 blank | 1 comment | 5 complexity | 5eb435df3e5349e4d7c51eaf04ac6821 MD5 | raw file
  1import re
  2
  3from django.conf import settings
  4from django import http
  5from django.core.mail import mail_managers
  6from django.utils.http import urlquote
  7from django.core import urlresolvers
  8from django.utils.hashcompat import md5_constructor
  9from django.utils.log import getLogger
 10
 11logger = getLogger('django.request')
 12
 13
 14class CommonMiddleware(object):
 15    """
 16    "Common" middleware for taking care of some basic operations:
 17
 18        - Forbids access to User-Agents in settings.DISALLOWED_USER_AGENTS
 19
 20        - URL rewriting: Based on the APPEND_SLASH and PREPEND_WWW settings,
 21          this middleware appends missing slashes and/or prepends missing
 22          "www."s.
 23
 24            - If APPEND_SLASH is set and the initial URL doesn't end with a
 25              slash, and it is not found in urlpatterns, a new URL is formed by
 26              appending a slash at the end. If this new URL is found in
 27              urlpatterns, then an HTTP-redirect is returned to this new URL;
 28              otherwise the initial URL is processed as usual.
 29
 30        - ETags: If the USE_ETAGS setting is set, ETags will be calculated from
 31          the entire page content and Not Modified responses will be returned
 32          appropriately.
 33    """
 34
 35    def process_request(self, request):
 36        """
 37        Check for denied User-Agents and rewrite the URL based on
 38        settings.APPEND_SLASH and settings.PREPEND_WWW
 39        """
 40
 41        # Check for denied User-Agents
 42        if 'HTTP_USER_AGENT' in request.META:
 43            for user_agent_regex in settings.DISALLOWED_USER_AGENTS:
 44                if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
 45                    logger.warning('Forbidden (User agent): %s' % request.path,
 46                        extra={
 47                            'status_code': 403,
 48                            'request': request
 49                        }
 50                    )
 51                    return http.HttpResponseForbidden('<h1>Forbidden</h1>')
 52
 53        # Check for a redirect based on settings.APPEND_SLASH
 54        # and settings.PREPEND_WWW
 55        host = request.get_host()
 56        old_url = [host, request.path]
 57        new_url = old_url[:]
 58
 59        if (settings.PREPEND_WWW and old_url[0] and
 60                not old_url[0].startswith('www.')):
 61            new_url[0] = 'www.' + old_url[0]
 62
 63        # Append a slash if APPEND_SLASH is set and the URL doesn't have a
 64        # trailing slash and there is no pattern for the current path
 65        if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
 66            urlconf = getattr(request, 'urlconf', None)
 67            if (not _is_valid_path(request.path_info, urlconf) and
 68                    _is_valid_path("%s/" % request.path_info, urlconf)):
 69                new_url[1] = new_url[1] + '/'
 70                if settings.DEBUG and request.method == 'POST':
 71                    raise RuntimeError, (""
 72                    "You called this URL via POST, but the URL doesn't end "
 73                    "in a slash and you have APPEND_SLASH set. Django can't "
 74                    "redirect to the slash URL while maintaining POST data. "
 75                    "Change your form to point to %s%s (note the trailing "
 76                    "slash), or set APPEND_SLASH=False in your Django "
 77                    "settings.") % (new_url[0], new_url[1])
 78
 79        if new_url == old_url:
 80            # No redirects required.
 81            return
 82        if new_url[0]:
 83            newurl = "%s://%s%s" % (
 84                request.is_secure() and 'https' or 'http',
 85                new_url[0], urlquote(new_url[1]))
 86        else:
 87            newurl = urlquote(new_url[1])
 88        if request.GET:
 89            newurl += '?' + request.META['QUERY_STRING']
 90        return http.HttpResponsePermanentRedirect(newurl)
 91
 92    def process_response(self, request, response):
 93        "Send broken link emails and calculate the Etag, if needed."
 94        if response.status_code == 404:
 95            if settings.SEND_BROKEN_LINK_EMAILS and not settings.DEBUG:
 96                # If the referrer was from an internal link or a non-search-engine site,
 97                # send a note to the managers.
 98                domain = request.get_host()
 99                referer = request.META.get('HTTP_REFERER', None)
100                is_internal = _is_internal_request(domain, referer)
101                path = request.get_full_path()
102                if referer and not _is_ignorable_404(path) and (is_internal or '?' not in referer):
103                    ua = request.META.get('HTTP_USER_AGENT', '<none>')
104                    ip = request.META.get('REMOTE_ADDR', '<none>')
105                    mail_managers("Broken %slink on %s" % ((is_internal and 'INTERNAL ' or ''), domain),
106                        "Referrer: %s\nRequested URL: %s\nUser agent: %s\nIP address: %s\n" \
107                                  % (referer, request.get_full_path(), ua, ip),
108                                  fail_silently=True)
109                return response
110
111        # Use ETags, if requested.
112        if settings.USE_ETAGS:
113            if response.has_header('ETag'):
114                etag = response['ETag']
115            else:
116                etag = '"%s"' % md5_constructor(response.content).hexdigest()
117            if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag:
118                cookies = response.cookies
119                response = http.HttpResponseNotModified()
120                response.cookies = cookies
121            else:
122                response['ETag'] = etag
123
124        return response
125
126def _is_ignorable_404(uri):
127    """
128    Returns True if a 404 at the given URL *shouldn't* notify the site managers.
129    """
130    for start in settings.IGNORABLE_404_STARTS:
131        if uri.startswith(start):
132            return True
133    for end in settings.IGNORABLE_404_ENDS:
134        if uri.endswith(end):
135            return True
136    return False
137
138def _is_internal_request(domain, referer):
139    """
140    Returns true if the referring URL is the same domain as the current request.
141    """
142    # Different subdomains are treated as different domains.
143    return referer is not None and re.match("^https?://%s/" % re.escape(domain), referer)
144
145def _is_valid_path(path, urlconf=None):
146    """
147    Returns True if the given path resolves against the default URL resolver,
148    False otherwise.
149
150    This is a convenience method to make working with "is this a match?" cases
151    easier, avoiding unnecessarily indented try...except blocks.
152    """
153    try:
154        urlresolvers.resolve(path, urlconf)
155        return True
156    except urlresolvers.Resolver404:
157        return False
158