diff --git a/mopidy/http/actor.py b/mopidy/http/actor.py index eee09ffe..3ab0a480 100644 --- a/mopidy/http/actor.py +++ b/mopidy/http/actor.py @@ -84,6 +84,10 @@ class HttpFrontend(pykka.ThreadingActor, CoreListener): def _get_app_request_handlers(self): result = [] for app in self.apps: + result.append(( + r'/%s' % app['name'], + handlers.AddSlashHandler + )) request_handlers = app['factory'](self.config, self.core) for handler in request_handlers: handler = list(handler) @@ -96,7 +100,11 @@ class HttpFrontend(pykka.ThreadingActor, CoreListener): result = [] for static in self.statics: result.append(( - r'/%s/?(.*)' % static['name'], + r'/%s' % static['name'], + handlers.AddSlashHandler + )) + result.append(( + r'/%s/(.*)' % static['name'], handlers.StaticFileHandler, { 'path': static['path'], diff --git a/mopidy/http/handlers.py b/mopidy/http/handlers.py index 91cc69d9..5911fe09 100644 --- a/mopidy/http/handlers.py +++ b/mopidy/http/handlers.py @@ -23,7 +23,7 @@ def mopidy_app_factory(config, core): (r'/rpc', JsonRpcHandler, { 'core': core, }), - (r'/?(.*)', StaticFileHandler, { + (r'/(.*)', StaticFileHandler, { 'path': os.path.join(os.path.dirname(__file__), 'data'), 'default_filename': 'mopidy.html' }), @@ -143,3 +143,10 @@ class StaticFileHandler(tornado.web.StaticFileHandler): self.set_header('Cache-Control', 'no-cache') self.set_header( 'X-Mopidy-Version', mopidy.__version__.encode('utf-8')) + + +class AddSlashHandler(tornado.web.RequestHandler): + + @tornado.web.addslash + def prepare(self): + return super(AddSlashHandler, self).prepare() diff --git a/tests/http/test_server.py b/tests/http/test_server.py index 1e645025..5f3495a5 100644 --- a/tests/http/test_server.py +++ b/tests/http/test_server.py @@ -45,6 +45,14 @@ class RootAppTest(HttpServerTest): response.headers['X-Mopidy-Version'], mopidy.__version__) self.assertEqual(response.headers['Cache-Control'], 'no-cache') + def test_should_return_static_files(self): + response = self.fetch('/mopidy.css', method='GET') + + self.assertIn('html {', tornado.escape.to_unicode(response.body)) + self.assertEqual( + response.headers['X-Mopidy-Version'], mopidy.__version__) + self.assertEqual(response.headers['Cache-Control'], 'no-cache') + class MopidyAppTest(HttpServerTest): def test_should_return_index(self): @@ -57,15 +65,11 @@ class MopidyAppTest(HttpServerTest): response.headers['X-Mopidy-Version'], mopidy.__version__) self.assertEqual(response.headers['Cache-Control'], 'no-cache') - def test_without_slash_should_return_index(self): - response = self.fetch('/mopidy', method='GET') + def test_without_slash_should_redirect(self): + response = self.fetch('/mopidy', method='GET', follow_redirects=False) - self.assertIn( - 'Here you can see events arriving from Mopidy in real time:', - tornado.escape.to_unicode(response.body)) - self.assertEqual( - response.headers['X-Mopidy-Version'], mopidy.__version__) - self.assertEqual(response.headers['Cache-Control'], 'no-cache') + self.assertEqual(response.code, 301) + self.assertEqual(response.headers['Location'], '/mopidy/') def test_should_return_static_files(self): response = self.fetch('/mopidy/mopidy.js', method='GET') @@ -158,6 +162,12 @@ class HttpServerWithStaticFilesTest(tornado.testing.AsyncHTTPTestCase): return tornado.web.Application(http_frontend._get_request_handlers()) + def test_without_slash_should_redirect(self): + response = self.fetch('/static', method='GET', follow_redirects=False) + + self.assertEqual(response.code, 301) + self.assertEqual(response.headers['Location'], '/static/') + def test_can_serve_static_files(self): response = self.fetch('/static/test_server.py', method='GET') @@ -203,8 +213,14 @@ class HttpServerWithWsgiAppTest(tornado.testing.AsyncHTTPTestCase): return tornado.web.Application(http_frontend._get_request_handlers()) + def test_without_slash_should_redirect(self): + response = self.fetch('/wsgi', method='GET', follow_redirects=False) + + self.assertEqual(response.code, 301) + self.assertEqual(response.headers['Location'], '/wsgi/') + def test_can_wrap_wsgi_apps(self): - response = self.fetch('/wsgi', method='GET') + response = self.fetch('/wsgi/', method='GET') self.assertEqual(200, response.code) self.assertIn(