Changeset 639
- Timestamp:
- 09/13/05 17:47:59
- Files:
-
- branches/httperrors (deleted)
- trunk/cherrypy/_cpcgifs.py (modified) (1 diff)
- trunk/cherrypy/_cperror.py (modified) (2 diffs)
- trunk/cherrypy/_cphttptools.py (modified) (4 diffs)
- trunk/cherrypy/_cputil.py (modified) (4 diffs)
- trunk/cherrypy/lib/cptools.py (modified) (1 diff)
- trunk/cherrypy/lib/httperrors.py (deleted)
- trunk/cherrypy/test/helper.py (modified) (2 diffs)
- trunk/cherrypy/test/test_core.py (modified) (1 diff)
- trunk/cherrypy/test/test_gzip_filter.py (modified) (2 diffs)
- trunk/cherrypy/tutorial/tut10_http_errors.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/_cpcgifs.py
r622 r639 9 9 except ValueError, ex: 10 10 if str(ex) == 'Maximum content length exceeded': 11 raise cherrypy.HTTP StatusError(status=413)11 raise cherrypy.HTTPError(status=413) 12 12 else: 13 13 raise ex trunk/cherrypy/_cperror.py
r613 r639 173 173 _missing = object() 174 174 175 class HTTPStatusError(Error): 176 """Exception raised when the client has made an error in its request.""" 177 178 def __init__(self, status=400, message=_missing): 175 class HTTPError(Error): 176 """ Exception raised when the client has made an error in its request. 177 This exception will automatically set the response status and body. 178 179 A custom body can be pased to the init method in place of the standard error page. 180 """ 181 182 def __init__(self, status=500, body=_missing): 179 183 self.status = status = int(status) 180 184 if status < 400 or status > 599: … … 183 187 # these 4 lines might dissapear 184 188 import cherrypy 185 cherrypy.response.status = status 186 if message is not _missing: 187 cherrypy.response.body=message 188 189 self.message = message 190 191 def getArgs(self): 192 return (self.status, self.message) 193 194 195 class NotFound(HTTPStatusError): 189 self.statusString = cherrypy._cputil.getErrorStatusAndPage(status)[0] 190 cherrypy.response.status = self.statusString 191 192 if body is _missing: 193 # because the init method is called before the exception is raised 194 # it is impossible to embed the traceback in the error page at this point. 195 # We use a generator so that the error page is generated at a later point ( 196 # after the exception is raised). 197 cherrypy.response.body = self.pageGenerator() 198 else: 199 cherrypy.response.body = body 200 201 def __str__(self): 202 return self.statusString 203 204 def pageGenerator(self): 205 import cherrypy 206 yield cherrypy._cputil.getErrorStatusAndPage(self.status)[1] 207 208 class NotFound(HTTPError): 196 209 """ Happens when a URL couldn't be mapped to any class.method """ 197 210 198 211 def __init__(self, path): 199 212 self.args = (path,) 200 HTTP StatusError.__init__(self, 404)213 HTTPError.__init__(self, 404) trunk/cherrypy/_cphttptools.py
r630 r639 45 45 46 46 import cherrypy 47 from cherrypy import _cputil, _cpcgifs, _cp error, _cpwsgiserver47 from cherrypy import _cputil, _cpcgifs, _cpwsgiserver, _cperror 48 48 from cherrypy.lib import cptools 49 49 … … 289 289 applyFilters('onEndResource') 290 290 except: 291 # This includes HTTP StatusError and NotFound291 # This includes HTTPError and NotFound 292 292 handleError(sys.exc_info()) 293 293 … … 381 381 except _cpwsgiserver.MaxSizeExceeded: 382 382 # Post data is too big 383 raise _cperror.HTTP StatusError(413)383 raise _cperror.HTTPError(413) 384 384 385 385 if forms.file: … … 423 423 applyFilters('beforeErrorResponse') 424 424 425 # _cpOnError and _cpOnHTTPErrorwill probably change cherrypy.response.body.425 # _cpOnError will probably change cherrypy.response.body. 426 426 # They may also change the headerMap, etc. 427 if sys.exc_info()[0] is cherrypy.HTTPStatusError: 428 _cputil.getSpecialAttribute('_cpOnHTTPError')() 429 else: 430 _cputil.getSpecialAttribute('_cpOnError')() 427 _cputil.getSpecialAttribute('_cpOnError')() 431 428 432 429 finalize() trunk/cherrypy/_cputil.py
r625 r639 38 38 39 39 import cherrypy 40 from cherrypy.lib import httperrors41 42 40 43 41 class EmptyClass: … … 153 151 f.close() 154 152 155 def _cpOnHTTPError(): 156 """ Default _cpOnHTTPError method """ 157 status, customMessage = sys.exc_info()[1].getArgs() 158 159 # get the error page 160 page = httperrors.getErrorPage(status, customMessage = customMessage) 161 cherrypy.response.status, cherrypy.response.body = page 162 163 if cherrypy.response.headerMap.has_key('Content-Encoding'): 164 del cherrypy.response.headerMap['Content-Encoding'] 153 def _HTTPErrorTemplate(errorString, message, traceback, version): 154 subTuple = (errorString, errorString, message, traceback, cherrypy.__version__) 155 156 return '''<?xml version="1.0" encoding="UTF-8"?> 157 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 158 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 159 <html> 160 <head> 161 <title>%s</title> 162 <style type="text/css"> 163 #poweredBy { 164 margin-top: 20px; 165 border-top: 2px solid black; 166 font-style: italic; 167 } 168 169 #traceback { 170 color: red; 171 } 172 </style> 173 </head> 174 <body> 175 <h2>%s</h2> 176 <p>%s</p> 177 <pre id="traceback">%s</pre> 178 <div id="poweredBy"> 179 <span>Powered by <a href="http://www.cherrypy.org">Cherrypy %s</a></span> 180 </div> 181 </body> 182 </html> 183 ''' % subTuple 184 185 import BaseHTTPServer 186 _HTTPResponses = BaseHTTPServer.BaseHTTPRequestHandler.responses 187 188 def getErrorStatusAndPage(status, traceback = None): 189 statusString, message = _HTTPResponses[status] 190 statusString = '%d %s' % (status, statusString) 191 192 if traceback is None: 193 traceback = '' 194 # get the traceback from formatExc 195 developmentMode = (cherrypy.config.get('server.environment') == 'development') 196 if cherrypy.config.get('server.showTracebacks') or developmentMode: 197 traceback = formatExc() 198 199 page = _HTTPErrorTemplate(statusString, message, traceback, cherrypy.__version__) 200 201 return statusString, page 165 202 166 203 def formatExc(exc=None): … … 168 205 if exc is None: 169 206 exc = sys.exc_info() 207 208 if exc == (None, None, None): 209 return "" 210 return "".join(traceback.format_exception(*exc)) 211 212 def getErrorStatusAndPage(status, traceback = None): 213 statusString, message = _HTTPResponses[status] 214 statusString = '%d %s' % (status, statusString) 215 216 if traceback is None: 217 traceback = '' 218 # get the traceback from formatExc 219 developmentMode = (cherrypy.config.get('server.environment') == 'development') 220 if cherrypy.config.get('server.showTracebacks') or developmentMode: 221 traceback = formatExc() 222 223 page = _HTTPErrorTemplate(statusString, message, traceback, cherrypy.__version__) 224 225 return statusString, page 226 227 """formatExc(exc=None) -> exc (or sys.exc_info), formatted.""" 228 if exc is None: 229 exc = sys.exc_info() 230 231 if exc == (None, None, None): 232 return "" 233 170 234 return "".join(traceback.format_exception(*exc)) 171 235 172 236 def _cpOnError(): 173 237 """ Default _cpOnError method """ 174 developmentMode = cherrypy.config.get('server.environment') == 'development'175 httpErrors = cherrypy.config.get('server.httpErrors')176 showTracebacks = cherrypy.config.get('server.showTracebacks')177 238 178 239 logTracebacks = cherrypy.config.get('server.logTracebacks', True) … … 182 243 response = cherrypy.response 183 244 184 if not developmentMode and httpErrors: 185 # if it isn't development mode and http errors are turned on 186 # set the response status and render the body 187 if response.status == 404: 188 response.status, response.body = httperrors.getErrorPage(404) 189 else: 190 response.status, response.body = httperrors.getErrorPage(500) 191 elif developmentMode or showTracebacks: 192 # if it is development mode or tracebacks are turned on 193 # return the traceback as plaintext 194 response.body = [formatExc()] 195 response.headerMap['Content-Type'] = 'text/plain' 245 if isinstance(sys.exc_info()[1], cherrypy.HTTPError): 246 # status, body already set 247 pass 196 248 else: 197 # If it is production mode but http errors are disabled then 198 # Display a simple, generic error message 199 response.body = "Unrecoverable error in the server" 200 response.headerMap['Content-Type'] = 'text/plain' 249 response.status, response.body = getErrorStatusAndPage(500) 201 250 202 251 if cherrypy.response.headerMap.has_key('Content-Encoding'): trunk/cherrypy/lib/cptools.py
r608 r639 206 206 cherrypy.response.headerMap['Content-Range'] = "bytes */%s" % content_length 207 207 message = "Invalid Range (first-byte-pos greater than Content-Length)" 208 raise cherrypy.HTTP StatusError(416, message)208 raise cherrypy.HTTPError(416, message) 209 209 210 210 return result trunk/cherrypy/test/helper.py
r587 r639 37 37 import webtest 38 38 import types 39 import re 40 39 41 for _x in dir(cherrypy): 40 42 y = getattr(cherrypy, _x) … … 127 129 else: 128 130 webtest.WebCase.getPage(self, url, headers, method, body) 131 132 def assertErrorPage(self, errorCode, pattern = ''): 133 """ Compare the response body with a built in error page. 134 The function will optionally look for the regexp pattern, 135 within the exception embedded in the error page. 136 """ 137 138 from cherrypy._cputil import getErrorStatusAndPage 139 page = getErrorStatusAndPage(errorCode, '')[1] 140 141 # escape the question marks 142 page = page.replace('?', r'\?') 143 144 # re to find the traceback in the page 145 traceRe = re.compile('(<pre id="traceback">)(</pre>)') 146 147 # stick the pattern in the page so we can match everythign 148 # at once 149 page = traceRe.sub( '\g<1>.*%s.*\g<2>' % pattern, page) 150 151 # check if there is no exception 152 if pattern and traceRe.search(self.body): 153 msg = 'No match for %s in body' % `pattern` 154 self._handlewebError(msg) 155 else: 156 if not re.search(page, self.body, re.DOTALL): 157 msg = 'Error page does not match' 158 self._handlewebError(msg) 129 159 130 160 CPTestLoader = webtest.ReloadingTestLoader() trunk/cherrypy/test/test_core.py
r626 r639 490 490 self.getPage("/error/missing") 491 491 self.assertStatus("404 Not Found") 492 self.assert InBody("NotFound")492 self.assertErrorPage(404) 493 493 494 494 ignore = helper.webtest.ignored_exceptions 495 495 ignore.append(ValueError) 496 496 try: 497 valerr = r'\n raise ValueError\(\)\nValueError\n $'497 valerr = r'\n raise ValueError\(\)\nValueError\n' 498 498 self.getPage("/error/page_method") 499 self.assert MatchesBody(valerr)500 499 self.assertErrorPage(500, valerr) 500 501 501 import cherrypy 502 502 proto = cherrypy.config.get("server.protocolVersion", "HTTP/1.0") trunk/cherrypy/test/test_gzip_filter.py
r605 r639 46 46 'server.logToScreen': False, 47 47 'server.environment': 'production', 48 'server.httpErrors': False,49 48 'server.showTracebacks': True, 50 49 'gzipFilter.on': True, … … 83 82 else: 84 83 self.assertNoHeader('Content-Encoding') 85 self.assertMatchesBody(r"IndexError\n$") 84 self.assertStatus('500 Internal error') 85 self.assertErrorPage(500, "IndexError\n") 86 86 finally: 87 87 helper.webtest.ignored_exceptions.pop() trunk/cherrypy/tutorial/tut10_http_errors.py
r608 r639 18 18 <a href="toggleTracebacks">Toggle tracebacks %s</a><br/><br/> 19 19 <a href="/doesNotExist">Click me i'm a broken link!</a> 20 <br/> 21 <a href="/customMessage">Use a custom error message</a> 20 22 <br/><br/> 21 23 These errors are explicitly raised by the application. … … 40 42 # raise an error based on the get query 41 43 code = int(code) 42 raise cherrypy.HTTP StatusError(status = code)44 raise cherrypy.HTTPError(status = code) 43 45 error.exposed = True 46 47 def customMessage(self): 48 raise cherrypy.HTTPError(500, "Plain text message") 49 customMessage.exposed = True 44 50 45 51 cherrypy.root = HTTPErrorDemo()

