0
# WSGI Middleware
1
2
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.
3
4
## Capabilities
5
6
### Middleware Initialization
7
8
Create WSGI middleware that wraps an Engine.IO server with optional static file serving and fallback application support.
9
10
```python { .api }
11
class WSGIApp:
12
def __init__(
13
self,
14
engineio_app,
15
wsgi_app=None,
16
static_files=None,
17
engineio_path='engine.io'
18
):
19
"""
20
Initialize WSGI middleware for Engine.IO.
21
22
Args:
23
engineio_app (Server): The Engine.IO server instance
24
wsgi_app (callable, optional): WSGI app for non-Engine.IO traffic
25
static_files (dict, optional): Static file mapping rules
26
engineio_path (str): Engine.IO endpoint path, default 'engine.io'
27
"""
28
```
29
30
### WSGI Application Interface
31
32
Standard WSGI application callable that routes requests between Engine.IO and fallback applications.
33
34
```python { .api }
35
def __call__(self, environ, start_response):
36
"""
37
WSGI application callable.
38
39
Args:
40
environ (dict): WSGI environment dictionary
41
start_response (callable): WSGI start_response callable
42
43
Returns:
44
iterable: WSGI response iterable
45
"""
46
```
47
48
### Error Handling
49
50
Built-in error handling for requests that don't match Engine.IO patterns.
51
52
```python { .api }
53
def not_found(self, start_response):
54
"""
55
Return a 404 Not Found response.
56
57
Args:
58
start_response (callable): WSGI start_response callable
59
60
Returns:
61
list: 404 response body
62
"""
63
```
64
65
## Integration Examples
66
67
### Flask Integration
68
69
```python
70
import engineio
71
from flask import Flask
72
73
# Create Flask app
74
app = Flask(__name__)
75
76
# Create Engine.IO server
77
eio = engineio.Server()
78
79
@eio.on('connect')
80
def on_connect(sid, environ):
81
print(f'Client {sid} connected')
82
83
@eio.on('message')
84
def on_message(sid, data):
85
print(f'Message from {sid}: {data}')
86
eio.send(sid, f'Echo: {data}')
87
88
@eio.on('disconnect')
89
def on_disconnect(sid):
90
print(f'Client {sid} disconnected')
91
92
# Flask routes
93
@app.route('/')
94
def index():
95
return '<h1>Hello World!</h1>'
96
97
@app.route('/api/data')
98
def get_data():
99
return {'message': 'Hello from Flask!'}
100
101
# Wrap Flask app with Engine.IO middleware
102
app = engineio.WSGIApp(eio, app)
103
104
if __name__ == '__main__':
105
import eventlet
106
import eventlet.wsgi
107
eventlet.wsgi.server(eventlet.listen(('', 5000)), app)
108
```
109
110
### Django Integration
111
112
```python
113
# django_project/wsgi.py
114
import os
115
import engineio
116
from django.core.wsgi import get_wsgi_application
117
118
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_project.settings')
119
120
# Get Django WSGI application
121
django_app = get_wsgi_application()
122
123
# Create Engine.IO server
124
eio = engineio.Server()
125
126
@eio.on('connect')
127
def on_connect(sid, environ):
128
print(f'Client {sid} connected')
129
130
@eio.on('message')
131
def on_message(sid, data):
132
eio.send(sid, f'Django says: {data}')
133
134
# Wrap Django with Engine.IO middleware
135
application = engineio.WSGIApp(eio, django_app)
136
```
137
138
### Standalone WSGI Application
139
140
```python
141
import engineio
142
143
# Create Engine.IO server only (no fallback app)
144
eio = engineio.Server()
145
146
@eio.on('connect')
147
def on_connect(sid, environ):
148
print(f'Client {sid} connected')
149
150
@eio.on('message')
151
def on_message(sid, data):
152
eio.send(sid, data.upper())
153
154
# Create standalone WSGI app
155
app = engineio.WSGIApp(eio)
156
157
# Deploy with any WSGI server
158
if __name__ == '__main__':
159
from wsgiref.simple_server import make_server
160
161
server = make_server('localhost', 8000, app)
162
print('Server running on http://localhost:8000')
163
server.serve_forever()
164
```
165
166
### Static File Serving
167
168
```python
169
import engineio
170
171
eio = engineio.Server()
172
173
# Define static file mappings
174
static_files = {
175
'/': 'index.html',
176
'/static/(.*)': r'static/\1',
177
'/images/(.*)': r'assets/images/\1'
178
}
179
180
# Create WSGI app with static file serving
181
app = engineio.WSGIApp(eio, static_files=static_files)
182
```
183
184
Static file mapping rules:
185
186
- Keys are URL patterns (can include regex groups)
187
- Values are file paths (can reference regex groups with `\1`, `\2`, etc.)
188
- Files are served relative to the current working directory
189
190
### Custom Endpoint Path
191
192
```python
193
import engineio
194
195
eio = engineio.Server()
196
197
# Use custom Engine.IO endpoint
198
app = engineio.WSGIApp(eio, engineio_path='custom/socket.io')
199
200
# Clients should connect to: http://server/custom/socket.io/
201
```
202
203
### Multiple Engine.IO Servers
204
205
```python
206
import engineio
207
208
# Create multiple servers for different purposes
209
chat_server = engineio.Server()
210
notifications_server = engineio.Server()
211
212
@chat_server.on('message')
213
def on_chat_message(sid, data):
214
chat_server.send(sid, f'Chat: {data}')
215
216
@notifications_server.on('message')
217
def on_notification(sid, data):
218
notifications_server.send(sid, f'Notification: {data}')
219
220
# Create separate middleware for each server
221
chat_app = engineio.WSGIApp(chat_server, engineio_path='chat')
222
notifications_app = engineio.WSGIApp(notifications_server, engineio_path='notifications')
223
224
# Combine using URL routing (example with Werkzeug)
225
from werkzeug.wsgi import DispatcherMiddleware
226
227
application = DispatcherMiddleware(None, {
228
'/chat': chat_app,
229
'/notifications': notifications_app
230
})
231
```
232
233
## Middleware Class (Deprecated)
234
235
```python { .api }
236
class Middleware(WSGIApp):
237
def __init__(self, engineio_app, wsgi_app=None, engineio_path='engine.io'):
238
"""
239
Deprecated alias for WSGIApp.
240
241
Args:
242
engineio_app (Server): The Engine.IO server instance
243
wsgi_app (callable, optional): WSGI app for non-Engine.IO traffic
244
engineio_path (str): Engine.IO endpoint path, default 'engine.io'
245
246
Note:
247
This class has been renamed to WSGIApp and is now deprecated.
248
Use WSGIApp instead for new applications. The static_files parameter
249
is not supported in this deprecated class.
250
"""
251
```
252
253
## WSGI Environment Access
254
255
Engine.IO event handlers receive the WSGI environment dictionary, providing access to request details:
256
257
```python
258
@eio.on('connect')
259
def on_connect(sid, environ):
260
# Access request information
261
remote_addr = environ.get('REMOTE_ADDR')
262
user_agent = environ.get('HTTP_USER_AGENT')
263
request_method = environ.get('REQUEST_METHOD')
264
query_string = environ.get('QUERY_STRING')
265
266
print(f'Client {sid} connected from {remote_addr}')
267
print(f'User-Agent: {user_agent}')
268
269
# Parse query parameters
270
from urllib.parse import parse_qs
271
params = parse_qs(query_string)
272
273
# Store client info in session
274
eio.save_session(sid, {
275
'remote_addr': remote_addr,
276
'user_agent': user_agent,
277
'params': params
278
})
279
```
280
281
## Deployment Considerations
282
283
### Production Deployment
284
285
```python
286
# For production, use a proper WSGI server
287
import engineio
288
from flask import Flask
289
290
app = Flask(__name__)
291
eio = engineio.Server(async_mode='eventlet')
292
293
# ... configure server and handlers ...
294
295
app = engineio.WSGIApp(eio, app)
296
297
# Deploy with Gunicorn + eventlet workers
298
# gunicorn --worker-class eventlet -w 1 --bind 0.0.0.0:5000 app:app
299
```
300
301
### Threading Considerations
302
303
```python
304
import engineio
305
306
# Configure for threading mode
307
eio = engineio.Server(async_mode='threading')
308
309
@eio.on('message')
310
def on_message(sid, data):
311
# Handler will run in a separate thread
312
import time
313
time.sleep(1) # Blocking operations are OK
314
eio.send(sid, 'Response after delay')
315
316
app = engineio.WSGIApp(eio)
317
```
318
319
### Eventlet Integration
320
321
```python
322
import eventlet
323
eventlet.monkey_patch()
324
325
import engineio
326
327
# Eventlet mode for high concurrency
328
eio = engineio.Server(async_mode='eventlet')
329
330
@eio.on('message')
331
def on_message(sid, data):
332
# Use eventlet.sleep for non-blocking delays
333
eventlet.sleep(1)
334
eio.send(sid, 'Eventlet response')
335
336
app = engineio.WSGIApp(eio)
337
338
# Run with eventlet WSGI server
339
eventlet.wsgi.server(eventlet.listen(('', 5000)), app)
340
```
341
342
## Error Handling
343
344
The WSGI middleware handles various error conditions automatically:
345
346
- **Invalid Engine.IO requests**: Returns appropriate HTTP error responses
347
- **Non-Engine.IO requests**: Routes to fallback WSGI app or returns 404
348
- **Static file errors**: Returns 404 for missing files
349
- **Server errors**: Logs exceptions and returns 500 responses
350
351
Custom error handling can be implemented in the fallback WSGI application:
352
353
```python
354
def custom_app(environ, start_response):
355
"""Custom WSGI app with error handling"""
356
try:
357
# Application logic here
358
response_body = b'Custom response'
359
status = '200 OK'
360
headers = [('Content-Type', 'text/plain')]
361
except Exception as e:
362
# Custom error handling
363
response_body = f'Error: {str(e)}'.encode()
364
status = '500 Internal Server Error'
365
headers = [('Content-Type', 'text/plain')]
366
367
start_response(status, headers)
368
return [response_body]
369
370
app = engineio.WSGIApp(eio, custom_app)
371
```