summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-04-18 00:52:27 -0400
committerChris McDonough <chrism@plope.com>2011-04-18 00:52:27 -0400
commit29a850643aea9452c4f09906505812cb03d7ef5c (patch)
tree74699691b2c21c6e19b58bd0cf4280dc37079596
parent015296cc9c764f63c7b41427ec2538b5e0f861e0 (diff)
downloadpyramid-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.txt10
-rw-r--r--pyramid/static.py50
-rw-r--r--pyramid/tests/test_integration.py59
-rw-r--r--pyramid/tests/test_static.py47
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