0
# Web Server
1
2
Production-ready WSGI server implementation for hosting web applications with built-in support for WebSocket upgrades. Eventlet's WSGI server provides high-performance concurrent request handling.
3
4
## Capabilities
5
6
### WSGI Server
7
8
A full-featured WSGI server that can host any WSGI-compliant web application.
9
10
```python { .api }
11
def server(sock, site, log=None, environ=None, max_size=None,
12
max_http_version=None, protocol=wsgi.HttpProtocol,
13
server_event=None, minimum_chunk_size=None,
14
log_x_forwarded_for=True, custom_pool=None,
15
keepalive=True, log_output=True, log_format=None,
16
url_length_limit=8192, debug=True, socket_timeout=None,
17
capitalize_response_headers=True):
18
"""
19
Start a WSGI server handling requests on the given socket.
20
21
Parameters:
22
- sock: listening socket to accept connections from
23
- site: WSGI application callable
24
- log: file-like object for access logging (default: sys.stdout)
25
- environ: dict of additional WSGI environ variables
26
- max_size: int, maximum number of concurrent connections
27
- max_http_version: str, maximum HTTP version to support
28
- protocol: protocol class for handling HTTP requests
29
- server_event: Event object signaled when server starts
30
- minimum_chunk_size: int, minimum chunk size for chunked encoding
31
- log_x_forwarded_for: bool, whether to log X-Forwarded-For header
32
- custom_pool: GreenPool instance for connection handling
33
- keepalive: bool, whether to support HTTP keep-alive
34
- log_output: bool, whether to enable access logging
35
- log_format: str, custom log format string
36
- url_length_limit: int, maximum URL length in bytes
37
- debug: bool, whether to include debug information in errors
38
- socket_timeout: int or None, socket timeout in seconds (default: None)
39
- capitalize_response_headers: bool, whether to capitalize response headers
40
41
Returns:
42
None (runs until interrupted)
43
"""
44
45
def format_date_time(timestamp):
46
"""
47
Format a unix timestamp to HTTP standard date/time string.
48
49
Parameters:
50
- timestamp: float, unix timestamp
51
52
Returns:
53
str: formatted date string for HTTP headers
54
"""
55
```
56
57
### WebSocket Support
58
59
WebSocket protocol implementation that integrates with WSGI applications.
60
61
```python { .api }
62
class WebSocketWSGI:
63
"""
64
Wraps a websocket handler function in a WSGI application.
65
Enables WebSocket support within WSGI frameworks.
66
"""
67
68
def __init__(self, handler):
69
"""
70
Create a WebSocket WSGI wrapper.
71
72
Parameters:
73
- handler: callable(ws) that handles WebSocket connections
74
"""
75
76
def __call__(self, environ, start_response):
77
"""
78
WSGI application interface.
79
80
Parameters:
81
- environ: WSGI environment dict
82
- start_response: WSGI start_response callable
83
84
Returns:
85
WSGI response iterable
86
"""
87
88
class WebSocket:
89
"""
90
WebSocket connection object for sending and receiving messages.
91
"""
92
93
def send(self, message):
94
"""
95
Send a message to the WebSocket client.
96
97
Parameters:
98
- message: str or bytes, message to send
99
100
Returns:
101
None
102
103
Raises:
104
ConnectionClosed: if WebSocket connection is closed
105
"""
106
107
def receive(self):
108
"""
109
Receive a message from the WebSocket client.
110
111
Returns:
112
str or bytes: received message
113
114
Raises:
115
ConnectionClosed: if WebSocket connection is closed
116
"""
117
118
def close(self):
119
"""
120
Close the WebSocket connection.
121
122
Returns:
123
None
124
"""
125
126
@property
127
def closed(self):
128
"""
129
Check if the WebSocket connection is closed.
130
131
Returns:
132
bool: True if closed, False if open
133
"""
134
```
135
136
## Usage Examples
137
138
### Basic WSGI Application
139
140
```python
141
import eventlet
142
import eventlet.wsgi
143
144
def hello_world_app(environ, start_response):
145
"""Simple WSGI application"""
146
status = '200 OK'
147
headers = [('Content-Type', 'text/plain')]
148
start_response(status, headers)
149
return [b'Hello, World!']
150
151
def run_wsgi_server():
152
"""Run a basic WSGI server"""
153
# Create listening socket
154
sock = eventlet.listen(('localhost', 8080))
155
print("WSGI server listening on localhost:8080")
156
157
# Start WSGI server
158
eventlet.wsgi.server(sock, hello_world_app)
159
160
if __name__ == "__main__":
161
run_wsgi_server()
162
```
163
164
### Flask Application
165
166
```python
167
import eventlet
168
import eventlet.wsgi
169
from flask import Flask
170
171
app = Flask(__name__)
172
173
@app.route('/')
174
def hello():
175
return 'Hello from Flask on Eventlet!'
176
177
@app.route('/users/<int:user_id>')
178
def get_user(user_id):
179
return f'User ID: {user_id}'
180
181
@app.route('/api/data')
182
def api_data():
183
# Simulate some async work
184
eventlet.sleep(0.1)
185
return {'message': 'Data from API', 'status': 'success'}
186
187
def run_flask_server():
188
"""Run Flask application with Eventlet WSGI server"""
189
sock = eventlet.listen(('localhost', 5000))
190
print("Flask server with Eventlet listening on localhost:5000")
191
192
eventlet.wsgi.server(
193
sock,
194
app,
195
log_output=True,
196
max_size=1000 # Max 1000 concurrent connections
197
)
198
199
if __name__ == "__main__":
200
# Enable monkey patching for Flask compatibility
201
eventlet.monkey_patch()
202
run_flask_server()
203
```
204
205
### WebSocket Chat Server
206
207
```python
208
import eventlet
209
import eventlet.wsgi
210
import eventlet.websocket
211
212
# Store connected clients
213
clients = set()
214
215
def websocket_handler(ws):
216
"""Handle WebSocket connections"""
217
clients.add(ws)
218
print(f"Client connected. Total clients: {len(clients)}")
219
220
try:
221
while not ws.closed:
222
message = ws.receive()
223
if message is None:
224
break
225
226
print(f"Received: {message}")
227
228
# Broadcast message to all connected clients
229
disconnected = set()
230
for client in clients:
231
try:
232
client.send(f"Broadcast: {message}")
233
except:
234
disconnected.add(client)
235
236
# Remove disconnected clients
237
clients -= disconnected
238
239
except eventlet.websocket.ConnectionClosed:
240
pass
241
finally:
242
clients.discard(ws)
243
print(f"Client disconnected. Total clients: {len(clients)}")
244
245
def websocket_app(environ, start_response):
246
"""WSGI app that handles WebSocket upgrades"""
247
if environ.get('HTTP_UPGRADE', '').lower() == 'websocket':
248
# Handle WebSocket upgrade
249
return eventlet.websocket.WebSocketWSGI(websocket_handler)(environ, start_response)
250
else:
251
# Handle regular HTTP requests
252
status = '200 OK'
253
headers = [('Content-Type', 'text/html')]
254
start_response(status, headers)
255
256
return [b"""
257
<!DOCTYPE html>
258
<html>
259
<head><title>WebSocket Chat</title></head>
260
<body>
261
<div id="messages"></div>
262
<input type="text" id="messageInput" placeholder="Type a message...">
263
<button onclick="sendMessage()">Send</button>
264
265
<script>
266
const ws = new WebSocket('ws://localhost:8080');
267
const messages = document.getElementById('messages');
268
269
ws.onmessage = function(event) {
270
const div = document.createElement('div');
271
div.textContent = event.data;
272
messages.appendChild(div);
273
};
274
275
function sendMessage() {
276
const input = document.getElementById('messageInput');
277
ws.send(input.value);
278
input.value = '';
279
}
280
281
document.getElementById('messageInput').addEventListener('keypress', function(e) {
282
if (e.key === 'Enter') {
283
sendMessage();
284
}
285
});
286
</script>
287
</body>
288
</html>
289
"""]
290
291
def run_websocket_server():
292
"""Run WebSocket chat server"""
293
sock = eventlet.listen(('localhost', 8080))
294
print("WebSocket chat server listening on localhost:8080")
295
296
eventlet.wsgi.server(sock, websocket_app, debug=True)
297
298
if __name__ == "__main__":
299
eventlet.monkey_patch()
300
run_websocket_server()
301
```
302
303
### Production WSGI Server with Configuration
304
305
```python
306
import eventlet
307
import eventlet.wsgi
308
import logging
309
import sys
310
311
def create_wsgi_app():
312
"""Create a sample WSGI application"""
313
def app(environ, start_response):
314
path = environ.get('PATH_INFO', '/')
315
316
if path == '/':
317
status = '200 OK'
318
headers = [('Content-Type', 'text/html')]
319
start_response(status, headers)
320
return [b'<h1>Production WSGI Server</h1>']
321
322
elif path == '/health':
323
status = '200 OK'
324
headers = [('Content-Type', 'application/json')]
325
start_response(status, headers)
326
return [b'{"status": "healthy"}']
327
328
else:
329
status = '404 Not Found'
330
headers = [('Content-Type', 'text/plain')]
331
start_response(status, headers)
332
return [b'Page not found']
333
334
return app
335
336
def run_production_server():
337
"""Run production WSGI server with full configuration"""
338
# Set up logging
339
logging.basicConfig(
340
level=logging.INFO,
341
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
342
)
343
344
# Create application
345
app = create_wsgi_app()
346
347
# Create listening socket
348
sock = eventlet.listen(('0.0.0.0', 8000))
349
print("Production WSGI server listening on 0.0.0.0:8000")
350
351
# Configure custom environment variables
352
custom_environ = {
353
'SERVER_NAME': 'eventlet-server',
354
'SERVER_SOFTWARE': 'Eventlet/1.0'
355
}
356
357
# Start server with production configuration
358
eventlet.wsgi.server(
359
sock,
360
app,
361
log=sys.stdout, # Access log output
362
environ=custom_environ,
363
max_size=2000, # Max concurrent connections
364
keepalive=True, # Enable HTTP keep-alive
365
log_x_forwarded_for=True, # Log X-Forwarded-For header
366
url_length_limit=16384, # 16KB URL limit
367
socket_timeout=30, # 30 second socket timeout
368
debug=False, # Disable debug mode for production
369
capitalize_response_headers=True
370
)
371
372
if __name__ == "__main__":
373
eventlet.monkey_patch()
374
run_production_server()
375
```
376
377
### WSGI with Custom Logging
378
379
```python
380
import eventlet
381
import eventlet.wsgi
382
import logging
383
import time
384
from io import StringIO
385
386
class WSGILogger:
387
"""Custom WSGI access logger"""
388
389
def __init__(self, logger):
390
self.logger = logger
391
392
def write(self, msg):
393
"""Write log message"""
394
if msg.strip(): # Ignore empty messages
395
self.logger.info(msg.strip())
396
397
def flush(self):
398
"""Flush any buffered output"""
399
pass
400
401
def timed_wsgi_app(environ, start_response):
402
"""WSGI app that tracks request timing"""
403
start_time = time.time()
404
405
# Process request
406
path = environ.get('PATH_INFO', '/')
407
408
if path.startswith('/slow'):
409
# Simulate slow endpoint
410
eventlet.sleep(2.0)
411
response = b'Slow response completed'
412
else:
413
response = b'Fast response'
414
415
# Calculate processing time
416
processing_time = time.time() - start_time
417
418
status = '200 OK'
419
headers = [
420
('Content-Type', 'text/plain'),
421
('X-Processing-Time', f'{processing_time:.3f}s')
422
]
423
start_response(status, headers)
424
425
return [response]
426
427
def run_logged_server():
428
"""Run server with custom logging"""
429
# Set up logger
430
logger = logging.getLogger('wsgi_access')
431
logger.setLevel(logging.INFO)
432
433
handler = logging.StreamHandler()
434
formatter = logging.Formatter(
435
'%(asctime)s - %(name)s - %(message)s'
436
)
437
handler.setFormatter(formatter)
438
logger.addHandler(handler)
439
440
# Create custom log writer
441
log_writer = WSGILogger(logger)
442
443
# Start server
444
sock = eventlet.listen(('localhost', 8080))
445
print("Logged WSGI server listening on localhost:8080")
446
447
eventlet.wsgi.server(
448
sock,
449
timed_wsgi_app,
450
log=log_writer,
451
log_format='%(client_ip)s - "%(request_line)s" %(status_code)s %(body_length)s %(wall_seconds).6f',
452
log_output=True
453
)
454
455
if __name__ == "__main__":
456
eventlet.monkey_patch()
457
run_logged_server()
458
```
459
460
### SSL/HTTPS WSGI Server
461
462
```python
463
import eventlet
464
import eventlet.wsgi
465
import ssl
466
467
def https_app(environ, start_response):
468
"""WSGI app for HTTPS server"""
469
status = '200 OK'
470
headers = [
471
('Content-Type', 'text/html'),
472
('Strict-Transport-Security', 'max-age=31536000; includeSubDomains')
473
]
474
start_response(status, headers)
475
476
return [b'''
477
<html>
478
<head><title>Secure HTTPS Server</title></head>
479
<body>
480
<h1>Secure Connection</h1>
481
<p>This page is served over HTTPS using Eventlet.</p>
482
</body>
483
</html>
484
''']
485
486
def run_https_server():
487
"""Run HTTPS WSGI server"""
488
# Create socket and wrap with SSL
489
sock = eventlet.listen(('localhost', 8443))
490
491
# Wrap socket with SSL
492
ssl_sock = eventlet.wrap_ssl(
493
sock,
494
certfile='server.crt', # SSL certificate file
495
keyfile='server.key', # SSL private key file
496
server_side=True,
497
ssl_version=ssl.PROTOCOL_TLS,
498
ciphers='ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS'
499
)
500
501
print("HTTPS WSGI server listening on https://localhost:8443")
502
503
eventlet.wsgi.server(
504
ssl_sock,
505
https_app,
506
log_output=True,
507
debug=False
508
)
509
510
if __name__ == "__main__":
511
eventlet.monkey_patch()
512
run_https_server()
513
```
514
515
## Configuration Options
516
517
### Server Performance Tuning
518
519
```python
520
# High-concurrency configuration
521
eventlet.wsgi.server(
522
sock,
523
app,
524
max_size=5000, # Support 5000 concurrent connections
525
socket_timeout=120, # 2 minute timeout
526
keepalive=True, # Enable keep-alive for efficiency
527
minimum_chunk_size=8192 # Larger chunks for better performance
528
)
529
530
# Memory-constrained configuration
531
eventlet.wsgi.server(
532
sock,
533
app,
534
max_size=500, # Limit concurrent connections
535
socket_timeout=30, # Shorter timeout
536
url_length_limit=4096 # Smaller URL limit
537
)
538
```
539
540
### Custom Protocol Handling
541
542
```python
543
import eventlet.wsgi
544
545
class CustomProtocol(eventlet.wsgi.HttpProtocol):
546
"""Custom HTTP protocol with additional features"""
547
548
def handle_one_request(self):
549
"""Override to add custom request handling"""
550
# Add custom headers or logging
551
result = super().handle_one_request()
552
return result
553
554
# Use custom protocol
555
eventlet.wsgi.server(
556
sock,
557
app,
558
protocol=CustomProtocol
559
)
560
```