or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdconfig.mdindex.mdlogging.mdmiddleware.mdserver.mdsupervisors.mdtypes.md

server.mddocs/

0

# Server Management

1

2

Server lifecycle management with the `Server` class for running ASGI applications and the `ServerState` class for shared state across protocol connections.

3

4

## Imports

5

6

```python

7

from uvicorn import Server, Config

8

from uvicorn.server import ServerState, HANDLED_SIGNALS

9

```

10

11

## Capabilities

12

13

### Server Class

14

15

Main server class that manages the ASGI server lifecycle, including startup, shutdown, and the main event loop.

16

17

```python { .api }

18

class Server:

19

"""

20

ASGI server implementation.

21

22

Manages the asyncio event loop, socket binding, protocol instances,

23

signal handling, and graceful shutdown for ASGI applications.

24

"""

25

26

def __init__(self, config: Config) -> None:

27

"""

28

Initialize server with configuration.

29

30

Args:

31

config: Server configuration object

32

33

Attributes set:

34

config: Server configuration

35

server_state: Shared state across protocol connections

36

started: Whether server has started (False initially)

37

should_exit: Signal for graceful shutdown (False initially)

38

force_exit: Signal for immediate shutdown (False initially)

39

last_notified: Timestamp of last notification (0.0 initially)

40

"""

41

42

def run(self, sockets: list[socket.socket] | None = None) -> None:

43

"""

44

Run the server (blocking).

45

46

Creates an event loop and runs the server synchronously until

47

shutdown. This is the main entry point for running the server.

48

49

**NOTE**: This method automatically calls config.load() if not already loaded,

50

so you don't need to call it manually when using run().

51

52

Args:

53

sockets: Optional pre-bound sockets to use instead of binding new ones

54

55

This method:

56

1. Configures logging

57

2. Loads the application configuration (calls config.load() if needed)

58

3. Creates an event loop

59

4. Runs serve() in the event loop

60

5. Handles cleanup on exit

61

"""

62

63

async def serve(self, sockets: list[socket.socket] | None = None) -> None:

64

"""

65

Run the server (async).

66

67

Asynchronous entry point for running the server. Can be used

68

when you need to run the server within an existing event loop.

69

70

**NOTE**: This method automatically calls config.load() if not already loaded,

71

so you don't need to call it manually when using serve().

72

73

Args:

74

sockets: Optional pre-bound sockets to use instead of binding new ones

75

76

This method:

77

1. Loads config if not already loaded (calls config.load() if needed)

78

2. Calls startup()

79

3. Runs main_loop() if startup succeeded

80

4. Calls shutdown()

81

"""

82

83

async def startup(self, sockets: list[socket.socket] | None = None) -> None:

84

"""

85

Start the server.

86

87

**IMPORTANT**: Before calling startup(), you must call config.load() to initialize

88

the configuration. The startup() method requires access to config.lifespan_class

89

and other attributes that are only set during config.load().

90

91

Performs server initialization:

92

- Starts lifespan handler

93

- Binds sockets

94

- Creates asyncio server

95

- Starts accepting connections

96

97

Args:

98

sockets: Optional pre-bound sockets to use

99

100

Sets:

101

started: True if startup succeeds

102

should_exit: True if startup fails

103

104

Raises:

105

AttributeError: If config.load() has not been called before startup()

106

"""

107

108

async def shutdown(self, sockets: list[socket.socket] | None = None) -> None:

109

"""

110

Shutdown the server gracefully.

111

112

Performs graceful shutdown sequence:

113

1. Stops accepting new connections

114

2. Waits for active connections to complete (up to timeout_graceful_shutdown)

115

3. Shuts down lifespan handler

116

4. Closes all remaining connections

117

5. Cancels all background tasks

118

119

Args:

120

sockets: Optional sockets to close

121

122

This method ensures all resources are cleaned up properly.

123

"""

124

125

async def main_loop(self) -> None:

126

"""

127

Main server event loop.

128

129

Runs a periodic tick loop that:

130

- Checks for shutdown signals

131

- Performs periodic maintenance

132

- Monitors connection health

133

134

The loop continues until should_exit is set.

135

"""

136

137

async def on_tick(self, counter: int) -> bool:

138

"""

139

Periodic tick callback.

140

141

Called regularly from main_loop() for periodic tasks.

142

143

Args:

144

counter: Tick counter (increments each tick)

145

146

Returns:

147

True to continue running, False to trigger shutdown

148

"""

149

150

def handle_exit(self, sig: int, frame: types.FrameType | None) -> None:

151

"""

152

Signal handler for shutdown signals.

153

154

Handles SIGINT, SIGTERM, and SIGBREAK (Windows) to trigger

155

graceful shutdown.

156

157

Args:

158

sig: Signal number

159

frame: Current stack frame

160

161

Sets should_exit on first signal, force_exit on second signal.

162

"""

163

164

def capture_signals(self) -> Generator[None, None, None]:

165

"""

166

Context manager for signal handling.

167

168

Sets up signal handlers on entry and restores original handlers

169

on exit. Used internally to manage signal handling during server

170

execution.

171

172

Yields:

173

None

174

175

Example usage (internal):

176

with server.capture_signals():

177

# Server running with signal handlers active

178

...

179

# Original signal handlers restored

180

"""

181

```

182

183

### Server State

184

185

Shared state available across all protocol instances.

186

187

```python { .api }

188

class ServerState:

189

"""

190

Shared state available across all protocol instances.

191

192

Provides coordination and statistics tracking for all active

193

connections and background tasks.

194

"""

195

196

total_requests: int

197

"""Total number of HTTP requests processed since server start."""

198

199

connections: set[asyncio.Protocol]

200

"""

201

Set of active protocol connection instances.

202

203

Each connection is a protocol instance (HTTP or WebSocket) that is

204

currently handling a client connection. Connections add themselves

205

to this set when created and remove themselves when closed.

206

"""

207

208

tasks: set[asyncio.Task[None]]

209

"""

210

Set of active background tasks.

211

212

Background tasks created by protocol handlers for async operations.

213

Tasks add themselves to this set when created and remove themselves

214

when complete.

215

"""

216

217

default_headers: list[tuple[bytes, bytes]]

218

"""

219

Default HTTP headers to include in all responses.

220

221

Headers are stored as byte tuples and added to every HTTP response

222

unless overridden by the application. Includes Server and Date headers

223

if configured, plus any custom headers from config.headers.

224

"""

225

```

226

227

## Constants

228

229

```python { .api }

230

# Signals that the server handles for graceful shutdown

231

HANDLED_SIGNALS: tuple[int, ...]

232

"""

233

Signals handled by the server for graceful shutdown.

234

235

On Unix: (signal.SIGINT, signal.SIGTERM)

236

On Windows: (signal.SIGINT, signal.SIGTERM, signal.SIGBREAK)

237

238

First signal triggers graceful shutdown (should_exit = True).

239

Second signal triggers immediate shutdown (force_exit = True).

240

"""

241

```

242

243

## Usage Examples

244

245

### Basic Server Usage

246

247

```python

248

from uvicorn import Config, Server

249

250

# Define ASGI application

251

async def app(scope, receive, send):

252

assert scope['type'] == 'http'

253

await send({

254

'type': 'http.response.start',

255

'status': 200,

256

'headers': [[b'content-type', b'text/plain']],

257

})

258

await send({

259

'type': 'http.response.body',

260

'body': b'Hello, World!',

261

})

262

263

# Create configuration

264

config = Config(app=app, host="127.0.0.1", port=8000)

265

266

# Create and run server

267

server = Server(config)

268

server.run()

269

```

270

271

### Running Server in Async Context

272

273

```python

274

import asyncio

275

from uvicorn import Config, Server

276

277

async def app(scope, receive, send):

278

# Your ASGI application

279

...

280

281

async def main():

282

config = Config(app=app, host="127.0.0.1", port=8000)

283

server = Server(config)

284

285

# Run server within existing event loop

286

await server.serve()

287

288

# Run with asyncio

289

asyncio.run(main())

290

```

291

292

### Server with Pre-bound Socket

293

294

```python

295

import socket

296

from uvicorn import Config, Server

297

298

# Create and bind socket manually

299

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

300

sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

301

sock.bind(("127.0.0.1", 8000))

302

303

# Pass socket to server

304

config = Config(app="myapp:app")

305

server = Server(config)

306

server.run(sockets=[sock])

307

```

308

309

### Graceful Shutdown Handling

310

311

```python

312

import asyncio

313

from uvicorn import Config, Server

314

315

async def app(scope, receive, send):

316

# Your ASGI application

317

...

318

319

async def run_server():

320

config = Config(

321

app=app,

322

host="127.0.0.1",

323

port=8000,

324

timeout_graceful_shutdown=30, # 30 second graceful shutdown

325

)

326

327

# IMPORTANT: Must call load() before creating Server and calling startup()

328

config.load()

329

330

server = Server(config)

331

332

# Start server

333

await server.startup()

334

335

if server.started:

336

try:

337

# Run until interrupted

338

await server.main_loop()

339

finally:

340

# Ensure clean shutdown

341

await server.shutdown()

342

343

asyncio.run(run_server())

344

```

345

346

### Programmatic Shutdown

347

348

```python

349

import asyncio

350

from uvicorn import Config, Server

351

352

async def app(scope, receive, send):

353

# Your ASGI application

354

...

355

356

async def run_with_timeout():

357

config = Config(app=app, host="127.0.0.1", port=8000)

358

config.load() # Required before calling startup()

359

360

server = Server(config)

361

362

# Start server

363

await server.startup()

364

365

if server.started:

366

# Run server for 60 seconds then shutdown

367

try:

368

await asyncio.sleep(60)

369

finally:

370

server.should_exit = True

371

await server.shutdown()

372

373

asyncio.run(run_with_timeout())

374

```

375

376

### Monitoring Server State

377

378

```python

379

import asyncio

380

from uvicorn import Config, Server

381

382

async def app(scope, receive, send):

383

# Your ASGI application

384

...

385

386

async def monitor_server():

387

config = Config(app=app, host="127.0.0.1", port=8000)

388

config.load() # Required before calling startup()

389

390

server = Server(config)

391

392

await server.startup()

393

394

if server.started:

395

# Monitor connections and requests

396

async def stats_reporter():

397

while not server.should_exit:

398

state = server.server_state

399

print(f"Active connections: {len(state.connections)}")

400

print(f"Total requests: {state.total_requests}")

401

print(f"Background tasks: {len(state.tasks)}")

402

await asyncio.sleep(5)

403

404

# Run stats reporter alongside server

405

stats_task = asyncio.create_task(stats_reporter())

406

407

try:

408

await server.main_loop()

409

finally:

410

stats_task.cancel()

411

await server.shutdown()

412

413

asyncio.run(monitor_server())

414

```

415

416

### Custom Signal Handling

417

418

```python

419

import signal

420

import asyncio

421

from uvicorn import Config, Server

422

423

async def app(scope, receive, send):

424

# Your ASGI application

425

...

426

427

async def run_with_custom_signals():

428

config = Config(app=app, host="127.0.0.1", port=8000)

429

config.load() # Required before calling startup()

430

431

server = Server(config)

432

433

# Custom shutdown handler

434

def shutdown_handler(signum, frame):

435

print(f"Received signal {signum}, shutting down...")

436

server.should_exit = True

437

438

# Set up custom signal handlers

439

signal.signal(signal.SIGINT, shutdown_handler)

440

signal.signal(signal.SIGTERM, shutdown_handler)

441

442

await server.startup()

443

444

if server.started:

445

try:

446

await server.main_loop()

447

finally:

448

await server.shutdown()

449

450

asyncio.run(run_with_custom_signals())

451

```

452

453

### Multi-Stage Startup and Shutdown

454

455

```python

456

import asyncio

457

from uvicorn import Config, Server

458

459

async def app(scope, receive, send):

460

# Your ASGI application

461

...

462

463

async def run_with_stages():

464

config = Config(app=app, host="127.0.0.1", port=8000)

465

466

# Stage 0: Load configuration (required before startup)

467

print("Loading configuration...")

468

config.load()

469

470

server = Server(config)

471

472

# Stage 1: Startup

473

print("Starting server...")

474

await server.startup()

475

476

if not server.started:

477

print("Server failed to start")

478

return

479

480

print(f"Server started on {config.host}:{config.port}")

481

print(f"Active connections: {len(server.server_state.connections)}")

482

483

# Stage 2: Run

484

try:

485

await server.main_loop()

486

except KeyboardInterrupt:

487

print("\nInterrupted by user")

488

489

# Stage 3: Shutdown

490

print("Shutting down server...")

491

await server.shutdown()

492

print("Server stopped")

493

494

asyncio.run(run_with_stages())

495

```

496

497

### Server with Connection Limiting

498

499

```python

500

from uvicorn import Config, Server

501

502

async def app(scope, receive, send):

503

# Your ASGI application

504

...

505

506

# Limit concurrent connections

507

config = Config(

508

app=app,

509

host="0.0.0.0",

510

port=8000,

511

limit_concurrency=100, # Max 100 concurrent connections

512

backlog=2048, # Socket backlog

513

)

514

515

server = Server(config)

516

server.run()

517

```

518

519

### Server with Request Limiting

520

521

```python

522

from uvicorn import Config, Server

523

524

async def app(scope, receive, send):

525

# Your ASGI application

526

...

527

528

# Restart after processing 10000 requests

529

config = Config(

530

app=app,

531

host="0.0.0.0",

532

port=8000,

533

limit_max_requests=10000,

534

)

535

536

server = Server(config)

537

server.run()

538

```

539