Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/branches/cherrypy-2.x/cherrypy/_cperror.py

Revision 1496 (checked in by fumanchu, 2 years ago)

2.x Fix for #507 (InternalRedirect?? supports only absolute paths). Paths relative to request.path are now possible by omitting the leading slash from the path.

  • Property svn:eol-style set to native
Line 
1 """Error classes for CherryPy."""
2
3 import urllib
4 import urlparse
5
6
7 class Error(Exception):
8     pass
9
10 class NotReady(Error):
11     """A request was made before the app server has been started."""
12     pass
13
14 class WrongConfigValue(Error):
15     """ Happens when a config value can't be parsed, or is otherwise illegal. """
16     pass
17
18 class RequestHandled(Exception):
19     """Exception raised when no further request handling should occur."""
20     pass
21
22 class InternalRedirect(Exception):
23     """Exception raised when processing should be handled by a different path.
24     
25     If you supply 'params', it will be used to re-populate params.
26     If 'params' is a dict, it will be used directly.
27     If 'params' is a string, it will be converted to a dict using cgi.parse_qs.
28     
29     If you omit 'params', the params from the original request will
30     remain in effect, including any POST parameters.
31     """
32    
33     def __init__(self, path, params=None):
34         import cherrypy
35         import cgi
36         request = cherrypy.request
37        
38         # Note that urljoin will "do the right thing" whether url is:
39         #  1. a URL relative to root (e.g. "/dummy")
40         #  2. a URL relative to the current path
41         # Note that any querystring will be discarded.
42         path = urlparse.urljoin(cherrypy.request.path, path)
43        
44         # Set a 'path' member attribute so that code which traps this
45         # error can have access to it.
46         self.path = path
47        
48         if params is not None:
49             if isinstance(params, basestring):
50                 request.query_string = params
51                 request.queryString = request.query_string # Backward compatibility
52                 pm = cgi.parse_qs(params, keep_blank_values=True)
53                 for key, val in pm.items():
54                     if len(val) == 1:
55                         pm[key] = val[0]
56                 request.params = pm
57             else:
58                 request.query_string = urllib.urlencode(params)
59                 request.queryString = request.query_string
60                 request.params = params.copy()
61        
62         Exception.__init__(self, path, params)
63
64
65
66 class HTTPRedirect(Exception):
67     """Exception raised when the request should be redirected.
68     
69     The new URL must be passed as the first argument to the Exception, e.g.,
70         cperror.HTTPRedirect(newUrl). Multiple URLs are allowed. If a URL
71         is absolute, it will be used as-is. If it is relative, it is assumed
72         to be relative to the current cherrypy.request.path.
73     """
74    
75     def __init__(self, urls, status=None):
76         import cherrypy
77        
78         if isinstance(urls, basestring):
79             urls = [urls]
80        
81         abs_urls = []
82         for url in urls:
83             # Note that urljoin will "do the right thing" whether url is:
84             #  1. a complete URL with host (e.g. "http://www.dummy.biz/test")
85             #  2. a URL relative to root (e.g. "/dummy")
86             #  3. a URL relative to the current path
87             # Note that any querystring in browser_url will be discarded.
88             url = urlparse.urljoin(cherrypy.request.browser_url, url)
89             abs_urls.append(url)
90         self.urls = abs_urls
91        
92         # RFC 2616 indicates a 301 response code fits our goal; however,
93         # browser support for 301 is quite messy. Do 302 instead. See
94         # http://ppewww.ph.gla.ac.uk/~flavell/www/post-redirect.html
95         if status is None:
96             if cherrypy.response.version >= "1.1":
97                 status = 303
98             else:
99                 status = 302
100         else:
101             status = int(status)
102             if status < 300 or status > 399:
103                 raise ValueError("status must be between 300 and 399.")
104        
105         self.status = status
106         Exception.__init__(self, abs_urls, status)
107    
108     def set_response(self):
109         import cherrypy
110         response = cherrypy.response
111         response.status = status = self.status
112        
113         if status in (300, 301, 302, 303, 307):
114             response.headers['Content-Type'] = "text/html"
115            
116             # "The ... URI SHOULD be given by the Location field
117             # in the response."
118             response.headers['Location'] = self.urls[0]
119            
120             # "Unless the request method was HEAD, the entity of the response
121             # SHOULD contain a short hypertext note with a hyperlink to the
122             # new URI(s)."
123             msg = {300: "This resource can be found at <a href='%s'>%s</a>.",
124                    301: "This resource has permanently moved to <a href='%s'>%s</a>.",
125                    302: "This resource resides temporarily at <a href='%s'>%s</a>.",
126                    303: "This resource can be found at <a href='%s'>%s</a>.",
127                    307: "This resource has moved temporarily to <a href='%s'>%s</a>.",
128                    }[status]
129             response.body = "<br />\n".join([msg % (u, u) for u in self.urls])
130         elif status == 304:
131             # Not Modified.
132             # "The response MUST include the following header fields:
133             # Date, unless its omission is required by section 14.18.1"
134             # The "Date" header should have been set in Request.__init__
135            
136             # "...the response SHOULD NOT include other entity-headers.
137             for key in ('Allow', 'Content-Encoding', 'Content-Language',
138                         'Content-Length', 'Content-Location', 'Content-MD5',
139                         'Content-Range', 'Content-Type', 'Expires',
140                         'Last-Modified'):
141                 if key in response.headers:
142                     del response.headers[key]
143            
144             # "The 304 response MUST NOT contain a message-body."
145             response.body = None
146         elif status == 305:
147             # Use Proxy.
148             # self.urls[0] should be the URI of the proxy.
149             response.headers['Location'] = self.urls[0]
150             response.body = None
151         else:
152             raise ValueError("The %s status code is unknown." % status)
153
154
155 class HTTPError(Error):
156     """ Exception used to return an HTTP error code to the client.
157         This exception will automatically set the response status and body.
158         
159         A custom message (a long description to display in the browser)
160         can be provided in place of the default.
161     """
162    
163     def __init__(self, status=500, message=None):
164         self.status = status = int(status)
165         if status < 400 or status > 599:
166             raise ValueError("status must be between 400 and 599.")
167         self.message = message
168         Error.__init__(self, status, message)
169    
170     def set_response(self):
171         import cherrypy
172         handler = cherrypy._cputil.get_special_attribute("_cp_on_http_error", "_cpOnHTTPError")
173         handler(self.status, self.message)
174
175
176 class NotFound(HTTPError):
177     """ Happens when a URL couldn't be mapped to any class.method """
178    
179     def __init__(self, path=None):
180         if path is None:
181             import cherrypy
182             path = cherrypy.request.path
183         self.args = (path,)
184         HTTPError.__init__(self, 404, "The path %s was not found." % repr(path))
185
186
187 class InternalError(HTTPError):
188     """ Error that should never happen """
189    
190     def __init__(self, message=None):
191         HTTPError.__init__(self, 500, message)
192
Note: See TracBrowser for help on using the browser.

Hosted by WebFaction

Log in as guest/cpguest to create tickets