Ticket #600: forward.patch
-
_cpconfig.py
old new 48 48 _cp_config = {'tools.gzip.on': True} 49 49 50 50 def index(self): 51 raise cherrypy.InternalRedirect("/cuba") 51 return "Hello world" 52 52 index.exposed = True 53 index._cp_config = {'request. recursive_redirect': True}53 index._cp_config = {'request.show_tracebacks': False} 54 54 55 55 56 56 Namespaces -
_cpdispatch.py
old new 308 308 merge(app.config[curpath]) 309 309 310 310 return handler 311 312 313 def XMLRPCDispatcher(next_dispatcher=Dispatcher()): 314 from cherrypy.lib import xmlrpc 315 def xmlrpc_dispatch(path_info): 316 path_info = xmlrpc.patched_path(path_info) 317 return next_dispatcher(path_info) 318 return xmlrpc_dispatch 319 320 321 def VirtualHost(next_dispatcher=Dispatcher(), use_x_forwarded_host=True, **domains): 322 """Select a different handler based on the Host header. 323 324 Useful when running multiple sites within one CP server. 325 326 From http://groups.google.com/group/cherrypy-users/browse_thread/thread/f393540fe278e54d: 327 328 For various reasons I need several domains to point to different parts of a 329 single website structure as well as to their own "homepage" EG 330 331 http://www.mydom1.com -> root 332 http://www.mydom2.com -> root/mydom2/ 333 http://www.mydom3.com -> root/mydom3/ 334 http://www.mydom4.com -> under construction page 335 336 but also to have http://www.mydom1.com/mydom2/ etc to be valid pages in 337 their own right. 338 """ 339 from cherrypy.lib import http 340 def vhost_dispatch(path_info): 341 header = cherrypy.request.headers.get 342 343 domain = header('Host', '') 344 if use_x_forwarded_host: 345 domain = header("X-Forwarded-Host", domain) 346 347 prefix = domains.get(domain, "") 348 if prefix: 349 path_info = http.urljoin(prefix, path_info) 350 351 return next_dispatcher(path_info) 352 return vhost_dispatch 353 -
_cperror.py
old new 12 12 class InternalRedirect(CherryPyException): 13 13 """Exception raised to switch to the handler for a different URL. 14 14 15 If you supply a query string, it will replace request.params. 16 If you omit the query string (no question mark), the params from the 17 original request will remain in effect. 18 To erase any query string parameters, write url + "?" with no params. 15 Any request.params must be supplied in a query string. 19 16 """ 20 17 21 18 def __init__(self, path): -
_cprequest.py
old new 135 135 class Request(object): 136 136 """An HTTP request.""" 137 137 138 prev = None 139 138 140 # Conversation/connection attributes 139 141 local = http.Host("localhost", 80) 140 142 remote = http.Host("localhost", 1111) … … 276 278 # path_info should be the path from the 277 279 # app root (script_name) to the handler. 278 280 self.script_name = self.app.script_name 279 self.path_info = p ath[len(self.script_name.rstrip("/")):]281 self.path_info = pi = path[len(self.script_name.rstrip("/")):] 280 282 281 # Loop to allow for InternalRedirect. 282 pi = self.path_info 283 qs = self.query_string 284 while True: 285 try: 286 self.respond(pi) 287 break 288 except cherrypy.InternalRedirect, ir: 289 if (ir.path in self.redirections 290 and not self.recursive_redirect): 291 raise RuntimeError("InternalRedirect visited the " 292 "same URL twice: %s" % repr(ir.path)) 293 294 # Add the *previous* path_info + qs to self.redirections. 295 if qs: 296 qs = "?" + qs 297 self.redirections.append(pi + qs) 298 299 pi = self.path_info = ir.path 300 qs = self.query_string = ir.query_string 301 if qs: 302 self.params = http.parse_query_string(qs) 303 except (KeyboardInterrupt, SystemExit): 283 self.respond(pi) 284 285 except (KeyboardInterrupt, SystemExit, cherrypy.InternalRedirect): 304 286 raise 305 287 except: 306 288 if self.throw_errors: … … 383 365 self.handle_error(sys.exc_info()) 384 366 385 367 def process_headers(self): 386 # Guard against re-reading body (e.g. on InternalRedirect)387 if self.headers_read:388 return389 self.headers_read = True390 391 368 self.params = http.parse_query_string(self.query_string) 392 369 393 370 # Process the headers into self.headers … … 451 428 452 429 def process_body(self): 453 430 """Convert request.rfile into request.params (or request.body).""" 454 # Guard against re-reading body (e.g. on InternalRedirect)455 if self.body_read:456 return457 self.body_read = True458 459 431 # FieldStorage only recognizes POST, so fake it. 460 432 methenv = {'REQUEST_METHOD': "POST"} 461 433 try: -
_cptools.py
old new 214 214 index = __call__ 215 215 216 216 217 class XMLRPCTool(object):218 """Tool for using XMLRPC over HTTP.219 220 Python's None value cannot be used in standard XML-RPC; to allow221 using it via an extension, provide a true value for allow_none.222 """223 224 def _setup(self):225 """Hook this tool into cherrypy.request."""226 request = cherrypy.request227 228 # Guard against running this method twice.229 if hasattr(request, 'xmlrpc'):230 return231 request.xmlrpc = True232 233 request.error_response = _xmlrpc.on_error234 path_info = request.path_info235 ppath = _xmlrpc.patched_path(path_info)236 if ppath != path_info:237 raise cherrypy.InternalRedirect(ppath)238 239 240 217 class WSGIAppTool(HandlerTool): 241 218 """A tool for running any WSGI middleware/application within CP. 242 219 … … 332 309 _d.session_auth = SessionAuthTool(cptools.session_auth) 333 310 _d.proxy = Tool('before_request_body', cptools.proxy, priority=30) 334 311 _d.response_headers = Tool('on_start_resource', cptools.response_headers) 335 _d.virtual_host = Tool('on_start_resource', cptools.virtual_host, priority=40)336 312 _d.log_tracebacks = Tool('before_error_response', cptools.log_traceback) 337 313 _d.log_headers = Tool('before_error_response', cptools.log_request_headers) 338 314 _d.err_redirect = ErrorTool(cptools.redirect) … … 345 321 _d.staticfile = HandlerTool(static.staticfile) 346 322 # _sessions.init must be bound after headers are read 347 323 _d.sessions = SessionTool('before_request_body', _sessions.init) 348 _d.xmlrpc = XMLRPCTool()324 _d.xmlrpc = ErrorTool(_xmlrpc.on_error) 349 325 _d.wsgiapp = WSGIAppTool(_wsgiapp.run) 350 326 _d.caching = CachingTool('before_handler', _caching.get, 'caching') 351 327 _d.expires = Tool('before_finalize', _caching.expires) -
_cptree.py
old new 1 1 """CherryPy Application and Tree objects.""" 2 2 3 3 import os 4 import StringIO 4 5 import sys 5 6 import cherrypy 6 7 from cherrypy import _cpconfig, _cplogging, tools … … 86 87 remote = http.Host(env('REMOTE_ADDR', ''), 87 88 int(env('REMOTE_PORT', -1)), 88 89 env('REMOTE_HOST', '')) 89 request = cherrypy.engine.request(local, remote, 90 env('wsgi.url_scheme'), 91 env('ACTUAL_SERVER_PROTOCOL', "HTTP/1.1")) 92 90 scheme = env('wsgi.url_scheme') 91 sproto = env('ACTUAL_SERVER_PROTOCOL', "HTTP/1.1") 93 92 # LOGON_USER is served by IIS, and is the name of the 94 93 # user after having been mapped to a local account. 95 94 # Both IIS and Apache set REMOTE_USER, when possible. 96 request.login = env('LOGON_USER') or env('REMOTE_USER') or None95 login = env('LOGON_USER') or env('REMOTE_USER') or None 97 96 98 request.multithread = environ['wsgi.multithread'] 99 request.multiprocess = environ['wsgi.multiprocess'] 100 request.wsgi_environ = environ 97 meth = environ['REQUEST_METHOD'] 98 path = env('SCRIPT_NAME', '') + env('PATH_INFO', '') 99 qs = env('QUERY_STRING', '') 100 rproto = env('SERVER_PROTOCOL') 101 headers = list(translate_headers(environ)) 102 rfile = environ['wsgi.input'] 101 103 102 request.app = app 104 # Loop to allow for InternalRedirect. 105 prev = None 106 redirections = [] 107 while True: 108 try: 109 request = cherrypy.engine.request(local, remote, scheme, sproto) 110 request.login = login 111 request.multithread = environ['wsgi.multithread'] 112 request.multiprocess = environ['wsgi.multiprocess'] 113 request.wsgi_environ = environ 114 request.app = app 115 if prev: 116 request.prev = prev 117 response = request.run(meth, path, qs, rproto, headers, rfile) 118 break 119 except cherrypy.InternalRedirect, ir: 120 request.close() 121 prev = request 122 123 if (ir.path in redirections and not self.recursive_redirect): 124 raise RuntimeError("InternalRedirect visited the " 125 "same URL twice: %s" % repr(ir.path)) 126 else: 127 # Add the *previous* path_info + qs to redirections. 128 if qs: 129 qs = "?" + qs 130 redirections.append(path + qs) 131 132 # Munge arguments to request.run and try again. 133 meth = "GET" 134 path = ir.path 135 qs = ir.query_string 136 rfile = StringIO.StringIO() 103 137 104 path = env('SCRIPT_NAME', '') + env('PATH_INFO', '')105 response = request.run(environ['REQUEST_METHOD'], path,106 env('QUERY_STRING', ''),107 env('SERVER_PROTOCOL'),108 translate_headers(environ),109 environ['wsgi.input'])110 138 s, h, b = response.status, response.header_list, response.body 111 139 exc = None 112 140 except (KeyboardInterrupt, SystemExit), ex: -
lib/cptools.py
old new 272 272 for k in dir(SessionAuth) if not k.startswith("__")]) 273 273 274 274 275 def virtual_host(use_x_forwarded_host=True, **domains):276 """Redirect internally based on the Host header.277 278 Useful when running multiple sites within one CP server.279 280 From http://groups.google.com/group/cherrypy-users/browse_thread/thread/f393540fe278e54d:281 282 For various reasons I need several domains to point to different parts of a283 single website structure as well as to their own "homepage" EG284 285 http://www.mydom1.com -> root286 http://www.mydom2.com -> root/mydom2/287 http://www.mydom3.com -> root/mydom3/288 http://www.mydom4.com -> under construction page289 290 but also to have http://www.mydom1.com/mydom2/ etc to be valid pages in291 their own right.292 """293 request = cherrypy.request294 295 # Guard against running twice.296 if hasattr(request, "virtual_prefix"):297 return298 299 domain = request.headers.get('Host', '')300 if use_x_forwarded_host:301 domain = request.headers.get("X-Forwarded-Host", domain)302 303 request.virtual_prefix = prefix = domains.get(domain, "")304 if prefix:305 raise cherrypy.InternalRedirect(_http.urljoin(prefix, request.path_info))306 307 275 def log_traceback(): 308 276 """Write the last error's traceback to the cherrypy error log.""" 309 277 from cherrypy import _cperror -
test/test_core.py
old new 164 164 raise cherrypy.InternalRedirect("cousin?t=6") 165 165 166 166 def cousin(self, t): 167 return repr(cherrypy.request.redirections + 168 [cherrypy.url(qs=cherrypy.request.query_string)]) 167 assert cherrypy.request.prev.closed 168 return cherrypy.request.prev.query_string 169 169 170 170 def petshop(self, user_id): 171 171 if user_id == "parrot": … … 173 173 raise cherrypy.InternalRedirect('/image/getImagesByUser?user_id=slug') 174 174 elif user_id == "terrier": 175 175 # Trade it for a fish when redirecting 176 cherrypy.request.params = {"user_id": "fish"} 177 raise cherrypy.InternalRedirect('/image/getImagesByUser') 176 raise cherrypy.InternalRedirect('/image/getImagesByUser?user_id=fish') 178 177 else: 179 178 # This should pass the user_id through to getImagesByUser 180 raise cherrypy.InternalRedirect('/image/getImagesByUser ')179 raise cherrypy.InternalRedirect('/image/getImagesByUser?user_id=%s' % user_id) 181 180 182 181 # We support Python 2.3, but the @-deco syntax would look like this: 183 182 # @tools.login_redir() … … 606 605 self.assertStatus(200) 607 606 608 607 # Relative path in InternalRedirect. 609 # Also tests request.redirections 608 # Also tests request.prev. 610 609 self.getPage("/internalredirect/relative?a=3&b=5") 611 self.assertBody("['/internalredirect/relative?a=3&b=5', " 612 "'%s/internalredirect/cousin?t=6']" % self.base()) 610 self.assertBody("a=3&b=5") 613 611 self.assertStatus(200) 614 612 615 613 # InternalRedirect on error -
test/test_virtualhost.py
old new 3 3 test.prefer_parent_path() 4 4 5 5 import cherrypy 6 from cherrypy import _cpdispatch 6 7 7 8 def setup_server(): 8 9 class Root: … … 38 39 root = Root() 39 40 root.mydom2 = VHost("Domain 2") 40 41 root.mydom3 = VHost("Domain 3") 41 cherrypy.tree.mount(root) 42 cherrypy.tree.mount(root, config={'/': { 43 'request.dispatch': _cpdispatch.VirtualHost( 44 **{'www.mydom2.com': '/mydom2', 45 'www.mydom3.com': '/mydom3', 46 'www.mydom4.com': '/dom4', 47 }), 48 }}) 42 49 43 cherrypy.config.update({ 44 'environment': 'test_suite', 45 'tools.virtual_host.on': True, 46 'tools.virtual_host.www.mydom2.com': '/mydom2', 47 'tools.virtual_host.www.mydom3.com': '/mydom3', 48 'tools.virtual_host.www.mydom4.com': '/dom4', 49 }) 50 cherrypy.config.update({'environment': 'test_suite'}) 50 51 51 52 from cherrypy.test import helper 52 53 … … 76 77 self.getPage("/vmethod/pos", [('Host', 'www.mydom3.com')]) 77 78 self.assertBody("You sent 'pos'") 78 79 80 # Test that cherrypy.url uses the browser url, not the virtual url 79 81 self.getPage("/url", [('Host', 'www.mydom2.com')]) 80 82 self.assertBody("http://www.mydom2.com/nextpage") 81 83 -
test/test_xmlrpc.py
old new 2 2 test.prefer_parent_path() 3 3 import xmlrpclib 4 4 5 from cherrypy import _cpdispatch 5 6 7 6 8 def setup_server(): 7 9 import cherrypy 8 10 from cherrypy import _cptools … … 57 59 58 60 root = Root() 59 61 root.xmlrpc = XmlRpc() 60 cherrypy.tree.mount(root) 62 cherrypy.tree.mount(root, config={'/': { 63 'request.dispatch': _cpdispatch.XMLRPCDispatcher(), 64 }}) 61 65 cherrypy.config.update({'environment': 'test_suite'}) 62 66 63 67

