Skip to content

Commit

Permalink
Merge pull request #26 from aclowes/static-middleware
Browse files Browse the repository at this point in the history
move whitenoise to middleware so https redirect works on static files
  • Loading branch information
aclowes authored Aug 29, 2017
2 parents 3dc9152 + d1e26ad commit 37b5da6
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 47 deletions.
4 changes: 4 additions & 0 deletions CONTRIBUTING.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ the test to run::
Making a Release
----------------

Tag the release_ on GitHub, naming it in the ``v0.0.0`` format.

.. _release: https://github.com/aclowes/yawn/releases/new

Build the frontend code, then bundle and release a source tarball. Finally, test
installing it::

Expand Down
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ Links

* Contributing_
* License_
* `Deploying YAWN on Kubernetes via Google Container Engine`_

.. _Contributing: CONTRIBUTING.rst
.. _License: LICENSE.txt
.. _Deploying YAWN on Kubernetes via Google Container Engine: https://github.com/aclowes/yawn-gke
27 changes: 3 additions & 24 deletions yawn/management/commands/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,11 @@
from gunicorn.app.base import BaseApplication
from django.core.management.base import BaseCommand
from django.core.wsgi import get_wsgi_application
from django.conf import settings
from whitenoise.django import DjangoWhiteNoise
from whitenoise.utils import decode_path_info


class DefaultFileServer(DjangoWhiteNoise):
def __call__(self, environ, start_response):
"""
Serve index.html if not /api/ or a static file
This allows users to navigate directly to a URL like http://127.0.0.1:8000/workflows/2
and we return index.html, and then react-router interprets the path.
"""
path = decode_path_info(environ['PATH_INFO'])
if path.startswith('/api'):
return self.application(environ, start_response)
static_file = self.files.get(path)
if static_file is None:
# serve the homepage on non-existent file
static_file = self.files.get('/index.html')
return self.serve(static_file, environ, start_response)


class WSGIApplication(BaseApplication):
"""A Gunicorn Application, with logic to parse config passed from the command"""

def __init__(self, options):
self.options = options
super().__init__()
Expand Down Expand Up @@ -56,9 +37,7 @@ def _set_cfg(self, args):
self.cfg.set(k.lower(), v)

def load(self):
app = get_wsgi_application()
whitenoise = DefaultFileServer(app, settings)
return whitenoise
return get_wsgi_application()


class Command(BaseCommand):
Expand Down
Empty file.
Empty file.
23 changes: 0 additions & 23 deletions yawn/management/tests/test_webserver.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import os
import sys

from unittest import mock

from django.conf import settings
from django.core import management

from yawn.management.commands import webserver
Expand All @@ -15,24 +13,3 @@ def test_webserver(mock_run):
with mock.patch.object(sys, 'argv', ['yawn', 'worker', '-b', '0.0.0.0:8000']):
management.call_command('webserver')
assert mock_run.call_count == 1


def test_static_files():
root = os.path.join(settings.BASE_DIR, 'management/tests/static')
with mock.patch.object(settings, 'WHITENOISE_ROOT', root):
app = webserver.get_wsgi_application()
whitenoise = webserver.DefaultFileServer(app, settings)
whitenoise.application = mock.Mock()

# an API request
whitenoise({'PATH_INFO': '/api/'}, None)
assert whitenoise.application.call_count == 1

# an unmatched file, goes to homepage
start_response = mock.Mock()
response = whitenoise({'PATH_INFO': '/', 'REQUEST_METHOD': 'GET'}, start_response)
assert 'index.html' in response.filelike.name

# a real static file
response = whitenoise({'PATH_INFO': '/favicon.ico', 'REQUEST_METHOD': 'GET'}, start_response)
assert 'favicon.ico' in response.filelike.name
1 change: 1 addition & 0 deletions yawn/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'yawn.utilities.middleware.DefaultFileMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
Expand Down
23 changes: 23 additions & 0 deletions yawn/utilities/middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from whitenoise.middleware import WhiteNoiseMiddleware


class DefaultFileMiddleware(WhiteNoiseMiddleware):
"""
Serve index.html if not /api/ or a static file
This allows users to navigate directly to a URL like http://127.0.0.1:8000/workflows/2
and we return index.html, and then react-router interprets the path.
"""

def process_request(self, request):

if request.path_info.startswith('/api'):
return # handled by Django

static_file = self.files.get(request.path_info)

if static_file is None:
# serve the homepage on non-existent file
static_file = self.files.get('/index.html')

return self.serve(static_file, request)
1 change: 1 addition & 0 deletions yawn/utilities/tests/static/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
index
1 change: 1 addition & 0 deletions yawn/utilities/tests/static/static.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
static
20 changes: 20 additions & 0 deletions yawn/utilities/tests/test_middleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import os
from unittest import mock

from django.conf import settings


def test_static_files(client):
root = os.path.join(settings.BASE_DIR, 'utilities/tests/static')
with mock.patch.object(settings, 'WHITENOISE_ROOT', root):
# an API request
response = client.get('/api/')
assert 'workflows' in response.data

# an unmatched file, goes to homepage
response = client.get('/something')
assert next(response.streaming_content) == b'index\n'

# a real static file
response = client.get('/static.txt')
assert next(response.streaming_content) == b'static\n'

0 comments on commit 37b5da6

Please sign in to comment.