Ticket #800: error_page_callable.patch
-
_cperror.py
old new 148 148 raise self 149 149 150 150 151 def clean_headers(status): 152 """Remove any headers which should not apply to an error response.""" 153 import cherrypy 154 155 response = cherrypy.response 156 157 # Remove headers which applied to the original content, 158 # but do not apply to the error page. 159 respheaders = response.headers 160 for key in ["Accept-Ranges", "Age", "ETag", "Location", "Retry-After", 161 "Vary", "Content-Encoding", "Content-Length", "Expires", 162 "Content-Location", "Content-MD5", "Last-Modified"]: 163 if respheaders.has_key(key): 164 del respheaders[key] 165 166 if status != 416: 167 # A server sending a response with status code 416 (Requested 168 # range not satisfiable) SHOULD include a Content-Range field 169 # with a byte-range-resp-spec of "*". The instance-length 170 # specifies the current length of the selected resource. 171 # A response with status code 206 (Partial Content) MUST NOT 172 # include a Content-Range field with a byte-range- resp-spec of "*". 173 if respheaders.has_key("Content-Range"): 174 del respheaders["Content-Range"] 175 176 151 177 class HTTPError(CherryPyException): 152 178 """ Exception used to return an HTTP error code (4xx-5xx) to the client. 153 179 This exception will automatically set the response status and body. … … 173 199 174 200 response = cherrypy.response 175 201 176 # Remove headers which applied to the original content, 177 # but do not apply to the error page. 178 respheaders = response.headers 179 for key in ["Accept-Ranges", "Age", "ETag", "Location", "Retry-After", 180 "Vary", "Content-Encoding", "Content-Length", "Expires", 181 "Content-Location", "Content-MD5", "Last-Modified"]: 182 if respheaders.has_key(key): 183 del respheaders[key] 202 clean_headers(self.status) 184 203 185 if self.status != 416:186 # A server sending a response with status code 416 (Requested187 # range not satisfiable) SHOULD include a Content-Range field188 # with a byte-range-resp-spec of "*". The instance-length189 # specifies the current length of the selected resource.190 # A response with status code 206 (Partial Content) MUST NOT191 # include a Content-Range field with a byte-range- resp-spec of "*".192 if respheaders.has_key("Content-Range"):193 del respheaders["Content-Range"]194 195 204 # In all cases, finalize will be called after this method, 196 205 # so don't bother cleaning up response values here. 197 206 response.status = self.status 198 207 tb = None 199 208 if cherrypy.request.show_tracebacks: 200 209 tb = format_exc() 201 resp headers['Content-Type'] = "text/html"210 response.headers['Content-Type'] = "text/html" 202 211 203 212 content = self.get_error_page(self.status, traceback=tb, 204 213 message=self.message) 205 214 response.body = content 206 resp headers['Content-Length'] = len(content)215 response.headers['Content-Length'] = len(content) 207 216 208 217 _be_ie_unfriendly(self.status) 209 218 -
_cprequest.py
old new 591 591 self.hooks.run('before_finalize') 592 592 cherrypy.response.finalize() 593 593 except (cherrypy.HTTPRedirect, cherrypy.HTTPError), inst: 594 inst.set_response() 594 ep = self.error_page.get(inst.status, '') 595 if ep and callable(ep): 596 ep(inst) 597 else: 598 inst.set_response() 595 599 self.stage = 'before_finalize (HTTPError)' 596 600 self.hooks.run('before_finalize') 597 601 cherrypy.response.finalize() … … 715 719 self.params.update(p) 716 720 717 721 def handle_error(self, exc): 718 """Handle the last exception. (Core)"""722 """Handle the last unanticipated exception. (Core)""" 719 723 try: 720 724 self.hooks.run("before_error_response") 721 725 if self.error_response: … … 723 727 self.hooks.run("after_error_response") 724 728 cherrypy.response.finalize() 725 729 except cherrypy.HTTPRedirect, inst: 726 inst.set_response() 730 ep = self.error_page.get(inst.status, '') 731 if ep and callable(ep): 732 ep(inst) 733 else: 734 inst.set_response() 727 735 cherrypy.response.finalize() 728 736 729 737 -
test/test_core.py
old new 239 239 def as_refyield(self): 240 240 for chunk in self.as_yield(): 241 241 yield chunk 242 243 242 243 244 def page401(e): 245 cherrypy.response.status = e.status 246 cherrypy._cperror.clean_headers(e.status) 247 cherrypy.response.body = "Well, I'm very sorry but you haven't paid!" 248 249 244 250 class Error(Test): 245 251 246 252 _cp_config = {'tools.log_tracebacks.on': True, 247 253 } 248 254 249 def custom(self): 250 raise cherrypy.HTTPError(404, "No, <b>really</b>, not found!") 251 custom._cp_config = {'error_page.404': os.path.join(localDir, "static/index.html")} 255 def custom(self, err='404'): 256 raise cherrypy.HTTPError(int(err), "No, <b>really</b>, not found!") 257 custom._cp_config = {'error_page.404': os.path.join(localDir, "static/index.html"), 258 'error_page.401': page401, 259 } 252 260 253 261 def noexist(self): 254 262 raise cherrypy.HTTPError(404, "No, <b>really</b>, not found!") … … 725 733 self.assertStatus(404) 726 734 self.assertBody("Hello, world\r\n" + (" " * 499)) 727 735 736 # Test custom error page. 737 self.getPage("/error/custom?err=401") 738 self.assertStatus(401) 739 self.assertBody("Well, I'm very sorry but you haven't paid!") 740 728 741 # Test error in custom error page (ticket #305). 729 742 # Note that the message is escaped for HTML (ticket #310). 730 743 self.getPage("/error/noexist")

