Source

circuits / circuits / web / errors.py

Full commit
James Mills 43bbdba 








Alessio Deiana 291e032 



James Mills 43bbdba 


James Mills a3e9497 



James Mills 43bbdba 


James Mills de133eb 
James Mills 43bbdba 
James Mills de133eb 




James Mills 43bbdba 



James Mills de133eb 

James Mills 43bbdba 
James Mills de133eb 




James Mills 9e79e4d 
James Mills 00fb139 

James Mills 9e79e4d 
James Mills 00fb139 
James Mills 43bbdba 
James Mills de133eb 

James Mills 43bbdba 
James Mills 016299b 
James Mills de133eb 


James Mills 00fb139 
James Mills de133eb 
James Mills 016299b 


James Mills 36e16b1 




James Mills 016299b 
James Mills 36e16b1 
James Mills 016299b 
James Mills 43bbdba 

James Mills de133eb 

James Mills 43bbdba 


James Mills de133eb 
James Mills 43bbdba 


James Mills de133eb 
James Mills 43bbdba 


James Mills de133eb 
James Mills 43bbdba 


James Mills de133eb 
James Mills 8707590 
James Mills 43bbdba 















James Mills de133eb 
James Mills 43bbdba 
James Mills de133eb 
James Mills 43bbdba 
James Mills de133eb 
James Mills 43bbdba 
James Mills de133eb 

James Mills 43bbdba 
James Mills de133eb 
James Mills 43bbdba 
James Mills de133eb 
James Mills 43bbdba 








James Mills de133eb 








James Mills 43bbdba 



James Mills de133eb 
James Mills 43bbdba 
















James Mills de133eb 
James Mills 43bbdba 






James Mills de133eb 
James Mills 43bbdba 

James Mills de133eb 
# Module:   errors
# Date:     11th February 2009
# Author:   James Mills, prologic at shortcircuit dot net dot au

"""Errors

This module implements a set of standard HTTP Errors.
"""

try:
    from urllib.parse import urljoin as _urljoin
except ImportError:
    from urlparse import urljoin as _urljoin

from circuits import Event

from . import utils
from .utils import escape
from .constants import SERVER_URL, SERVER_VERSION
from .constants import DEFAULT_ERROR_MESSAGE, HTTP_STATUS_CODES

class HTTPError(Event):

    channel = "httperror"

    code = 500
    description = ""

    def __init__(self, request, response, code=None, **kwargs):
        super(HTTPError, self).__init__(request, response, code, **kwargs)

        self.request = request
        self.response = response

        if code is not None:
            self.code = code

        self.error = kwargs.get("error", None)

        self.description = kwargs.get("description",
                getattr(self.__class__, "description", ""))

        if self.error is not None:
            self.traceback = "ERROR: (%s) %s\n%s" % (
                    self.error[0], self.error[1], "".join(self.error[2]))
        else:
            self.traceback = ""

        self.response.close = True
        self.response.code = self.code

        self.data = {
            "code": self.code,
            "name": HTTP_STATUS_CODES.get(self.code, "???"),
            "description": self.description,
            "traceback": escape(self.traceback),
            "url": SERVER_URL,
            "version": SERVER_VERSION
        }

    def sanitize(self):
        if self.code < 300 or self.code > 399:
            if "Location" in self.response.headers:
                del self.response.headers["Location"]

    def __str__(self):
        self.sanitize()
        return DEFAULT_ERROR_MESSAGE % self.data

    def __repr__(self):
        return "<%s %d %s>" % (self.__class__.__name__, self.code,
                HTTP_STATUS_CODES.get(self.code, "???"))

class Forbidden(HTTPError):

    code = 403

class Unauthorized(HTTPError):

    code = 401

class NotFound(HTTPError):

    code = 404

class Redirect(HTTPError):

    def __init__(self, request, response, urls, code=None):
        if isinstance(urls, basestring):
            urls = [urls]
        
        abs_urls = []
        for url in urls:
            # Note that urljoin will "do the right thing" whether url is:
            #  1. a complete URL with host (e.g. "http://www.example.com/test")
            #  2. a URL relative to root (e.g. "/dummy")
            #  3. a URL relative to the current path
            # Note that any query string in request is discarded.
            url = _urljoin(utils.url(request), url)
            abs_urls.append(url)
        self.urls = urls = abs_urls
        
        # RFC 2616 indicates a 301 response code fits our goal; however,
        # browser support for 301 is quite messy. Do 302/303 instead. See
        # http://ppewww.ph.gla.ac.uk/~flavell/www/post-redirect.html
        if code is None:
            if request.protocol >= (1, 1):
                code = 303
            else:
                code = 302
        else:
            if code < 300 or code > 399:
                raise ValueError("status code must be between 300 and 399.")

        super(Redirect, self).__init__(request, response, code)
        
        if code in (300, 301, 302, 303, 307):
            response.headers["Content-Type"] = "text/html"
            # "The ... URI SHOULD be given by the Location field
            # in the response."
            response.headers["Location"] = urls[0]
            
            # "Unless the request method was HEAD, the entity of the response
            # SHOULD contain a short hypertext note with a hyperlink to the
            # new URI(s)."
            msg = {300: "This resource can be found at <a href='%s'>%s</a>.",
                   301: ("This resource has permanently moved to "
                       "<a href='%s'>%s</a>."),
                   302: ("This resource resides temporarily at "
                        "<a href='%s'>%s</a>."),
                   303: ("This resource can be found at "
                        "<a href='%s'>%s</a>."),
                   307: ("This resource has moved temporarily to "
                        "<a href='%s'>%s</a>."),
                   }[code]
            response.body = "<br />\n".join([msg % (u, u) for u in urls])
            # Previous code may have set C-L, so we have to reset it
            # (allow finalize to set it).
            response.headers.pop("Content-Length", None)
        elif code == 304:
            # Not Modified.
            # "The response MUST include the following header fields:
            # Date, unless its omission is required by section 14.18.1"
            # The "Date" header should have been set in Response.__init__
            
            # "...the response SHOULD NOT include other entity-headers."
            for key in ("Allow", "Content-Encoding", "Content-Language",
                        "Content-Length", "Content-Location", "Content-MD5",
                        "Content-Range", "Content-Type", "Expires",
                        "Last-Modified"):
                if key in response.headers:
                    del response.headers[key]
            
            # "The 304 response MUST NOT contain a message-body."
            response.body = None
            # Previous code may have set C-L, so we have to reset it.
            response.headers.pop("Content-Length", None)
        elif code == 305:
            # Use Proxy.
            # urls[0] should be the URI of the proxy.
            response.headers["Location"] = urls[0]
            response.body = None
            # Previous code may have set C-L, so we have to reset it.
            response.headers.pop("Content-Length", None)
        else:
            raise ValueError("The %s status code is unknown." % code)

    def __repr__(self):
        return "<%s %d %s %s>" % (self.__class__.__name__, self.code, self.name,
                " ".join(self.urls))