Changeset 640
- Timestamp:
- 09/13/05 18:56:39
- Files:
-
- branches/filterorder/cherrypy/_cpcgifs.py (modified) (1 diff)
- branches/filterorder/cherrypy/_cperror.py (modified) (2 diffs)
- branches/filterorder/cherrypy/_cphttptools.py (modified) (3 diffs)
- branches/filterorder/cherrypy/_cputil.py (modified) (4 diffs)
- branches/filterorder/cherrypy/lib/cptools.py (modified) (1 diff)
- branches/filterorder/cherrypy/lib/httperrors.py (deleted)
- branches/filterorder/cherrypy/test/helper.py (modified) (2 diffs)
- branches/filterorder/cherrypy/test/test_core.py (modified) (1 diff)
- branches/filterorder/cherrypy/test/test_gzip_filter.py (modified) (2 diffs)
- branches/filterorder/cherrypy/tutorial/tut10_http_errors.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
branches/filterorder/cherrypy/_cpcgifs.py
r622 r640 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 branches/filterorder/cherrypy/_cperror.py
r613 r640 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) branches/filterorder/cherrypy/_cphttptools.py
r638 r640 291 291 applyFilters('onEndResource') 292 292 except: 293 # This includes HTTP StatusError and NotFound293 # This includes HTTPError and NotFound 294 294 handleError(sys.exc_info()) 295 295 … … 383 383 except _cpwsgiserver.MaxSizeExceeded: 384 384 # Post data is too big 385 raise _cperror.HTTP StatusError(413)385 raise _cperror.HTTPError(413) 386 386 387 387 if forms.file: … … 425 425 applyFilters('beforeErrorResponse') 426 426 427 # _cpOnError and _cpOnHTTPErrorwill probably change cherrypy.response.body.427 # _cpOnError will probably change cherrypy.response.body. 428 428 # They may also change the headerMap, etc. 429 if sys.exc_info()[0] is cherrypy.HTTPStatusError: 430 getSpecialAttribute('_cpOnHTTPError')() 431 else: 432 getSpecialAttribute('_cpOnError')() 429 _cputil.getSpecialAttribute('_cpOnError')() 433 430 434 431 finalize() branches/filterorder/cherrypy/_cputil.py
r638 r640 36 36 37 37 import cherrypy 38 from cherrypy.lib import httperrors39 40 38 41 39 class EmptyClass: … … 154 152 f.close() 155 153 156 def _cpOnHTTPError(): 157 """ Default _cpOnHTTPError method """ 158 status, customMessage = sys.exc_info()[1].getArgs() 159 160 # get the error page 161 page = httperrors.getErrorPage(status, customMessage = customMessage) 162 cherrypy.response.status, cherrypy.response.body = page 163 164 if cherrypy.response.headerMap.has_key('Content-Encoding'): 165 del cherrypy.response.headerMap['Content-Encoding'] 154 def _HTTPErrorTemplate(errorString, message, traceback, version): 155 subTuple = (errorString, errorString, message, traceback, cherrypy.__version__) 156 157 return '''<?xml version="1.0" encoding="UTF-8"?> 158 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 159 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 160 <html> 161 <head> 162 <title>%s</title> 163 <style type="text/css"> 164 #poweredBy { 165 margin-top: 20px; 166 border-top: 2px solid black; 167 font-style: italic; 168 } 169 170 #traceback { 171 color: red; 172 } 173 </style> 174 </head> 175 <body> 176 <h2>%s</h2> 177 <p>%s</p> 178 <pre id="traceback">%s</pre> 179 <div id="poweredBy"> 180 <span>Powered by <a href="http://www.cherrypy.org">Cherrypy %s</a></span> 181 </div> 182 </body> 183 </html> 184 ''' % subTuple 185 186 import BaseHTTPServer 187 _HTTPResponses = BaseHTTPServer.BaseHTTPRequestHandler.responses 188 189 def getErrorStatusAndPage(status, traceback = None): 190 statusString, message = _HTTPResponses[status] 191 statusString = '%d %s' % (status, statusString) 192 193 if traceback is None: 194 traceback = '' 195 # get the traceback from formatExc 196 developmentMode = (cherrypy.config.get('server.environment') == 'development') 197 if cherrypy.config.get('server.showTracebacks') or developmentMode: 198 traceback = formatExc() 199 200 page = _HTTPErrorTemplate(statusString, message, traceback, cherrypy.__version__) 201 202 return statusString, page 166 203 167 204 def formatExc(exc=None): … … 169 206 if exc is None: 170 207 exc = sys.exc_info() 208 209 if exc == (None, None, None): 210 return "" 211 return "".join(traceback.format_exception(*exc)) 212 213 def getErrorStatusAndPage(status, traceback = None): 214 statusString, message = _HTTPResponses[status] 215 statusString = '%d %s' % (status, statusString) 216 217 if traceback is None: 218 traceback = '' 219 # get the traceback from formatExc 220 developmentMode = (cherrypy.config.get('server.environment') == 'development') 221 if cherrypy.config.get('server.showTracebacks') or developmentMode: 222 traceback = formatExc() 223 224 page = _HTTPErrorTemplate(statusString, message, traceback, cherrypy.__version__) 225 226 return statusString, page 227 228 """formatExc(exc=None) -> exc (or sys.exc_info), formatted.""" 229 if exc is None: 230 exc = sys.exc_info() 231 232 if exc == (None, None, None): 233 return "" 234 171 235 return "".join(traceback.format_exception(*exc)) 172 236 173 237 def _cpOnError(): 174 238 """ Default _cpOnError method """ 175 developmentMode = cherrypy.config.get('server.environment') == 'development'176 httpErrors = cherrypy.config.get('server.httpErrors')177 showTracebacks = cherrypy.config.get('server.showTracebacks')178 239 179 240 logTracebacks = cherrypy.config.get('server.logTracebacks', True) … … 183 244 response = cherrypy.response 184 245 185 if not developmentMode and httpErrors: 186 # if it isn't development mode and http errors are turned on 187 # set the response status and render the body 188 if response.status == 404: 189 response.status, response.body = httperrors.getErrorPage(404) 190 else: 191 response.status, response.body = httperrors.getErrorPage(500) 192 elif developmentMode or showTracebacks: 193 # if it is development mode or tracebacks are turned on 194 # return the traceback as plaintext 195 response.body = [formatExc()] 196 response.headerMap['Content-Type'] = 'text/plain' 246 if isinstance(sys.exc_info()[1], cherrypy.HTTPError): 247 # status, body already set 248 pass 197 249 else: 198 # If it is production mode but http errors are disabled then 199 # Display a simple, generic error message 200 response.body = "Unrecoverable error in the server" 201 response.headerMap['Content-Type'] = 'text/plain' 250 response.status, response.body = getErrorStatusAndPage(500) 202 251 203 252 if cherrypy.response.headerMap.has_key('Content-Encoding'): branches/filterorder/cherrypy/lib/cptools.py
r608 r640 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 branches/filterorder/cherrypy/test/helper.py
r638 r640 39 39 import webtest 40 40 import types 41 import re 42 41 43 for _x in dir(cherrypy): 42 44 y = getattr(cherrypy, _x) … … 129 131 else: 130 132 webtest.WebCase.getPage(self, url, headers, method, body) 133 134 def assertErrorPage(self, errorCode, pattern = ''): 135 """ Compare the response body with a built in error page. 136 The function will optionally look for the regexp pattern, 137 within the exception embedded in the error page. 138 """ 139 140 from cherrypy._cputil import getErrorStatusAndPage 141 page = getErrorStatusAndPage(errorCode, '')[1] 142 143 # escape the question marks 144 page = page.replace('?', r'\?') 145 146 # re to find the traceback in the page 147 traceRe = re.compile('(<pre id="traceback">)(</pre>)') 148 149 # stick the pattern in the page so we can match everythign 150 # at once 151 page = traceRe.sub( '\g<1>.*%s.*\g<2>' % pattern, page) 152 153 # check if there is no exception 154 if pattern and traceRe.search(self.body): 155 msg = 'No match for %s in body' % `pattern` 156 self._handlewebError(msg) 157 else: 158 if not re.search(page, self.body, re.DOTALL): 159 msg = 'Error page does not match' 160 self._handlewebError(msg) 131 161 132 162 CPTestLoader = webtest.ReloadingTestLoader() branches/filterorder/cherrypy/test/test_core.py
r638 r640 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") branches/filterorder/cherrypy/test/test_gzip_filter.py
r605 r640 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() branches/filterorder/cherrypy/tutorial/tut10_http_errors.py
r608 r640 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()

