1. Armin Ronacher
  2. werkzeug-main

Commits

mitsuhiko  committed d52f978 Merge

added :attr:`follow_redirect` to the :func:`open` of :class:`Client`.
added support for `extra_files` in
:func:`~werkzeug.script.make_runserver`

  • Participants
  • Parent commits 65ff4c4, ca26cf9
  • Branches default

Comments (0)

Files changed (6)

File AUTHORS

View file
 - Christoph Rauch
 - Clemens Hermann
 - Jason Kirtland
+- Ali Afshar
+- Christopher Grebs <cg@webshox.org>
 
 Contributors of code for werkzeug/examples are:
 

File CHANGES

View file
 - :class:`MultiDict` copies now instead of revealing internal
   lists to the caller for `getlist` and iteration functions that
   return lists.
+- added :attr:`follow_redirect` to the :func:`open` of :class:`Client`.
+- added support for `extra_files` in
+  :func:`~werkzeug.script.make_runserver`
 
 Version 0.4.1
 -------------

File tests/test_test.py

View file
 
 import sys
 from cStringIO import StringIO
+from nose.tools import assert_raises
 from werkzeug.wrappers import Request, Response
 from werkzeug.test import Client, EnvironBuilder, create_environ
+from werkzeug.utils import redirect, get_host
+from werkzeug.datastructures import Headers
+
 
 
 def cookie_app(environ, start_response):
     return response(environ, start_response)
 
 
+def redirect_demo_app(environ, start_response):
+    response = redirect('http://localhost/some/redirect/')
+    return response(environ, start_response)
+
+
+def external_redirect_demo_app(environ, start_response):
+    response = redirect('http://example.org/')
+    return response(environ, start_response)
+
+
 def test_set_cookie_app():
     """Test that a server cookie is set and stored in the client"""
     c = Client(cookie_app)
     builder.files.add_file('blah', SpecialInput())
     builder.close()
     assert len(closed) == 2
+
+
+def test_follow_redirect():
+    env = create_environ('/', base_url='http://localhost')
+    c = Client(redirect_demo_app)
+    headers = Headers(c.open(environ_overrides=env, follow_redirects=True)[2])
+    assert headers['Location'] == 'http://localhost/some/redirect/'
+
+
+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))

File werkzeug/http.py

View file
 """
 import re
 import inspect
-from email.utils import parsedate_tz, mktime_tz
+try:
+    from email.utils import parsedate_tz, mktime_tz
+except ImportError:
+    from email.Utils import parsedate_tz, mktime_tz
 from cStringIO import StringIO
 from tempfile import TemporaryFile
 from urllib2 import parse_http_list as _parse_list_header

File werkzeug/script.py

View file
 
 def make_runserver(app_factory, hostname='localhost', port=5000,
                    use_reloader=False, use_debugger=False, use_evalex=True,
-                   threaded=False, processes=1, static_files=None):
+                   threaded=False, processes=1, static_files=None,
+                   extra_files=None):
     """Returns an action callback that spawns a new development server.
 
     .. versionadded:: 0.5
-       `static_files` was added.
+       `static_files` and `extra_files` was added.
 
     :param app_factory: a function that returns a new WSGI application.
     :param hostname: the default hostname the server should listen on.
     :param threaded: the default threading setting.
     :param processes: the default number of processes to start.
     :param static_files: optionally a dict of static files.
+    :param extra_files: optionally a list of extra files to track for reloading.
     """
     def action(hostname=('h', hostname), port=('p', port),
                reloader=use_reloader, debugger=use_debugger,
         from werkzeug.serving import run_simple
         app = app_factory()
         run_simple(hostname, port, app, reloader, debugger, evalex,
-                   None, 1, threaded, processes, static_files)
+                   extra_files, 1, threaded, processes, static_files)
     return action

File werkzeug/test.py

View file
 from werkzeug._internal import _empty_stream
 from werkzeug.wrappers import BaseRequest
 from werkzeug.utils import create_environ, run_wsgi_app, get_current_url, \
-     url_encode, url_decode, FileStorage
+     url_encode, url_decode, FileStorage, get_host
 from werkzeug.datastructures import FileMultiDict, MultiDict, \
      CombinedMultiDict, Headers
 
            `mimetype`.  This change was made for consistency with
            :class:`werkzeug.FileWrapper`.
 
+            The `follow_redirects` parameter was added to :func:`open`.
+
         Additional parameters:
 
         :param as_tuple: Returns a tuple in the form ``(environ, result)``
-        :param buffered: set this to true to buffer the application run.
+        :param buffered: Set this to true to buffer the application run.
                          This will automatically close the application for
                          you as well.
+        :param follow_redirects: Set this to True if the `Client` should
+                                 follow HTTP redirects.
         """
         as_tuple = kwargs.pop('as_tuple', False)
         buffered = kwargs.pop('buffered', False)
+        follow_redirects = kwargs.pop('follow_redirects', False)
         environ = None
         if not kwargs and len(args) == 1:
             if isinstance(args[0], EnvironBuilder):
         rv = run_wsgi_app(self.application, environ, buffered=buffered)
         if self.cookie_jar is not None:
             self.cookie_jar.extract_wsgi(environ, rv[2])
+
+        # handle redirects
+        redirect_chain = []
+        status_code = int(rv[1].split(None, 1)[0])
+        while status_code in (301, 302, 303, 305, 307) and follow_redirects:
+            redirect = dict(rv[2])['Location']
+            host = get_host(create_environ('/', redirect))
+            if get_host(environ).split(':', 1)[0] != host:
+                raise RuntimeError('%r does not support redirect to '
+                                   'external targets' % self.__class__)
+
+            scheme, netloc, script_root, qs, anchor = urlparse.urlsplit(redirect)
+            redirect_chain.append((redirect, status_code))
+
+            kwargs.update({
+                'base_url':         urlparse.urlunsplit((scheme, host,
+                                    script_root, '', '')).rstrip('/') + '/',
+                'query_string':     qs,
+                'as_tuple':         as_tuple,
+                'buffered':         buffered,
+                'follow_redirects': False
+            })
+            rv = self.open(*args, **kwargs)
+            status_code = int(rv[1].split(None, 1)[0])
+
+            # Prevent loops
+            if redirect_chain[-1] in redirect_chain[0:-1]:
+                break
+
         response = self.response_wrapper(*rv)
         if as_tuple:
             return environ, response