diff --git a/VERSION b/VERSION index 177f328c..433e4326 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 1.99.3 (2011-11-30 22:09:17) dev +Version 1.99.3 (2011-11-30 22:23:02) dev diff --git a/gluon/rewrite.py b/gluon/rewrite.py index e750f20d..5f7a7fad 100644 --- a/gluon/rewrite.py +++ b/gluon/rewrite.py @@ -69,6 +69,7 @@ def _params_default(app=None): p.error_message_ticket = \ '

Internal error

Ticket issued: %(ticket)s' p.routers = None + p.logging = 'off' return p params_apps = dict() @@ -76,6 +77,25 @@ params = _params_default(app=None) # regex rewrite parameters thread.routes = params # default to base regex rewrite parameters routers = None +def log_rewrite(string): + "Log rewrite activity under control of routes.py" + if params.logging == 'debug': # catch common cases first + logger.debug(string) + elif params.logging == 'off' or not params.logging: + pass + elif params.logging == 'print': + print string + elif params.logging == 'info': + logger.info(string) + elif params.logging == 'warning': + logger.warning(string) + elif params.logging == 'error': + logger.error(string) + elif params.logging == 'critical': + logger.critical(string) + else: + logger.debug(string) + ROUTER_KEYS = set(('default_application', 'applications', 'default_controller', 'controllers', 'default_function', 'functions', 'default_language', 'languages', 'domain', 'domains', 'root_static', 'path_prefix', @@ -254,7 +274,8 @@ def load(routes='routes.py', app=None, data=None, rdict=None): p[sym].append(compile_regex(k, v)) for sym in ('routes_onerror', 'routes_apps_raw', 'error_handler','error_message', 'error_message_ticket', - 'default_application','default_controller', 'default_function'): + 'default_application','default_controller', 'default_function', + 'logging'): if sym in symbols: p[sym] = symbols[sym] if 'routers' in symbols: @@ -307,7 +328,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None): if app in p.routers: routers[app].update(p.routers[app]) - logger.debug('URL rewrite is on. configuration in %s' % path) + log_rewrite('URL rewrite is on. configuration in %s' % path) regex_at = re.compile(r'(? %s' % (tag, key, value, rewritten)) + log_rewrite('%s: [%s] [%s] -> %s' % (tag, key, value, rewritten)) return rewritten - logger.debug('%s: [%s] -> %s (not rewritten)' % (tag, key, default)) + log_rewrite('%s: [%s] -> %s (not rewritten)' % (tag, key, default)) return default def regex_select(env=None, app=None, request=None): @@ -479,7 +500,7 @@ def regex_select(env=None, app=None, request=None): thread.routes = params_apps.get(app, params) else: thread.routes = params # default to base rewrite parameters - logger.debug("select routing parameters: %s" % thread.routes.name) + log_rewrite("select routing parameters: %s" % thread.routes.name) return app # for doctest def regex_filter_in(e): @@ -643,9 +664,9 @@ def regex_filter_out(url, e=None): for (regex, value) in thread.routes.routes_out: if regex.match(items[0]): rewritten = '?'.join([regex.sub(value, items[0])] + items[1:]) - logger.debug('routes_out: [%s] -> %s' % (url, rewritten)) + log_rewrite('routes_out: [%s] -> %s' % (url, rewritten)) return rewritten - logger.debug('routes_out: [%s] not rewritten' % url) + log_rewrite('routes_out: [%s] not rewritten' % url) return url @@ -660,6 +681,8 @@ def filter_url(url, method='get', remote='0.0.0.0', out=False, app=False, lang=N k = uri.find('?') if k < 0: k = len(uri) + if isinstance(domain, str): + domain = (domain, None) (path_info, query_string) = (uri[:k], uri[k+1:]) path_info = urllib.unquote(path_info) # simulate server e = { @@ -855,7 +878,7 @@ class MapUrlIn(object): # set the application router # - logger.debug("select application=%s" % self.application) + log_rewrite("select application=%s" % self.application) self.request.application = self.application if self.application not in routers: self.router = routers.BASE # support gluon.main.wsgibase init->welcome @@ -884,7 +907,7 @@ class MapUrlIn(object): root_static_file = os.path.join(self.request.env.applications_parent, 'applications', self.application, self.controller, self.arg0) - logger.debug("route: root static=%s" % root_static_file) + log_rewrite("route: root static=%s" % root_static_file) return root_static_file return None @@ -896,7 +919,7 @@ class MapUrlIn(object): else: self.language = self.default_language if self.language: - logger.debug("route: language=%s" % self.language) + log_rewrite("route: language=%s" % self.language) self.pop_arg_if(self.language == arg0) arg0 = self.arg0 @@ -910,7 +933,7 @@ class MapUrlIn(object): else: self.controller = arg0 self.pop_arg_if(arg0 == self.controller) - logger.debug("route: controller=%s" % self.controller) + log_rewrite("route: controller=%s" % self.controller) if not self.router._acfe_match.match(self.controller): raise HTTP(400, thread.routes.error_message % 'invalid request', web2py_error='invalid controller') @@ -939,7 +962,7 @@ class MapUrlIn(object): static_file = os.path.join(self.request.env.applications_parent, 'applications', self.application, 'static', file) - logger.debug("route: static=%s" % static_file) + log_rewrite("route: static=%s" % static_file) return static_file def map_function(self): @@ -962,7 +985,7 @@ class MapUrlIn(object): else: self.function = arg0 self.pop_arg_if(True) - logger.debug("route: function.ext=%s.%s" % (self.function, self.extension)) + log_rewrite("route: function.ext=%s.%s" % (self.function, self.extension)) if not self.router._acfe_match.match(self.function): raise HTTP(400, thread.routes.error_message % 'invalid request', @@ -1117,22 +1140,26 @@ class MapUrlOut(object): # # because we presume the lang string to be unambiguous, its presence protects application omission # + if self.exclusive_domain: + applications = [self.domain_application] + else: + applications = self.applications if self.omit_language: - if not self.applications or self.controller in self.applications: + if not applications or self.controller in applications: self.omit_application = False if self.omit_application: - if not self.applications or self.function in self.applications: + if not applications or self.function in applications: self.omit_controller = False if not self.controllers or self.function in self.controllers: self.omit_controller = False if self.args: - if self.args[0] in self.functions or self.args[0] in self.controllers or self.args[0] in self.applications: + if self.args[0] in self.functions or self.args[0] in self.controllers or self.args[0] in applications: self.omit_function = False if self.omit_controller: - if self.function in self.controllers or self.function in self.applications: + if self.function in self.controllers or self.function in applications: self.omit_controller = False if self.omit_application: - if self.controller in self.applications: + if self.controller in applications: self.omit_application = False # handle static as a special case diff --git a/gluon/tests/test_router.py b/gluon/tests/test_router.py index c6f377fb..9d8933df 100644 --- a/gluon/tests/test_router.py +++ b/gluon/tests/test_router.py @@ -30,12 +30,14 @@ def setUpModule(): "build a temporary applications tree" # applications/ os.mkdir(abspath('applications')) + # applications/app/ for app in ('admin', 'examples', 'welcome'): os.mkdir(abspath('applications', app)) # applications/app/(controllers, static) for subdir in ('controllers', 'static'): os.mkdir(abspath('applications', app, subdir)) + # applications/admin/controllers/*.py for ctr in ('appadmin', 'default', 'gae', 'mercurial', 'shell', 'wizard'): open(abspath('applications', 'admin', 'controllers', '%s.py' % ctr), 'w').close() @@ -855,6 +857,7 @@ class TestRouter(unittest.TestCase): Test REQUEST_URI in env ''' load(rdict=dict()) + self.assertEqual(filter_url('http://domain.com/abc', env=True).request_uri, '/init/default/abc') self.assertEqual(filter_url('http://domain.com/abc?def', env=True).request_uri, @@ -866,6 +869,49 @@ class TestRouter(unittest.TestCase): self.assertEqual(filter_url('http://domain.com/index/a%20bc', env=True).request_uri, "/init/default/index/a%20bc") + def test_request_collide(self): + ''' + Test controller-app name collision: admin vs welcome/admin + ''' + router_collide = dict( + BASE = dict( + domains = { + 'ex.domain.com' : 'examples', + 'ad.domain.com' : 'admin', + 'welcome.com' : 'welcome', + 'www.welcome.com' : 'welcome', + }, + exclusive_domain=True, + ), + ) + load(rdict=router_collide) + + # basic inbound + self.assertEqual(filter_url('http://ex.domain.com'), '/examples/default/exdef') + self.assertEqual(filter_url('http://ad.domain.com'), '/admin/default/index') + self.assertEqual(filter_url('http://welcome.com'), '/welcome/default/index') + self.assertEqual(filter_url('http://www.welcome.com'), '/welcome/default/index') + # basic outbound + self.assertEqual(filter_url('http://ex.domain.com/examples/default/exdef', domain='examples', out=True), "/") + self.assertEqual(filter_url('http://ad.domain.com/admin/default/index', domain='admin', out=True), "/") + self.assertEqual(filter_url('http://welcome.com/welcome/default/index', domain='welcome', out=True), "/") + self.assertEqual(filter_url('http://www.welcome.com/welcome/default/index', domain='welcome', out=True), "/") + + # inbound + self.assertEqual(filter_url('http://welcome.com/admin'), '/welcome/admin/index') + self.assertEqual(filter_url('http://welcome.com/f1'), '/welcome/default/f1') + self.assertEqual(filter_url('http://ad.domain.com/shell'), '/admin/shell/index') + self.assertEqual(filter_url('http://ad.domain.com/f1'), '/admin/default/f1') + # outbound + self.assertEqual(filter_url('http://welcome.com/welcome/other/index', domain='welcome', out=True), "/other") + self.assertEqual(filter_url('http://welcome.com/welcome/admin/index', domain='welcome', out=True), "/admin") + self.assertEqual(filter_url('http://ad.domain.com/admin/shell/index', domain='admin', out=True), "/shell") + self.assertEqual(filter_url('http://ad.domain.com/admin/default/f1', domain='admin', out=True), "/f1") + router_collide['BASE']['exclusive_domain'] = False + load(rdict=router_collide) + self.assertEqual(filter_url('http://welcome.com/welcome/admin/index', domain='welcome', out=True), "/welcome/admin") + + if __name__ == '__main__': setUpModule() # pre-2.7 unittest.main() diff --git a/router.example.py b/router.example.py index 3c798c94..8c5a1589 100644 --- a/router.example.py +++ b/router.example.py @@ -103,6 +103,12 @@ routers = dict( ), ) +# Specify log level for rewrite's debug logging +# Possible values: debug, info, warning, error, critical (loglevels), +# off, print (print uses print statement rather than logging) +# GAE users may want to use 'off' to suppress routine logging. +# +logging = 'debug' # Error-handling redirects all HTTP errors (status codes >= 400) to a specified # path. If you wish to use error-handling redirects, uncomment the tuple diff --git a/routes.example.py b/routes.example.py index 251fcc6e..f2c06c52 100644 --- a/routes.example.py +++ b/routes.example.py @@ -42,6 +42,13 @@ routes_in = ((r'.*:/favicon.ico', r'/examples/static/favicon.ico'), routes_out = ((r'.*http://otherdomain.com.* /app/ctr(?P.*)', r'\g'), (r'/app(?P.*)', r'\g')) +# Specify log level for rewrite's debug logging +# Possible values: debug, info, warning, error, critical (loglevels), +# off, print (print uses print statement rather than logging) +# GAE users may want to use 'off' to suppress routine logging. +# +logging = 'debug' + # Error-handling redirects all HTTP errors (status codes >= 400) to a specified # path. If you wish to use error-handling redirects, uncomment the tuple # below. You can customize responses by adding a tuple entry with the first