Skip to content

Commit

Permalink
support serving static files in wsgi and asgi middlewares
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelgrinberg committed Nov 22, 2018
1 parent f1ccfe2 commit 0c69759
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 55 deletions.
34 changes: 28 additions & 6 deletions engineio/async_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@

class Asgi:
def __init__(self, engineio_server, asgi_app=None,
engineio_path='engine.io'):
engineio_path='engine.io', static_files=None):
self.engineio_server = engineio_server
self.asgi_app = asgi_app
self.engineio_path = engineio_path.strip('/')
self.static_files = static_files or {}

def __call__(self, scope):
if scope['type'] in ['http', 'websocket'] and \
scope['path'].startswith('/{0}/'.format(self.engineio_path)):
return self.engineio_asgi_app(scope)
elif scope['type'] == 'http' and scope['path'] in self.static_files:
return self.serve_static_file(scope)
elif self.asgi_app is not None:
return self.asgi_app(scope)
elif scope['type'] == 'lifespan':
Expand All @@ -24,9 +27,27 @@ async def _app(receive, send):
await self.engineio_server.handle_request(scope, receive, send)
return _app

async def default_asgi_app(self, receive, send):
await receive()
return self.not_found(receive, send)
def serve_static_file(self, scope):
async def _send_static_file(receive, send):
event = await receive()
if event['type'] == 'http.request':
if scope['path'] in self.static_files:
content_type = self.static_files[scope['path']][
'content_type'].encode('utf-8')
filename = self.static_files[scope['path']]['filename']
status_code = 200
with open(filename, 'rb') as f:
payload = f.read()
else:
content_type = b'text/plain'
status_code = 404
payload = b'not found'
await send({'type': 'http.response.start',
'status': status_code,
'headers': [(b'Content-Type', content_type)]})
await send({'type': 'http.response.body',
'body': payload})
return _send_static_file

async def lifespan(self, receive, send):
event = await receive()
Expand All @@ -44,9 +65,10 @@ async def not_found(self, receive, send):
'body': b'not found'})


def create_asgi_app(engineio_server, asgi_app=None, engineio_path='engine.io'):
def create_asgi_app(engineio_server, asgi_app=None, engineio_path='engine.io',
static_files=None):
return Asgi(engineio_server, asgi_app=asgi_app,
engineio_path=engineio_path)
engineio_path=engineio_path, static_files=static_files)


async def translate_request(scope, receive, send):
Expand Down
16 changes: 14 additions & 2 deletions engineio/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ class Middleware(object):
:param engineio_path: The endpoint where the Engine.IO application should
be installed. The default value is appropriate for
most cases.
:param static_files: A dictionary where the keys are URLs that should be
served as static files. For each URL, the value is
a dictionary with ``content_type`` and ``filename``
keys. This option is intended to be used for serving
client files during development.
Example usage::
import engineio
Expand All @@ -20,10 +24,12 @@ class Middleware(object):
app = engineio.Middleware(eio, wsgi_app)
eventlet.wsgi.server(eventlet.listen(('', 8000)), app)
"""
def __init__(self, engineio_app, wsgi_app=None, engineio_path='engine.io'):
def __init__(self, engineio_app, wsgi_app=None, engineio_path='engine.io',
static_files=None):
self.engineio_app = engineio_app
self.wsgi_app = wsgi_app
self.engineio_path = engineio_path.strip('/')
self.static_files = static_files or {}

def __call__(self, environ, start_response):
if 'gunicorn.socket' in environ:
Expand All @@ -45,6 +51,12 @@ def get_socket(self):
if path is not None and \
path.startswith('/{0}/'.format(self.engineio_path)):
return self.engineio_app.handle_request(environ, start_response)
elif path in self.static_files:
start_response(
'200 OK',
[('Content-Type', self.static_files[path]['content_type'])])
with open(self.static_files[path]['filename'], 'rb') as f:
return [f.read()]
elif self.wsgi_app is not None:
return self.wsgi_app(environ, start_response)
else:
Expand Down
34 changes: 0 additions & 34 deletions examples/asgi/app.py

This file was deleted.

14 changes: 7 additions & 7 deletions examples/asgi/latency.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
import engineio
from engineio.async_asgi import create_asgi_app

from app import App

eio = engineio.AsyncServer(async_mode='asgi')
app = create_asgi_app(eio, App({
'/latency.html': b'text/html',
'/static/engine.io.js': b'application/javascript',
'/static/style.css': b'text/css',
}))
app = create_asgi_app(eio, static_files={
'/': {'content_type': 'text/html', 'filename': 'latency.html'},
'/static/engine.io.js': {'content_type': 'application/javascript',
'filename': 'static/engine.io.js'},
'/static/style.css': {'content_type': 'text/css',
'filename': 'static/style.css'}
})


async def index(request):
Expand Down
11 changes: 5 additions & 6 deletions examples/asgi/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
import engineio
from engineio.async_asgi import create_asgi_app

from app import App

eio = engineio.AsyncServer(async_mode='asgi')
app = create_asgi_app(eio, App({
'/simple.html': b'text/html',
'/static/engine.io.js': b'application/javascript',
}))
app = create_asgi_app(eio, static_files={
'/': {'content_type': 'text/html', 'filename': 'simple.html'},
'/static/engine.io.js': {'content_type': 'application/javascript',
'filename': 'static/engine.io.js'}
})


async def index(request):
Expand Down

0 comments on commit 0c69759

Please sign in to comment.