or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

asgi-integration.mdasync-websocket.mdexceptions.mdindex.mdsync-websocket.md

asgi-integration.mddocs/

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

```