1 """Error classes for CherryPy."""
2
3 from cgi import escape as _escape
4 from sys import exc_info as _exc_info
5 from urlparse import urljoin as _urljoin
6 from cherrypy.lib import http as _http
7
10
11
13 """Exception raised to switch to the handler for a different URL.
14
15 Any request.params must be supplied in a query string.
16 """
17
38
39
41 """Exception raised when the request should be redirected.
42
43 The new URL must be passed as the first argument to the Exception,
44 e.g., HTTPRedirect(newUrl). Multiple URLs are allowed. If a URL is
45 absolute, it will be used as-is. If it is relative, it is assumed
46 to be relative to the current cherrypy.request.path_info.
47 """
48
82
84 """Modify cherrypy.response status, headers, and body to represent self.
85
86 CherryPy uses this internally, but you can also use it to create an
87 HTTPRedirect object and set its output without *raising* the exception.
88 """
89 import cherrypy
90 response = cherrypy.response
91 response.status = status = self.status
92
93 if status in (300, 301, 302, 303, 307):
94 response.headers['Content-Type'] = "text/html"
95
96
97 response.headers['Location'] = self.urls[0]
98
99
100
101
102 msg = {300: "This resource can be found at <a href='%s'>%s</a>.",
103 301: "This resource has permanently moved to <a href='%s'>%s</a>.",
104 302: "This resource resides temporarily at <a href='%s'>%s</a>.",
105 303: "This resource can be found at <a href='%s'>%s</a>.",
106 307: "This resource has moved temporarily to <a href='%s'>%s</a>.",
107 }[status]
108 response.body = "<br />\n".join([msg % (u, u) for u in self.urls])
109 elif status == 304:
110
111
112
113
114
115
116 for key in ('Allow', 'Content-Encoding', 'Content-Language',
117 'Content-Length', 'Content-Location', 'Content-MD5',
118 'Content-Range', 'Content-Type', 'Expires',
119 'Last-Modified'):
120 if key in response.headers:
121 del response.headers[key]
122
123
124 response.body = None
125 elif status == 305:
126
127
128 response.headers['Location'] = self.urls[0]
129 response.body = None
130 else:
131 raise ValueError("The %s status code is unknown." % status)
132
134 """Use this exception as a request.handler (raise self)."""
135 raise self
136
137
139 """ Exception used to return an HTTP error code (4xx-5xx) to the client.
140 This exception will automatically set the response status and body.
141
142 A custom message (a long description to display in the browser)
143 can be provided in place of the default.
144 """
145
146 - def __init__(self, status=500, message=None):
152
154 """Modify cherrypy.response status, headers, and body to represent self.
155
156 CherryPy uses this internally, but you can also use it to create an
157 HTTPError object and set its output without *raising* the exception.
158 """
159 import cherrypy
160
161 response = cherrypy.response
162
163
164
165 respheaders = response.headers
166 for key in ["Accept-Ranges", "Age", "ETag", "Location", "Retry-After",
167 "Vary", "Content-Encoding", "Content-Length", "Expires",
168 "Content-Location", "Content-MD5", "Last-Modified"]:
169 if respheaders.has_key(key):
170 del respheaders[key]
171
172 if self.status != 416:
173
174
175
176
177
178
179 if respheaders.has_key("Content-Range"):
180 del respheaders["Content-Range"]
181
182
183
184 response.status = self.status
185 tb = None
186 if cherrypy.request.show_tracebacks:
187 tb = format_exc()
188 content = get_error_page(self.status, traceback=tb,
189 message=self.message)
190 response.body = content
191 respheaders['Content-Length'] = len(content)
192 respheaders['Content-Type'] = "text/html"
193
194 _be_ie_unfriendly(self.status)
195
197 """Use this exception as a request.handler (raise self)."""
198 raise self
199
200
202 """Exception raised when a URL could not be mapped to any handler (404)."""
203
210
211
213 """Exception raised when Response.timed_out is detected."""
214 pass
215
216
217 _HTTPErrorTemplate = '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
218 <html>
219 <head>
220 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
221 <title>%(status)s</title>
222 <style type="text/css">
223 #powered_by {
224 margin-top: 20px;
225 border-top: 2px solid black;
226 font-style: italic;
227 }
228
229 #traceback {
230 color: red;
231 }
232 </style>
233 </head>
234 <body>
235 <h2>%(status)s</h2>
236 <p>%(message)s</p>
237 <pre id="traceback">%(traceback)s</pre>
238 <div id="powered_by">
239 <span>Powered by <a href="http://www.cherrypy.org">CherryPy %(version)s</a></span>
240 </div>
241 </body>
242 </html>
243 '''
244
245 -def get_error_page(status, **kwargs):
246 """Return an HTML page, containing a pretty error response.
247
248 status should be an int or a str.
249 kwargs will be interpolated into the page template.
250 """
251 import cherrypy
252
253 try:
254 code, reason, message = _http.valid_status(status)
255 except ValueError, x:
256 raise cherrypy.HTTPError(500, x.args[0])
257
258
259
260 if kwargs.get('status') is None:
261 kwargs['status'] = "%s %s" % (code, reason)
262 if kwargs.get('message') is None:
263 kwargs['message'] = message
264 if kwargs.get('traceback') is None:
265 kwargs['traceback'] = ''
266 if kwargs.get('version') is None:
267 kwargs['version'] = cherrypy.__version__
268 for k, v in kwargs.iteritems():
269 if v is None:
270 kwargs[k] = ""
271 else:
272 kwargs[k] = _escape(kwargs[k])
273
274 template = _HTTPErrorTemplate
275
276
277 error_page_file = cherrypy.request.error_page.get(code, '')
278 if error_page_file:
279 try:
280 template = file(error_page_file, 'rb').read()
281 except:
282 m = kwargs['message']
283 if m:
284 m += "<br />"
285 m += ("In addition, the custom error page "
286 "failed:\n<br />%s" % (_exc_info()[1]))
287 kwargs['message'] = m
288
289 return template % kwargs
290
291
292 _ie_friendly_error_sizes = {
293 400: 512, 403: 256, 404: 512, 405: 256,
294 406: 512, 408: 512, 409: 512, 410: 256,
295 500: 512, 501: 512, 505: 512,
296 }
297
298
321
322
331
333 """Produce status, headers, body for a critical error.
334
335 Returns a triple without calling any other questionable functions,
336 so it should be as error-free as possible. Call it from an HTTP server
337 if you get errors outside of the request.
338
339 If extrabody is None, a friendly but rather unhelpful error message
340 is set in the body. If extrabody is a string, it will be appended
341 as-is to the body.
342 """
343
344
345
346
347
348
349 body = "Unrecoverable error in the server."
350 if extrabody is not None:
351 body += "\n" + extrabody
352
353 return ("500 Internal Server Error",
354 [('Content-Type', 'text/plain'),
355 ('Content-Length', str(len(body)))],
356 [body])
357