0
# ASGI Integration
1
2
Direct ASGI WebSocket support for modern Python web frameworks implementing the ASGI specification. Provides seamless integration with ASGI-compatible frameworks like FastAPI, Starlette, Django Channels, and Quart.
3
4
## Capabilities
5
6
### WebSocketASGI Class
7
8
ASGI WebSocket wrapper that provides a simplified interface for handling WebSocket connections in ASGI applications. Handles the ASGI WebSocket protocol internally while exposing a clean API for message handling.
9
10
```python { .api }
11
class WebSocketASGI:
12
def __init__(self, scope, receive, send, subprotocols=None):
13
"""
14
Initialize ASGI WebSocket wrapper.
15
16
Parameters:
17
- scope: ASGI scope dictionary containing connection details
18
- receive: ASGI receive callable for getting messages
19
- send: ASGI send callable for sending messages
20
- subprotocols: List of supported subprotocols or None
21
"""
22
```
23
24
```python { .api }
25
@classmethod
26
async def accept(cls, scope, receive, send, subprotocols=None):
27
"""
28
Accept ASGI WebSocket connection.
29
30
Parameters:
31
- scope: ASGI scope dictionary with connection metadata
32
- receive: ASGI receive callable to get messages from client
33
- send: ASGI send callable to send messages to client
34
- subprotocols: List of supported subprotocols or None for no negotiation
35
36
Returns:
37
WebSocketASGI instance ready for communication
38
"""
39
```
40
41
### WebSocketASGI Methods
42
43
```python { .api }
44
async def receive(self):
45
"""
46
Receive message from ASGI WebSocket connection.
47
48
Returns:
49
Received data as str (text messages) or bytes (binary messages)
50
51
Raises:
52
ConnectionClosed: When client disconnects
53
OSError: For unsupported message types
54
"""
55
56
async def send(self, data):
57
"""
58
Send message over ASGI WebSocket connection.
59
60
Parameters:
61
- data: Data to send (str for text message, bytes for binary message)
62
"""
63
64
async def close(self):
65
"""
66
Close ASGI WebSocket connection.
67
68
Sends close message to client through ASGI interface.
69
"""
70
```
71
72
### WebSocketASGI Attributes
73
74
```python { .api }
75
class WebSocketASGI:
76
subprotocol: str | None # Negotiated subprotocol name
77
connected: bool # Connection status
78
```
79
80
## Usage Examples
81
82
### FastAPI Integration
83
84
```python
85
from fastapi import FastAPI, WebSocket
86
import simple_websocket
87
88
app = FastAPI()
89
90
@app.websocket("/ws")
91
async def websocket_endpoint(websocket: WebSocket):
92
# Accept WebSocket connection via ASGI
93
scope = websocket.scope
94
receive = websocket.receive
95
send = websocket.send
96
97
ws = await simple_websocket.WebSocketASGI.accept(
98
scope=scope,
99
receive=receive,
100
send=send,
101
subprotocols=['chat', 'echo']
102
)
103
104
try:
105
while True:
106
# Receive message from client
107
message = await ws.receive()
108
print(f"Received: {message}")
109
110
# Echo message back
111
await ws.send(f"Echo: {message}")
112
113
except simple_websocket.ConnectionClosed:
114
print("Client disconnected")
115
finally:
116
await ws.close()
117
```
118
119
### Starlette Integration
120
121
```python
122
from starlette.applications import Starlette
123
from starlette.websockets import WebSocket
124
from starlette.routing import WebSocketRoute
125
import simple_websocket
126
127
async def websocket_handler(websocket: WebSocket):
128
# Accept WebSocket connection
129
ws = await simple_websocket.WebSocketASGI.accept(
130
scope=websocket.scope,
131
receive=websocket.receive,
132
send=websocket.send
133
)
134
135
try:
136
# Send welcome message
137
await ws.send("Welcome to the WebSocket server!")
138
139
# Handle incoming messages
140
while True:
141
data = await ws.receive()
142
143
# Handle different message types
144
if isinstance(data, str):
145
await ws.send(f"Text received: {data}")
146
elif isinstance(data, bytes):
147
await ws.send(f"Binary data received: {len(data)} bytes")
148
149
except simple_websocket.ConnectionClosed:
150
print("WebSocket connection closed")
151
152
routes = [
153
WebSocketRoute('/ws', websocket_handler)
154
]
155
156
app = Starlette(routes=routes)
157
```
158
159
### Django Channels Integration
160
161
```python
162
from channels.generic.websocket import AsyncWebsocketConsumer
163
import simple_websocket
164
165
class SimpleWebSocketConsumer(AsyncWebsocketConsumer):
166
async def connect(self):
167
# Accept connection using simple-websocket ASGI wrapper
168
self.ws = await simple_websocket.WebSocketASGI.accept(
169
scope=self.scope,
170
receive=self.receive,
171
send=self.send
172
)
173
174
# Send initial message
175
await self.ws.send("Connected to Django Channels via simple-websocket")
176
177
async def disconnect(self, close_code):
178
if hasattr(self, 'ws'):
179
await self.ws.close()
180
181
async def receive(self, text_data=None, bytes_data=None):
182
try:
183
# Use simple-websocket receive method
184
data = await self.ws.receive()
185
186
# Process and respond
187
if isinstance(data, str):
188
response = f"Processed text: {data.upper()}"
189
else:
190
response = f"Processed binary data: {len(data)} bytes"
191
192
await self.ws.send(response)
193
194
except simple_websocket.ConnectionClosed:
195
await self.close()
196
```
197
198
### Custom ASGI Application
199
200
```python
201
import simple_websocket
202
203
async def websocket_app(scope, receive, send):
204
"""
205
Simple ASGI WebSocket application using simple-websocket.
206
"""
207
if scope['type'] != 'websocket':
208
# Not a WebSocket connection
209
await send({
210
'type': 'http.response.start',
211
'status': 404,
212
'headers': []
213
})
214
await send({
215
'type': 'http.response.body',
216
'body': b'Not Found'
217
})
218
return
219
220
# Handle WebSocket connection
221
ws = await simple_websocket.WebSocketASGI.accept(
222
scope=scope,
223
receive=receive,
224
send=send,
225
subprotocols=['v1', 'v2'] # Subprotocol negotiation
226
)
227
228
try:
229
# Send connection info
230
await ws.send(f"Connected with subprotocol: {ws.subprotocol}")
231
232
# Message handling loop
233
message_count = 0
234
while True:
235
data = await ws.receive()
236
message_count += 1
237
238
# Send response with message counter
239
response = {
240
'message_id': message_count,
241
'echo': data,
242
'subprotocol': ws.subprotocol
243
}
244
245
await ws.send(str(response))
246
247
except simple_websocket.ConnectionClosed:
248
print(f"Connection closed after {message_count} messages")
249
250
# Run with any ASGI server (uvicorn, hypercorn, etc.)
251
# uvicorn app:websocket_app --host 0.0.0.0 --port 8000
252
```
253
254
### Subprotocol Negotiation with ASGI
255
256
```python
257
import simple_websocket
258
from fastapi import FastAPI, WebSocket
259
260
app = FastAPI()
261
262
@app.websocket("/ws")
263
async def websocket_with_subprotocols(websocket: WebSocket):
264
# Custom subprotocol list
265
supported_protocols = ['chat.v1', 'chat.v2', 'echo']
266
267
ws = await simple_websocket.WebSocketASGI.accept(
268
scope=websocket.scope,
269
receive=websocket.receive,
270
send=websocket.send,
271
subprotocols=supported_protocols
272
)
273
274
try:
275
# Handle different protocols differently
276
if ws.subprotocol == 'chat.v1':
277
await ws.send("Welcome to Chat v1!")
278
# Handle v1 protocol specifics
279
elif ws.subprotocol == 'chat.v2':
280
await ws.send("Welcome to Chat v2 with enhanced features!")
281
# Handle v2 protocol specifics
282
elif ws.subprotocol == 'echo':
283
await ws.send("Echo mode activated")
284
# Simple echo functionality
285
else:
286
await ws.send("No subprotocol selected")
287
288
# Main message loop
289
while True:
290
message = await ws.receive()
291
292
# Protocol-specific message handling
293
if ws.subprotocol == 'echo':
294
await ws.send(f"Echo: {message}")
295
else:
296
await ws.send(f"[{ws.subprotocol}] Received: {message}")
297
298
except simple_websocket.ConnectionClosed:
299
print(f"Connection with protocol '{ws.subprotocol}' closed")
300
```
301
302
## Error Handling
303
304
```python
305
import simple_websocket
306
from fastapi import FastAPI, WebSocket
307
308
app = FastAPI()
309
310
@app.websocket("/ws")
311
async def error_handling_example(websocket: WebSocket):
312
try:
313
ws = await simple_websocket.WebSocketASGI.accept(
314
websocket.scope,
315
websocket.receive,
316
websocket.send
317
)
318
319
while True:
320
try:
321
message = await ws.receive()
322
await ws.send(f"Received: {message}")
323
324
except simple_websocket.ConnectionClosed:
325
print("Client disconnected normally")
326
break
327
except OSError as e:
328
print(f"WebSocket error: {e}")
329
break
330
331
except Exception as e:
332
print(f"Failed to establish WebSocket connection: {e}")
333
# Connection was not properly established
334
335
finally:
336
# Cleanup if needed
337
try:
338
await ws.close()
339
except:
340
pass # Connection may already be closed
341
```