Commits

Christopher Grebs committed 648b79b

added 'allow_subdomain_redirects' option to werkzeug.test.Client. This allows the test client to redirect to subdomains on the same domain (usefull for multi-subdomain applications), but still not to external targets. This is an optional feature so we assume the user knows that all his subdomains (at least those get tested) are handled by his application.

Comments (0)

Files changed (2)

tests/test_test.py

 
 
 def external_redirect_demo_app(environ, start_response):
-    response = redirect('http://example.org/')
+    response = redirect('http://example.com/')
+    return response(environ, start_response)
+
+
+def external_subdomain_redirect_demo_app(environ, start_response):
+    if 'test.example.com' in environ['HTTP_HOST']:
+        response = Response('redirected successfully to subdomain')
+    else:
+        response = redirect('http://test.example.com/login')
     return response(environ, start_response)
 
 
 def test_follow_external_redirect():
     env = create_environ('/', base_url='http://localhost')
     c = Client(external_redirect_demo_app)
-    assert_raises(RuntimeError, lambda: c.open(environ_overrides=env, follow_redirects=True))
+    assert_raises(RuntimeError, lambda: c.get(environ_overrides=env, follow_redirects=True))
+
+
+def test_follow_external_redirect_on_same_subdomain():
+    env = create_environ('/', base_url='http://example.com')
+    c = Client(external_subdomain_redirect_demo_app, allow_subdomain_redirects=True)
+    c.get(environ_overrides=env, follow_redirects=True)
+
+    # check that this does not work for real external domains
+    env = create_environ('/', base_url='http://localhost')
+    assert_raises(RuntimeError, lambda: c.get(environ_overrides=env, follow_redirects=True))
+
+    # check that subdomain redirects fail if no `allow_subdomain_redirects` is applied
+    c = Client(external_subdomain_redirect_demo_app)
+    assert_raises(RuntimeError, lambda: c.get(environ_overrides=env, follow_redirects=True))
 
 
 @raises(ClientRedirectError)
     sent for subsequent requests. This is True by default, but passing False
     will disable this behaviour.
 
+    If you want to request some subdomain of your application you may set
+    `allow_subdomain_redirects` to `True` as if not no external redirects
+    are allowed.
+
     .. versionadded:: 0.5
        `use_cookies` is new in this version.  Older versions did not provide
        builtin cookie support.
     """
 
-    def __init__(self, application, response_wrapper=None, use_cookies=True):
+    def __init__(self, application, response_wrapper=None, use_cookies=True,
+                 allow_subdomain_redirects=False):
         self.application = application
         if response_wrapper is None:
             response_wrapper = lambda a, s, h: (a, s, h)
         else:
             self.cookie_jar = None
         self.redirect_client = None
+        self.allow_subdomain_redirects = allow_subdomain_redirects
 
     def open(self, *args, **kwargs):
         """Takes the same arguments as the :class:`EnvironBuilder` class with
                 self.redirect_client.cookie_jar = self.cookie_jar
 
             redirect = dict(rv[2])['Location']
+
             scheme, netloc, script_root, qs, anchor = urlparse.urlsplit(redirect)
             base_url = urlparse.urlunsplit((scheme, netloc, '', '', '')).rstrip('/') + '/'
-            host = get_host(create_environ('/', base_url, query_string=qs)).split(':', 1)[0]
-            if get_host(environ).split(':', 1)[0] != host:
+
+            cur_server_name = netloc.split(':', 1)[0].split('.')
+            real_server_name = get_host(environ).split(':', 1)[0].split('.')
+
+            if self.allow_subdomain_redirects:
+                allowed = cur_server_name[-len(real_server_name):] == real_server_name
+            else:
+                allowed = cur_server_name == real_server_name
+
+            if not allowed:
                 raise RuntimeError('%r does not support redirect to '
                                    'external targets' % self.__class__)
 
 
             # the redirect request should be a new request, and not be based on
             # the old request
+
             redirect_kwargs = {
                 'path':             script_root,
                 'base_url':         base_url,