Engine.IO server and client for Python providing real-time bidirectional communication
—
WSGI application middleware for integrating Engine.IO servers with Flask, Django, and other WSGI-compatible web frameworks. Provides seamless integration with existing web applications while handling Engine.IO traffic.
Create WSGI middleware that wraps an Engine.IO server with optional static file serving and fallback application support.
class WSGIApp:
def __init__(
self,
engineio_app,
wsgi_app=None,
static_files=None,
engineio_path='engine.io'
):
"""
Initialize WSGI middleware for Engine.IO.
Args:
engineio_app (Server): The Engine.IO server instance
wsgi_app (callable, optional): WSGI app for non-Engine.IO traffic
static_files (dict, optional): Static file mapping rules
engineio_path (str): Engine.IO endpoint path, default 'engine.io'
"""Standard WSGI application callable that routes requests between Engine.IO and fallback applications.
def __call__(self, environ, start_response):
"""
WSGI application callable.
Args:
environ (dict): WSGI environment dictionary
start_response (callable): WSGI start_response callable
Returns:
iterable: WSGI response iterable
"""Built-in error handling for requests that don't match Engine.IO patterns.
def not_found(self, start_response):
"""
Return a 404 Not Found response.
Args:
start_response (callable): WSGI start_response callable
Returns:
list: 404 response body
"""import engineio
from flask import Flask
# Create Flask app
app = Flask(__name__)
# Create Engine.IO server
eio = engineio.Server()
@eio.on('connect')
def on_connect(sid, environ):
print(f'Client {sid} connected')
@eio.on('message')
def on_message(sid, data):
print(f'Message from {sid}: {data}')
eio.send(sid, f'Echo: {data}')
@eio.on('disconnect')
def on_disconnect(sid):
print(f'Client {sid} disconnected')
# Flask routes
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
@app.route('/api/data')
def get_data():
return {'message': 'Hello from Flask!'}
# Wrap Flask app with Engine.IO middleware
app = engineio.WSGIApp(eio, app)
if __name__ == '__main__':
import eventlet
import eventlet.wsgi
eventlet.wsgi.server(eventlet.listen(('', 5000)), app)# django_project/wsgi.py
import os
import engineio
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings')
# Get Django WSGI application
django_app = get_wsgi_application()
# Create Engine.IO server
eio = engineio.Server()
@eio.on('connect')
def on_connect(sid, environ):
print(f'Client {sid} connected')
@eio.on('message')
def on_message(sid, data):
eio.send(sid, f'Django says: {data}')
# Wrap Django with Engine.IO middleware
application = engineio.WSGIApp(eio, django_app)import engineio
# Create Engine.IO server only (no fallback app)
eio = engineio.Server()
@eio.on('connect')
def on_connect(sid, environ):
print(f'Client {sid} connected')
@eio.on('message')
def on_message(sid, data):
eio.send(sid, data.upper())
# Create standalone WSGI app
app = engineio.WSGIApp(eio)
# Deploy with any WSGI server
if __name__ == '__main__':
from wsgiref.simple_server import make_server
server = make_server('localhost', 8000, app)
print('Server running on http://localhost:8000')
server.serve_forever()import engineio
eio = engineio.Server()
# Define static file mappings
static_files = {
'/': 'index.html',
'/static/(.*)': r'static/\1',
'/images/(.*)': r'assets/images/\1'
}
# Create WSGI app with static file serving
app = engineio.WSGIApp(eio, static_files=static_files)Static file mapping rules:
\1, \2, etc.)import engineio
eio = engineio.Server()
# Use custom Engine.IO endpoint
app = engineio.WSGIApp(eio, engineio_path='custom/socket.io')
# Clients should connect to: http://server/custom/socket.io/import engineio
# Create multiple servers for different purposes
chat_server = engineio.Server()
notifications_server = engineio.Server()
@chat_server.on('message')
def on_chat_message(sid, data):
chat_server.send(sid, f'Chat: {data}')
@notifications_server.on('message')
def on_notification(sid, data):
notifications_server.send(sid, f'Notification: {data}')
# Create separate middleware for each server
chat_app = engineio.WSGIApp(chat_server, engineio_path='chat')
notifications_app = engineio.WSGIApp(notifications_server, engineio_path='notifications')
# Combine using URL routing (example with Werkzeug)
from werkzeug.wsgi import DispatcherMiddleware
application = DispatcherMiddleware(None, {
'/chat': chat_app,
'/notifications': notifications_app
})class Middleware(WSGIApp):
def __init__(self, engineio_app, wsgi_app=None, engineio_path='engine.io'):
"""
Deprecated alias for WSGIApp.
Args:
engineio_app (Server): The Engine.IO server instance
wsgi_app (callable, optional): WSGI app for non-Engine.IO traffic
engineio_path (str): Engine.IO endpoint path, default 'engine.io'
Note:
This class has been renamed to WSGIApp and is now deprecated.
Use WSGIApp instead for new applications. The static_files parameter
is not supported in this deprecated class.
"""Engine.IO event handlers receive the WSGI environment dictionary, providing access to request details:
@eio.on('connect')
def on_connect(sid, environ):
# Access request information
remote_addr = environ.get('REMOTE_ADDR')
user_agent = environ.get('HTTP_USER_AGENT')
request_method = environ.get('REQUEST_METHOD')
query_string = environ.get('QUERY_STRING')
print(f'Client {sid} connected from {remote_addr}')
print(f'User-Agent: {user_agent}')
# Parse query parameters
from urllib.parse import parse_qs
params = parse_qs(query_string)
# Store client info in session
eio.save_session(sid, {
'remote_addr': remote_addr,
'user_agent': user_agent,
'params': params
})# For production, use a proper WSGI server
import engineio
from flask import Flask
app = Flask(__name__)
eio = engineio.Server(async_mode='eventlet')
# ... configure server and handlers ...
app = engineio.WSGIApp(eio, app)
# Deploy with Gunicorn + eventlet workers
# gunicorn --worker-class eventlet -w 1 --bind 0.0.0.0:5000 app:appimport engineio
# Configure for threading mode
eio = engineio.Server(async_mode='threading')
@eio.on('message')
def on_message(sid, data):
# Handler will run in a separate thread
import time
time.sleep(1) # Blocking operations are OK
eio.send(sid, 'Response after delay')
app = engineio.WSGIApp(eio)import eventlet
eventlet.monkey_patch()
import engineio
# Eventlet mode for high concurrency
eio = engineio.Server(async_mode='eventlet')
@eio.on('message')
def on_message(sid, data):
# Use eventlet.sleep for non-blocking delays
eventlet.sleep(1)
eio.send(sid, 'Eventlet response')
app = engineio.WSGIApp(eio)
# Run with eventlet WSGI server
eventlet.wsgi.server(eventlet.listen(('', 5000)), app)The WSGI middleware handles various error conditions automatically:
Custom error handling can be implemented in the fallback WSGI application:
def custom_app(environ, start_response):
"""Custom WSGI app with error handling"""
try:
# Application logic here
response_body = b'Custom response'
status = '200 OK'
headers = [('Content-Type', 'text/plain')]
except Exception as e:
# Custom error handling
response_body = f'Error: {str(e)}'.encode()
status = '500 Internal Server Error'
headers = [('Content-Type', 'text/plain')]
start_response(status, headers)
return [response_body]
app = engineio.WSGIApp(eio, custom_app)Install with Tessl CLI
npx tessl i tessl/pypi-python-engineio