diff options
| author | Chris McDonough <chrism@plope.com> | 2011-04-18 00:52:27 -0400 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2011-04-18 00:52:27 -0400 |
| commit | 29a850643aea9452c4f09906505812cb03d7ef5c (patch) | |
| tree | 74699691b2c21c6e19b58bd0cf4280dc37079596 | |
| parent | 015296cc9c764f63c7b41427ec2538b5e0f861e0 (diff) | |
| download | pyramid-29a850643aea9452c4f09906505812cb03d7ef5c.tar.gz pyramid-29a850643aea9452c4f09906505812cb03d7ef5c.tar.bz2 pyramid-29a850643aea9452c4f09906505812cb03d7ef5c.zip | |
- When visiting a URL that represented a static view which resolved to a
subdirectory, the ``index.html`` of that subdirectory would not be served
properly. Instead, a redirect to ``/subdir`` would be issued. This has
been fixed, and now visiting a subdirectory that contains an ``index.html``
within a static view returns the index.html properly. See also
https://github.com/Pylons/pyramid/issues/67.
- Redirects issued by a static view did not take into account any existing
``SCRIPT_NAME`` (such as one set by a url mapping composite). Now they do.
Closes #67.
| -rw-r--r-- | CHANGES.txt | 10 | ||||
| -rw-r--r-- | pyramid/static.py | 50 | ||||
| -rw-r--r-- | pyramid/tests/test_integration.py | 59 | ||||
| -rw-r--r-- | pyramid/tests/test_static.py | 47 |
4 files changed, 155 insertions, 11 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 0bd19572a..3ae834d93 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -104,6 +104,16 @@ Bug Fixes DummyRequest instead of eagerly assigning an attribute. See also https://github.com/Pylons/pyramid/issues/165 +- When visiting a URL that represented a static view which resolved to a + subdirectory, the ``index.html`` of that subdirectory would not be served + properly. Instead, a redirect to ``/subdir`` would be issued. This has + been fixed, and now visiting a subdirectory that contains an ``index.html`` + within a static view returns the index.html properly. See also + https://github.com/Pylons/pyramid/issues/67. + +- Redirects issued by a static view did not take into account any existing + ``SCRIPT_NAME`` (such as one set by a url mapping composite). Now they do. + 1.0 (2011-01-30) ================ diff --git a/pyramid/static.py b/pyramid/static.py index 80981f0b8..223652768 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -151,8 +151,8 @@ class StaticURLInfo(object): extra['view_permission'] = permission extra['view'] = view - # register a route using the computed view, permission, and pattern, - # plus any extras passed to us via add_static_view + # register a route using the computed view, permission, and + # pattern, plus any extras passed to us via add_static_view pattern = "%s*subpath" % name # name already ends with slash self.config.add_route(name, pattern, **extra) self.registrations.append((name, spec, False)) @@ -208,12 +208,44 @@ class static_view(object): self.app = app def __call__(self, context, request): - subpath = '/'.join(request.subpath) + # Point PATH_INFO to the static file/dir path; point SCRIPT_NAME + # to the prefix before it. + + # Postconditions: + # - SCRIPT_NAME and PATH_INFO are empty or start with / + # - At least one of SCRIPT_NAME or PATH_INFO are set. + # - SCRIPT_NAME is not '/' (it should be '', and PATH_INFO should + # be '/'). + request_copy = request.copy() - # Fix up PATH_INFO to get rid of everything but the "subpath" - # (the actual path to the file relative to the root dir). - request_copy.environ['PATH_INFO'] = '/' + subpath - # Zero out SCRIPT_NAME for good measure. - request_copy.environ['SCRIPT_NAME'] = '' - return request_copy.get_response(self.app) + script_name = request_copy.environ.get('SCRIPT_NAME', '') + path_info = request_copy.environ.get('PATH_INFO', '/') + + new_script_name = script_name + new_path_info = path_info + + subpath = list(request.subpath) + + if subpath: + # compute new_path_info + new_path_info = '/' + '/'.join(subpath) + if path_info.endswith('/'): + # readd trailing slash stripped by subpath (traversal) + # conversion + new_path_info += '/' + + # compute new_script_name + tmp = [] + workback = (script_name + path_info).split('/') + while workback: + el = workback.pop() + if el: + tmp.insert(0, el) + if tmp == subpath: + new_script_name = '/'.join(workback) + break + + request_copy.environ['SCRIPT_NAME'] = new_script_name + request_copy.environ['PATH_INFO'] = new_path_info + return request_copy.get_response(self.app) diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index dc7525080..095f22f41 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -45,7 +45,7 @@ here = os.path.dirname(__file__) staticapp = static(os.path.join(here, 'fixtures')) class TestStaticApp(unittest.TestCase): - def test_it(self): + def test_basic(self): from webob import Request context = DummyContext() from StringIO import StringIO @@ -57,13 +57,68 @@ class TestStaticApp(unittest.TestCase): 'wsgi.version':(1,0), 'wsgi.url_scheme':'http', 'wsgi.input':StringIO()}) - request.subpath = ['minimal.pt'] + request.subpath = ('minimal.pt',) result = staticapp(context, request) self.assertEqual(result.status, '200 OK') self.assertEqual( result.body.replace('\r', ''), open(os.path.join(here, 'fixtures/minimal.pt'), 'r').read()) + def test_file_in_subdir(self): + from webob import Request + context = DummyContext() + from StringIO import StringIO + request = Request({'PATH_INFO':'', + 'SCRIPT_NAME':'', + 'SERVER_NAME':'localhost', + 'SERVER_PORT':'80', + 'REQUEST_METHOD':'GET', + 'wsgi.version':(1,0), + 'wsgi.url_scheme':'http', + 'wsgi.input':StringIO()}) + request.subpath = ('static', 'index.html',) + result = staticapp(context, request) + self.assertEqual(result.status, '200 OK') + self.assertEqual( + result.body.replace('\r', ''), + open(os.path.join(here, 'fixtures/static/index.html'), 'r').read()) + + def test_redirect_to_subdir(self): + from webob import Request + context = DummyContext() + from StringIO import StringIO + request = Request({'PATH_INFO':'', + 'SCRIPT_NAME':'', + 'SERVER_NAME':'localhost', + 'SERVER_PORT':'80', + 'REQUEST_METHOD':'GET', + 'wsgi.version':(1,0), + 'wsgi.url_scheme':'http', + 'wsgi.input':StringIO()}) + request.subpath = ('static',) + result = staticapp(context, request) + self.assertEqual(result.status, '301 Moved Permanently') + self.assertEqual(result.location, 'http://localhost/static/') + + def test_redirect_to_subdir_with_existing_script_name(self): + from webob import Request + context = DummyContext() + from StringIO import StringIO + request = Request({'PATH_INFO':'', + 'SCRIPT_NAME':'/script_name', + 'SERVER_NAME':'localhost', + 'SERVER_PORT':'80', + 'REQUEST_METHOD':'GET', + 'wsgi.version':(1,0), + 'wsgi.url_scheme':'http', + 'wsgi.input':StringIO()}) + request.subpath = ['static'] + result = staticapp(context, request) + self.assertEqual(result.status, '301 Moved Permanently') + self.assertEqual(result.location, + 'http://localhost/script_name/static/') + + class IntegrationBase(unittest.TestCase): root_factory = None package = None diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py index cd3006689..9c4c4a1c8 100644 --- a/pyramid/tests/test_static.py +++ b/pyramid/tests/test_static.py @@ -259,6 +259,53 @@ class Test_static_view(unittest.TestCase): self.assertEqual(response.package_name, 'another') self.assertEqual(response.cache_max_age, 3600) + def test_no_subpath_preserves_path_info_and_script_name(self): + view = self._makeOne('fixtures', package_name='another') + context = DummyContext() + request = DummyRequest() + request.subpath = () + request.environ = self._makeEnviron(PATH_INFO='/path_info', + SCRIPT_NAME='/script_name') + view(context, request) + self.assertEqual(request.copied, True) + self.assertEqual(request.environ['PATH_INFO'], '/path_info') + self.assertEqual(request.environ['SCRIPT_NAME'], '/script_name') + + def test_with_subpath_path_info_ends_with_slash(self): + view = self._makeOne('fixtures', package_name='another') + context = DummyContext() + request = DummyRequest() + request.subpath = ('subpath',) + request.environ = self._makeEnviron(PATH_INFO='/path_info/subpath/') + view(context, request) + self.assertEqual(request.copied, True) + self.assertEqual(request.environ['PATH_INFO'], '/subpath/') + self.assertEqual(request.environ['SCRIPT_NAME'], '/path_info') + + def test_with_subpath_original_script_name_preserved(self): + view = self._makeOne('fixtures', package_name='another') + context = DummyContext() + request = DummyRequest() + request.subpath = ('subpath',) + request.environ = self._makeEnviron(PATH_INFO='/path_info/subpath/', + SCRIPT_NAME='/scriptname') + view(context, request) + self.assertEqual(request.copied, True) + self.assertEqual(request.environ['PATH_INFO'], '/subpath/') + self.assertEqual(request.environ['SCRIPT_NAME'], + '/scriptname/path_info') + + def test_with_subpath_new_script_name_fixes_trailing_double_slashes(self): + view = self._makeOne('fixtures', package_name='another') + context = DummyContext() + request = DummyRequest() + request.subpath = ('sub', 'path') + request.environ = self._makeEnviron(PATH_INFO='/path_info//sub//path//') + view(context, request) + self.assertEqual(request.copied, True) + self.assertEqual(request.environ['PATH_INFO'], '/sub/path/') + self.assertEqual(request.environ['SCRIPT_NAME'], '/path_info/') + class TestStaticURLInfo(unittest.TestCase): def _getTargetClass(self): from pyramid.static import StaticURLInfo |
