or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

additional.mdauth.mdclient.mdcore-server.mdindex.mdrequest-response.mdtesting.mdwebsockets.md

client.mddocs/

0

# HTTP Client

1

2

BlackSheep provides a full-featured async HTTP client built for high performance and ease of use. The `ClientSession` class supports connection pooling, SSL configuration, redirects, timeouts, middleware, and cookie management.

3

4

## ClientSession

5

6

The main HTTP client class for making requests to web services and APIs.

7

8

### Basic Client Usage

9

10

```python { .api }

11

import asyncio

12

from blacksheep.client import ClientSession

13

from blacksheep import JSONContent, TextContent

14

15

# Basic client usage

16

async def basic_example():

17

async with ClientSession() as client:

18

# GET request

19

response = await client.get("https://api.example.com/users")

20

data = await response.json()

21

print(f"Users: {data}")

22

23

# POST request with JSON

24

json_data = JSONContent({"name": "Alice", "email": "alice@example.com"})

25

response = await client.post("https://api.example.com/users", content=json_data)

26

result = await response.json()

27

print(f"Created: {result}")

28

29

asyncio.run(basic_example())

30

```

31

32

### Client Configuration

33

34

```python { .api }

35

import ssl

36

from blacksheep.client import ClientSession, ConnectionPools, CookieJar

37

from blacksheep import URL, Headers

38

39

# Advanced client configuration

40

async def configured_client():

41

# Custom SSL context

42

ssl_context = ssl.create_default_context()

43

ssl_context.check_hostname = False

44

ssl_context.verify_mode = ssl.CERT_NONE

45

46

# Custom headers

47

default_headers = Headers([

48

(b"User-Agent", b"MyApp/1.0"),

49

(b"Accept", b"application/json")

50

])

51

52

client = ClientSession(

53

base_url="https://api.example.com",

54

ssl=ssl_context, # SSL configuration

55

default_headers=default_headers, # Default headers for all requests

56

follow_redirects=True, # Follow HTTP redirects

57

connection_timeout=30.0, # Connection timeout (seconds)

58

request_timeout=60.0, # Request timeout (seconds)

59

maximum_redirects=10, # Max redirect hops

60

cookie_jar=CookieJar(), # Cookie storage

61

middlewares=[] # Client middleware

62

)

63

64

async with client:

65

response = await client.get("/users/123")

66

return await response.json()

67

```

68

69

### HTTP Methods

70

71

```python { .api }

72

from blacksheep import Headers, JSONContent, FormContent

73

from typing import Optional, Dict, Union, Iterable, Tuple

74

75

# Type aliases for client

76

URLType = Union[str, bytes, URL]

77

HeadersType = Union[Dict[str, str], Iterable[Tuple[str, str]]]

78

ParamsType = Union[Dict[str, str], Iterable[Tuple[str, str]]]

79

80

async def http_methods_example():

81

async with ClientSession() as client:

82

# GET request

83

response = await client.get(

84

url="https://api.example.com/users",

85

headers={"Authorization": "Bearer token123"},

86

params={"page": "1", "limit": "10"}

87

)

88

89

# POST request

90

json_data = JSONContent({"name": "Alice"})

91

response = await client.post(

92

url="https://api.example.com/users",

93

content=json_data,

94

headers={"Content-Type": "application/json"}

95

)

96

97

# PUT request

98

update_data = JSONContent({"name": "Alice Updated"})

99

response = await client.put(

100

url="https://api.example.com/users/123",

101

content=update_data

102

)

103

104

# DELETE request

105

response = await client.delete("https://api.example.com/users/123")

106

107

# PATCH request

108

patch_data = JSONContent({"email": "newemail@example.com"})

109

response = await client.patch(

110

url="https://api.example.com/users/123",

111

content=patch_data

112

)

113

114

# HEAD request (headers only)

115

response = await client.head("https://api.example.com/users/123")

116

117

# OPTIONS request

118

response = await client.options("https://api.example.com/users")

119

120

# TRACE request

121

response = await client.trace("https://api.example.com/debug")

122

```

123

124

### Request Content Types

125

126

```python { .api }

127

from blacksheep import JSONContent, TextContent, FormContent, MultiPartFormData, FormPart

128

129

async def content_types_example():

130

async with ClientSession() as client:

131

# JSON content

132

json_data = {"user": {"name": "Alice", "age": 30}}

133

response = await client.post(

134

"https://api.example.com/users",

135

content=JSONContent(json_data)

136

)

137

138

# Text content

139

text_data = "Plain text content"

140

response = await client.post(

141

"https://api.example.com/text",

142

content=TextContent(text_data)

143

)

144

145

# Form data (URL-encoded)

146

form_data = {"username": "alice", "password": "secret"}

147

response = await client.post(

148

"https://api.example.com/login",

149

content=FormContent(form_data)

150

)

151

152

# Multipart form data (file upload)

153

text_part = FormPart(b"title", b"My Document")

154

with open("document.pdf", "rb") as f:

155

file_data = f.read()

156

file_part = FormPart(

157

name=b"document",

158

data=file_data,

159

content_type=b"application/pdf",

160

file_name=b"document.pdf"

161

)

162

163

multipart = MultiPartFormData([text_part, file_part])

164

response = await client.post(

165

"https://api.example.com/upload",

166

content=multipart

167

)

168

```

169

170

## Response Handling

171

172

Process and parse HTTP responses from the client.

173

174

### Response Properties

175

176

```python { .api }

177

async def response_handling():

178

async with ClientSession() as client:

179

response = await client.get("https://api.example.com/users/123")

180

181

# Basic response properties

182

status_code: int = response.status

183

reason_phrase: str = response.reason

184

headers: Headers = response.headers

185

186

# Check response status

187

if response.status == 200:

188

print("Success!")

189

elif 400 <= response.status < 500:

190

print("Client error")

191

elif 500 <= response.status < 600:

192

print("Server error")

193

194

# Response content access

195

raw_bytes: bytes = await response.read()

196

text_content: str = await response.text()

197

json_data: dict = await response.json()

198

199

# Stream large responses

200

async for chunk in response.stream():

201

process_chunk(chunk)

202

```

203

204

### Response Content Parsing

205

206

```python { .api }

207

import json

208

209

async def response_parsing():

210

async with ClientSession() as client:

211

# JSON response

212

response = await client.get("https://api.example.com/users")

213

if response.declares_json():

214

users = await response.json()

215

216

# Custom JSON parsing

217

response = await client.get("https://api.example.com/data")

218

custom_data = await response.json(loads=json.loads)

219

220

# Text response

221

response = await client.get("https://api.example.com/readme")

222

readme_text = await response.text()

223

224

# Binary response

225

response = await client.get("https://api.example.com/image.jpg")

226

image_bytes = await response.read()

227

228

# Form data response

229

response = await client.get("https://api.example.com/form-data")

230

if response.declares_content_type(b"application/x-www-form-urlencoded"):

231

form_data = await response.form()

232

233

# Check content type

234

content_type = response.content_type()

235

is_json = response.declares_json()

236

is_xml = response.declares_xml()

237

has_body = response.has_body()

238

```

239

240

## Connection Management

241

242

Manage HTTP connections, pooling, and SSL configuration.

243

244

### Connection Pools

245

246

```python { .api }

247

from blacksheep.client import ConnectionPools

248

249

# Custom connection pool configuration

250

async def connection_pooling():

251

pools = ConnectionPools(

252

max_connections=100, # Total connection limit

253

max_connections_per_host=20 # Per-host connection limit

254

)

255

256

client = ClientSession(pools=pools)

257

258

async with client:

259

# Multiple concurrent requests reuse connections

260

tasks = [

261

client.get(f"https://api.example.com/users/{i}")

262

for i in range(10)

263

]

264

responses = await asyncio.gather(*tasks)

265

266

for response in responses:

267

data = await response.json()

268

print(data)

269

```

270

271

### SSL Configuration

272

273

```python { .api }

274

import ssl

275

276

async def ssl_configuration():

277

# Custom SSL context

278

ssl_context = ssl.create_default_context()

279

280

# Client certificate authentication

281

ssl_context.load_cert_chain("client-cert.pem", "client-key.pem")

282

283

# Custom CA certificate

284

ssl_context.load_verify_locations("custom-ca.pem")

285

286

# SSL options

287

ssl_context.check_hostname = True

288

ssl_context.verify_mode = ssl.CERT_REQUIRED

289

290

client = ClientSession(ssl=ssl_context)

291

292

async with client:

293

# HTTPS request with custom SSL

294

response = await client.get("https://secure-api.example.com/data")

295

296

# Disable SSL verification (not recommended for production)

297

insecure_client = ClientSession(ssl=False)

298

299

async with insecure_client:

300

response = await client.get("https://self-signed.example.com/api")

301

```

302

303

## Redirects and Error Handling

304

305

Handle HTTP redirects and client errors effectively.

306

307

### Redirect Configuration

308

309

```python { .api }

310

from blacksheep.client import ClientSession

311

312

async def redirect_handling():

313

# Follow redirects (default)

314

client = ClientSession(follow_redirects=True, maximum_redirects=5)

315

316

async with client:

317

# Automatically follows redirects

318

response = await client.get("https://example.com/redirect-me")

319

final_url = response.url # Final URL after redirects

320

321

# Manual redirect handling

322

manual_client = ClientSession(follow_redirects=False)

323

324

async with manual_client:

325

response = await client.get("https://example.com/redirect-me")

326

327

if response.is_redirect():

328

location = response.get_first_header(b"Location")

329

if location:

330

# Follow redirect manually

331

next_response = await client.get(location.decode())

332

```

333

334

### Client Exceptions

335

336

```python { .api }

337

from blacksheep.client import (

338

ConnectionTimeout, RequestTimeout, ConnectionClosedError,

339

CircularRedirectError, MaximumRedirectsExceededError,

340

MissingLocationForRedirect, UnsupportedRedirect

341

)

342

343

async def error_handling():

344

async with ClientSession() as client:

345

try:

346

response = await client.get("https://slow-api.example.com/data")

347

data = await response.json()

348

349

except ConnectionTimeout:

350

print("Connection timed out")

351

352

except RequestTimeout:

353

print("Request timed out")

354

355

except ConnectionClosedError:

356

print("Connection closed unexpectedly")

357

358

except CircularRedirectError:

359

print("Circular redirect detected")

360

361

except MaximumRedirectsExceededError:

362

print("Too many redirects")

363

364

except MissingLocationForRedirect:

365

print("Redirect response missing Location header")

366

367

except UnsupportedRedirect:

368

print("Unsupported redirect scheme")

369

```

370

371

## Cookies and Sessions

372

373

Handle cookies and maintain session state across requests.

374

375

### Cookie Management

376

377

```python { .api }

378

from blacksheep.client import CookieJar

379

from blacksheep.cookies import Cookie

380

381

async def cookie_handling():

382

# Automatic cookie handling

383

cookie_jar = CookieJar()

384

client = ClientSession(cookie_jar=cookie_jar)

385

386

async with client:

387

# Login request that sets cookies

388

login_data = FormContent({"username": "alice", "password": "secret"})

389

response = await client.post("https://example.com/login", content=login_data)

390

391

# Subsequent requests automatically include cookies

392

profile_response = await client.get("https://example.com/profile")

393

394

# Cookies are automatically stored and sent

395

396

# Manual cookie handling

397

manual_client = ClientSession(cookie_jar=False) # Disable automatic cookies

398

399

async with manual_client:

400

# Set cookies manually

401

headers = {"Cookie": "session_id=abc123; user_pref=dark_theme"}

402

response = await client.get("https://example.com/dashboard", headers=headers)

403

```

404

405

## Client Middleware

406

407

Implement custom middleware for request/response processing.

408

409

### Custom Middleware

410

411

```python { .api }

412

import time

413

from typing import Callable, Awaitable

414

415

async def logging_middleware(

416

request: Request,

417

handler: Callable[[Request], Awaitable[Response]]

418

) -> Response:

419

"""Log all client requests and responses"""

420

print(f"Sending {request.method} to {request.url}")

421

start_time = time.time()

422

423

response = await handler(request)

424

425

duration = time.time() - start_time

426

print(f"Response: {response.status} ({duration:.3f}s)")

427

428

return response

429

430

async def auth_middleware(

431

request: Request,

432

handler: Callable[[Request], Awaitable[Response]]

433

) -> Response:

434

"""Add authentication header to all requests"""

435

token = get_auth_token() # Get current auth token

436

request.add_header(b"Authorization", f"Bearer {token}".encode())

437

438

response = await handler(request)

439

440

# Handle auth errors

441

if response.status == 401:

442

# Refresh token and retry

443

new_token = await refresh_auth_token()

444

request.set_header(b"Authorization", f"Bearer {new_token}".encode())

445

response = await handler(request)

446

447

return response

448

449

# Add middleware to client

450

async def client_with_middleware():

451

client = ClientSession(middlewares=[logging_middleware, auth_middleware])

452

453

async with client:

454

# All requests will go through middleware

455

response = await client.get("https://api.example.com/protected")

456

```

457

458

## Timeouts and Performance

459

460

Configure timeouts and optimize client performance.

461

462

### Timeout Configuration

463

464

```python { .api }

465

async def timeout_configuration():

466

# Configure timeouts

467

client = ClientSession(

468

connection_timeout=10.0, # 10 seconds to establish connection

469

request_timeout=30.0 # 30 seconds for complete request

470

)

471

472

async with client:

473

try:

474

# Fast timeout for health check

475

response = await client.get("https://api.example.com/health")

476

477

except ConnectionTimeout:

478

print("Could not connect within 10 seconds")

479

480

except RequestTimeout:

481

print("Request did not complete within 30 seconds")

482

483

# Per-request timeout override

484

async def per_request_timeout():

485

async with ClientSession() as client:

486

# Override default timeout for specific request

487

headers = {"X-Timeout": "5"} # Custom timeout hint

488

response = await client.get(

489

"https://api.example.com/slow-operation",

490

headers=headers

491

)

492

```

493

494

## Complete Client Example

495

496

A comprehensive example showing various client features:

497

498

```python { .api }

499

import asyncio

500

import ssl

501

from blacksheep.client import ClientSession, CookieJar

502

from blacksheep import JSONContent, FormContent, Headers

503

from typing import Optional, Dict, Any

504

505

class APIClient:

506

"""Example API client with authentication and error handling"""

507

508

def __init__(self, base_url: str, api_key: Optional[str] = None):

509

self.base_url = base_url

510

self.api_key = api_key

511

self._client: Optional[ClientSession] = None

512

513

async def __aenter__(self) -> 'APIClient':

514

# Configure client

515

default_headers = Headers([

516

(b"User-Agent", b"MyAPIClient/1.0"),

517

(b"Accept", b"application/json")

518

])

519

520

if self.api_key:

521

default_headers.add(b"Authorization", f"Bearer {self.api_key}".encode())

522

523

self._client = ClientSession(

524

base_url=self.base_url,

525

default_headers=default_headers,

526

follow_redirects=True,

527

connection_timeout=10.0,

528

request_timeout=30.0,

529

cookie_jar=CookieJar()

530

)

531

532

await self._client.__aenter__()

533

return self

534

535

async def __aexit__(self, exc_type, exc_val, exc_tb):

536

if self._client:

537

await self._client.__aexit__(exc_type, exc_val, exc_tb)

538

539

async def get_user(self, user_id: int) -> Dict[str, Any]:

540

"""Get user by ID"""

541

response = await self._client.get(f"/users/{user_id}")

542

543

if response.status == 404:

544

raise ValueError(f"User {user_id} not found")

545

elif response.status != 200:

546

raise RuntimeError(f"API error: {response.status}")

547

548

return await response.json()

549

550

async def create_user(self, user_data: Dict[str, Any]) -> Dict[str, Any]:

551

"""Create new user"""

552

content = JSONContent(user_data)

553

response = await self._client.post("/users", content=content)

554

555

if response.status != 201:

556

error = await response.json()

557

raise RuntimeError(f"Failed to create user: {error}")

558

559

return await response.json()

560

561

async def upload_file(self, file_path: str, user_id: int) -> Dict[str, Any]:

562

"""Upload file for user"""

563

with open(file_path, "rb") as f:

564

file_data = f.read()

565

566

# Create multipart form data

567

from blacksheep import MultiPartFormData, FormPart

568

569

file_part = FormPart(

570

name=b"file",

571

data=file_data,

572

content_type=b"application/octet-stream",

573

file_name=file_path.encode()

574

)

575

576

user_part = FormPart(b"user_id", str(user_id).encode())

577

multipart = MultiPartFormData([file_part, user_part])

578

579

response = await self._client.post(f"/users/{user_id}/files", content=multipart)

580

return await response.json()

581

582

# Usage example

583

async def main():

584

async with APIClient("https://api.example.com", "your-api-key") as client:

585

# Get user

586

user = await client.get_user(123)

587

print(f"User: {user}")

588

589

# Create user

590

new_user = await client.create_user({

591

"name": "Alice Johnson",

592

"email": "alice@example.com"

593

})

594

print(f"Created: {new_user}")

595

596

# Upload file

597

result = await client.upload_file("document.pdf", new_user["id"])

598

print(f"Upload: {result}")

599

600

if __name__ == "__main__":

601

asyncio.run(main())

602

```

603

604

The BlackSheep HTTP client provides a powerful, flexible foundation for building HTTP-based integrations with excellent performance, comprehensive error handling, and rich configuration options.