or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-management.mdengine-system.mdform-data.mdhttp-utilities.mdindex.mdplugin-system.mdrequest-operations.mdresponse-handling.mdrouting-system.mdserver-framework.md

plugin-system.mddocs/

0

# Plugin System

1

2

Plugin installation, configuration, and built-in plugins for extending client functionality with features like timeouts, cookies, authentication, and more.

3

4

## Capabilities

5

6

### HttpClientPlugin Interface

7

8

Core interface for creating and installing client plugins.

9

10

```kotlin { .api }

11

/**

12

* Interface for HTTP client plugins

13

*/

14

interface HttpClientPlugin<out TConfig : Any, TPlugin : Any> {

15

/** Unique key for identifying the plugin */

16

val key: AttributeKey<TPlugin>

17

18

/**

19

* Prepare the plugin with configuration

20

* @param block Configuration block

21

* @return Configured plugin instance

22

*/

23

fun prepare(block: TConfig.() -> Unit = {}): TPlugin

24

25

/**

26

* Install the plugin into a client

27

* @param plugin Prepared plugin instance

28

* @param scope HttpClient to install into

29

*/

30

fun install(plugin: TPlugin, scope: HttpClient)

31

}

32

```

33

34

### Plugin Installation

35

36

Methods for installing plugins into HttpClient configurations.

37

38

```kotlin { .api }

39

/**

40

* Install a plugin with configuration in HttpClientConfig

41

* @param plugin The plugin to install

42

* @param configure Configuration block for the plugin

43

*/

44

fun <TBuilder : Any, TPlugin : Any> HttpClientConfig<*>.install(

45

plugin: HttpClientPlugin<TBuilder, TPlugin>,

46

configure: TBuilder.() -> Unit = {}

47

)

48

49

/**

50

* Install a plugin by key with custom installation logic

51

* @param key String identifier for the plugin

52

* @param block Installation logic

53

*/

54

fun HttpClientConfig<*>.install(key: String, block: HttpClient.() -> Unit)

55

```

56

57

**Usage Examples:**

58

59

```kotlin

60

import io.ktor.client.*

61

import io.ktor.client.plugins.*

62

63

val client = HttpClient {

64

// Install plugin with configuration

65

install(HttpTimeout) {

66

requestTimeoutMillis = 30000

67

connectTimeoutMillis = 10000

68

socketTimeoutMillis = 60000

69

}

70

71

// Install plugin without configuration

72

install(HttpRedirect)

73

74

// Install by string key

75

install("CustomPlugin") {

76

// Custom installation logic

77

}

78

}

79

```

80

81

## Built-in Plugins

82

83

### HttpTimeout Plugin

84

85

Configures request, connection, and socket timeouts.

86

87

```kotlin { .api }

88

/**

89

* HTTP timeout plugin for configuring request timeouts

90

*/

91

val HttpTimeout: ClientPlugin<HttpTimeoutConfig>

92

93

/**

94

* Configuration for HTTP timeout plugin

95

*/

96

class HttpTimeoutConfig {

97

/** Total request timeout in milliseconds */

98

var requestTimeoutMillis: Long? = null

99

100

/** Connection timeout in milliseconds */

101

var connectTimeoutMillis: Long? = null

102

103

/** Socket timeout in milliseconds */

104

var socketTimeoutMillis: Long? = null

105

}

106

107

/**

108

* Configure timeout for a specific request

109

* @param block Configuration block for timeout

110

*/

111

fun HttpRequestBuilder.timeout(block: HttpTimeoutConfig.() -> Unit)

112

```

113

114

**Usage Examples:**

115

116

```kotlin

117

import io.ktor.client.*

118

import io.ktor.client.plugins.*

119

import io.ktor.client.request.*

120

121

// Global timeout configuration

122

val client = HttpClient {

123

install(HttpTimeout) {

124

requestTimeoutMillis = 30000

125

connectTimeoutMillis = 5000

126

socketTimeoutMillis = 60000

127

}

128

}

129

130

// Per-request timeout configuration

131

val response = client.get("https://api.example.com/slow-endpoint") {

132

timeout {

133

requestTimeoutMillis = 60000

134

}

135

}

136

```

137

138

### HttpRedirect Plugin

139

140

Handles HTTP redirects automatically.

141

142

```kotlin { .api }

143

/**

144

* HTTP redirect plugin for handling redirects

145

*/

146

val HttpRedirect: ClientPlugin<HttpRedirect.Config>

147

148

class HttpRedirect {

149

/**

150

* Configuration for HTTP redirect plugin

151

*/

152

class Config {

153

/** Whether to check HTTP method when following redirects */

154

var checkHttpMethod: Boolean = true

155

156

/** Whether to allow HTTPS to HTTP downgrades */

157

var allowHttpsDowngrade: Boolean = false

158

159

/** Maximum number of redirect jumps to follow */

160

var maxJumps: Int = 20

161

}

162

}

163

```

164

165

**Usage Examples:**

166

167

```kotlin

168

import io.ktor.client.*

169

import io.ktor.client.plugins.*

170

171

val client = HttpClient {

172

install(HttpRedirect) {

173

checkHttpMethod = false

174

allowHttpsDowngrade = true

175

maxJumps = 10

176

}

177

}

178

```

179

180

### HttpCallValidator Plugin

181

182

Validates responses and handles exceptions.

183

184

```kotlin { .api }

185

/**

186

* HTTP call validator plugin for response validation

187

*/

188

val HttpCallValidator: ClientPlugin<HttpCallValidator.Config>

189

190

class HttpCallValidator {

191

/**

192

* Configuration for HTTP call validator plugin

193

*/

194

class Config {

195

/**

196

* Add response validation logic

197

* @param block Validation block that receives the response

198

*/

199

fun validateResponse(block: suspend (response: HttpResponse) -> Unit)

200

201

/**

202

* Add exception handling logic for requests

203

* @param block Exception handling block

204

*/

205

fun handleResponseExceptionWithRequest(

206

block: suspend (exception: Throwable, request: HttpRequest) -> Unit

207

)

208

}

209

}

210

```

211

212

**Usage Examples:**

213

214

```kotlin

215

import io.ktor.client.*

216

import io.ktor.client.plugins.*

217

import io.ktor.client.statement.*

218

219

val client = HttpClient {

220

install(HttpCallValidator) {

221

validateResponse { response ->

222

if (response.status.value !in 200..299) {

223

throw ResponseException(response, "HTTP ${response.status}")

224

}

225

}

226

227

handleResponseExceptionWithRequest { exception, request ->

228

println("Request to ${request.url} failed: ${exception.message}")

229

}

230

}

231

}

232

```

233

234

### UserAgent Plugin

235

236

Sets the User-Agent header for requests.

237

238

```kotlin { .api }

239

/**

240

* User-Agent plugin for setting user agent header

241

*/

242

val UserAgent: ClientPlugin<UserAgentConfig>

243

244

/**

245

* Configuration for User-Agent plugin

246

*/

247

class UserAgentConfig {

248

/** User agent string to use */

249

var agent: String = "Ktor client"

250

}

251

```

252

253

**Usage Examples:**

254

255

```kotlin

256

import io.ktor.client.*

257

import io.ktor.client.plugins.*

258

259

val client = HttpClient {

260

install(UserAgent) {

261

agent = "MyApplication/1.0.0"

262

}

263

}

264

```

265

266

### DefaultRequest Plugin

267

268

Sets default configuration for all requests.

269

270

```kotlin { .api }

271

/**

272

* Default request plugin for setting default request configuration

273

*/

274

val DefaultRequest: ClientPlugin<DefaultRequest.Config>

275

276

class DefaultRequest {

277

/**

278

* Configuration for default request plugin

279

*/

280

class Config {

281

/**

282

* Configure default request settings

283

* @param block Configuration block applied to all requests

284

*/

285

fun request(block: HttpRequestBuilder.() -> Unit)

286

}

287

}

288

```

289

290

**Usage Examples:**

291

292

```kotlin

293

import io.ktor.client.*

294

import io.ktor.client.plugins.*

295

import io.ktor.client.request.*

296

import io.ktor.http.*

297

298

val client = HttpClient {

299

install(DefaultRequest) {

300

request {

301

host = "api.example.com"

302

url {

303

protocol = URLProtocol.HTTPS

304

path("v1/")

305

}

306

header(HttpHeaders.Authorization, "Bearer $token")

307

}

308

}

309

}

310

311

// This request will use the default configuration

312

val response = client.get("users") // Becomes https://api.example.com/v1/users

313

```

314

315

### HttpCookies Plugin

316

317

Handles HTTP cookies automatically.

318

319

```kotlin { .api }

320

/**

321

* HTTP cookies plugin for cookie management

322

*/

323

val HttpCookies: ClientPlugin<HttpCookies.Config>

324

325

class HttpCookies {

326

/**

327

* Configuration for HTTP cookies plugin

328

*/

329

class Config {

330

/** Cookie storage implementation */

331

var storage: CookiesStorage = AcceptAllCookiesStorage()

332

}

333

}

334

335

/**

336

* Interface for cookie storage implementations

337

*/

338

interface CookiesStorage : Closeable {

339

/**

340

* Get cookies for a request URL

341

* @param requestUrl URL of the request

342

* @return List of applicable cookies

343

*/

344

suspend fun get(requestUrl: Url): List<Cookie>

345

346

/**

347

* Add a cookie from a response

348

* @param requestUrl URL of the request

349

* @param cookie Cookie to store

350

*/

351

suspend fun addCookie(requestUrl: Url, cookie: Cookie)

352

}

353

354

/**

355

* Cookie storage that accepts all cookies

356

*/

357

class AcceptAllCookiesStorage : CookiesStorage

358

359

/**

360

* Cookie storage with constant cookies

361

*/

362

class ConstantCookiesStorage(vararg cookies: Cookie) : CookiesStorage

363

```

364

365

**Usage Examples:**

366

367

```kotlin

368

import io.ktor.client.*

369

import io.ktor.client.plugins.cookies.*

370

import io.ktor.http.*

371

372

val client = HttpClient {

373

install(HttpCookies) {

374

storage = AcceptAllCookiesStorage()

375

}

376

}

377

378

// Custom cookie storage

379

val customStorage = ConstantCookiesStorage(

380

Cookie("session", "abc123", domain = "example.com"),

381

Cookie("preferences", "theme=dark", domain = "example.com")

382

)

383

384

val clientWithCustomCookies = HttpClient {

385

install(HttpCookies) {

386

storage = customStorage

387

}

388

}

389

```

390

391

### HttpCache Plugin

392

393

Provides HTTP response caching functionality.

394

395

```kotlin { .api }

396

/**

397

* HTTP cache plugin for response caching

398

*/

399

val HttpCache: ClientPlugin<HttpCache.Config>

400

401

class HttpCache {

402

/**

403

* Configuration for HTTP cache plugin

404

*/

405

class Config {

406

/** Whether the cache is shared between multiple clients */

407

var isShared: Boolean = false

408

409

/**

410

* Configure public cache storage

411

* @param storage Cache storage implementation

412

*/

413

fun publicStorage(storage: CacheStorage)

414

415

/**

416

* Configure private cache storage

417

* @param storage Cache storage implementation

418

*/

419

fun privateStorage(storage: CacheStorage)

420

}

421

}

422

423

/**

424

* Interface for cache storage implementations

425

*/

426

interface CacheStorage {

427

/**

428

* Find cached entry for URL and vary headers

429

* @param url Request URL

430

* @param vary Vary header values

431

* @return Cached entry or null if not found

432

*/

433

suspend fun find(url: Url, vary: Map<String, String>): HttpCacheEntry?

434

435

/**

436

* Find all cached entries for URL

437

* @param url Request URL

438

* @return Set of cached entries

439

*/

440

suspend fun findAll(url: Url): Set<HttpCacheEntry>

441

442

/**

443

* Store cache entry

444

* @param url Request URL

445

* @param data Cache entry to store

446

*/

447

suspend fun store(url: Url, data: HttpCacheEntry)

448

449

companion object {

450

/** Create unlimited cache storage */

451

fun Unlimited(): CacheStorage

452

453

/** Create disabled cache storage */

454

fun Disabled(): CacheStorage

455

}

456

}

457

458

/**

459

* HTTP cache entry

460

*/

461

data class HttpCacheEntry(

462

val url: Url,

463

val statusCode: HttpStatusCode,

464

val requestTime: GMTDate,

465

val responseTime: GMTDate,

466

val version: HttpProtocolVersion,

467

val expires: GMTDate,

468

val headers: Headers,

469

val body: ByteArray

470

)

471

```

472

473

**Usage Examples:**

474

475

```kotlin

476

import io.ktor.client.*

477

import io.ktor.client.plugins.cache.*

478

479

val client = HttpClient {

480

install(HttpCache) {

481

publicStorage(CacheStorage.Unlimited())

482

privateStorage(CacheStorage.Unlimited())

483

}

484

}

485

```

486

487

### BodyProgress Plugin

488

489

Tracks upload and download progress for request and response bodies.

490

491

```kotlin { .api }

492

/**

493

* Body progress plugin for tracking upload/download progress

494

*/

495

val BodyProgress: ClientPlugin<BodyProgress.Config>

496

497

class BodyProgress {

498

/**

499

* Configuration for body progress plugin

500

*/

501

class Config {

502

/**

503

* Register a progress listener

504

* @param listener Progress listener to register

505

*/

506

fun register(listener: ProgressListener)

507

}

508

}

509

510

/**

511

* Interface for progress listeners

512

*/

513

fun interface ProgressListener {

514

/**

515

* Called when progress is made

516

* @param bytesSentTotal Total bytes sent so far

517

* @param contentLength Total content length, null if unknown

518

*/

519

fun onProgress(bytesSentTotal: Long, contentLength: Long?)

520

}

521

```

522

523

**Usage Examples:**

524

525

```kotlin

526

import io.ktor.client.*

527

import io.ktor.client.plugins.*

528

import io.ktor.client.request.*

529

530

val client = HttpClient {

531

install(BodyProgress) {

532

register { bytesSentTotal, contentLength ->

533

val progress = if (contentLength != null) {

534

(bytesSentTotal * 100 / contentLength).toInt()

535

} else {

536

-1

537

}

538

println("Upload progress: $bytesSentTotal bytes ($progress%)")

539

}

540

}

541

}

542

543

// Progress will be tracked for this upload

544

val response = client.post("https://api.example.com/upload") {

545

setBody(largeFileContent)

546

}

547

```

548

549

## Advanced Plugins

550

551

### Server-Sent Events (SSE) Plugin

552

553

Provides Server-Sent Events support.

554

555

```kotlin { .api }

556

/**

557

* Server-Sent Events plugin

558

*/

559

val SSE: ClientPlugin<SSEConfig>

560

561

/**

562

* Configuration for SSE plugin

563

*/

564

class SSEConfig {

565

/** Reconnection time for failed connections */

566

var reconnectionTime: Duration? = null

567

568

/** Whether to show comment events */

569

var showCommentEvents: Boolean? = null

570

571

/** Whether to show retry events */

572

var showRetryEvents: Boolean? = null

573

574

/** Maximum reconnection attempts */

575

var maxReconnectionAttempts: Int = Int.MAX_VALUE

576

}

577

578

/**

579

* SSE session interface

580

*/

581

interface ClientSSESession {

582

/** Flow of incoming server-sent events */

583

val incoming: Flow<ServerSentEvent>

584

}

585

586

/**

587

* SSE session with deserialization support

588

*/

589

interface ClientSSESessionWithDeserialization {

590

/** Flow of incoming typed server-sent events */

591

val incoming: Flow<TypedServerSentEvent<String>>

592

593

/**

594

* Deserialize event data

595

* @param data Event data string

596

* @return Deserialized object of type T

597

*/

598

suspend inline fun <reified T> deserialize(data: String?): T?

599

}

600

601

/**

602

* Connect to Server-Sent Events endpoint

603

* @param host Server host

604

* @param port Server port

605

* @param path URL path

606

* @param request Request configuration

607

* @param block SSE session block

608

*/

609

suspend fun HttpClient.sse(

610

host: String = "localhost",

611

port: Int = DEFAULT_PORT,

612

path: String = "/",

613

request: HttpRequestBuilder.() -> Unit = {},

614

block: suspend ClientSSESession.() -> Unit

615

)

616

617

/**

618

* Connect to Server-Sent Events endpoint with deserialization

619

* @param request Request configuration

620

* @param deserialize Deserialization function

621

* @param block SSE session block

622

*/

623

suspend fun <T> HttpClient.sse(

624

request: HttpRequestBuilder.() -> Unit,

625

deserialize: (TypeInfo, String) -> Any?,

626

block: suspend ClientSSESessionWithDeserialization.() -> Unit

627

)

628

```

629

630

**Usage Examples:**

631

632

```kotlin

633

import io.ktor.client.*

634

import io.ktor.client.plugins.sse.*

635

import kotlinx.coroutines.flow.collect

636

637

val client = HttpClient {

638

install(SSE) {

639

reconnectionTime = 3.seconds

640

showCommentEvents = true

641

}

642

}

643

644

// Connect to SSE endpoint

645

client.sse(

646

host = "api.example.com",

647

path = "/events"

648

) {

649

incoming.collect { event ->

650

println("Received: ${event.data}")

651

}

652

}

653

```

654

655

### WebSockets Plugin

656

657

Provides WebSocket client support.

658

659

```kotlin { .api }

660

/**

661

* WebSockets plugin

662

*/

663

val WebSockets: ClientPlugin<WebSocketConfig>

664

665

/**

666

* Connect to WebSocket endpoint

667

* @param method HTTP method for handshake

668

* @param host Server host

669

* @param port Server port

670

* @param path URL path

671

* @param request Request configuration

672

* @param block WebSocket session block

673

*/

674

suspend fun HttpClient.webSocket(

675

method: HttpMethod = HttpMethod.Get,

676

host: String = "localhost",

677

port: Int = DEFAULT_PORT,

678

path: String = "/",

679

request: HttpRequestBuilder.() -> Unit = {},

680

block: suspend DefaultClientWebSocketSession.() -> Unit

681

)

682

683

/**

684

* Connect to WebSocket endpoint with URL string

685

* @param urlString WebSocket URL

686

* @param request Request configuration

687

* @param block WebSocket session block

688

*/

689

suspend fun HttpClient.webSocket(

690

urlString: String,

691

request: HttpRequestBuilder.() -> Unit = {},

692

block: suspend DefaultClientWebSocketSession.() -> Unit

693

)

694

695

/**

696

* Connect to secure WebSocket endpoint

697

*/

698

suspend fun HttpClient.wss(

699

host: String = "localhost",

700

port: Int = DEFAULT_PORT,

701

path: String = "/",

702

request: HttpRequestBuilder.() -> Unit = {},

703

block: suspend DefaultClientWebSocketSession.() -> Unit

704

)

705

```

706

707

**Usage Examples:**

708

709

```kotlin

710

import io.ktor.client.*

711

import io.ktor.client.plugins.websocket.*

712

import io.ktor.websocket.*

713

714

val client = HttpClient {

715

install(WebSockets)

716

}

717

718

// Connect to WebSocket

719

client.webSocket(

720

host = "echo.websocket.org",

721

port = 80,

722

path = "/"

723

) {

724

// Send message

725

send("Hello WebSocket!")

726

727

// Receive messages

728

for (frame in incoming) {

729

when (frame) {

730

is Frame.Text -> {

731

println("Received: ${frame.readText()}")

732

}

733

is Frame.Binary -> {

734

println("Received binary data")

735

}

736

is Frame.Close -> {

737

println("Connection closed")

738

break

739

}

740

}

741

}

742

}

743

```

744

745

## Types

746

747

Plugin system related types:

748

749

```kotlin { .api }

750

/**

751

* Attribute key for identifying plugins

752

*/

753

data class AttributeKey<T>(val name: String)

754

755

/**

756

* Exception for SSE operations

757

*/

758

class SSEClientException(

759

response: HttpResponse?,

760

cause: Throwable?,

761

message: String?

762

) : IllegalStateException()

763

764

/**

765

* Server-sent event data

766

*/

767

data class ServerSentEvent(

768

val data: String?,

769

val event: String?,

770

val id: String?,

771

val retry: Long?,

772

val comments: String?

773

)

774

775

/**

776

* Typed server-sent event

777

*/

778

data class TypedServerSentEvent<T>(

779

val data: T?,

780

val event: String?,

781

val id: String?,

782

val retry: Long?

783

)

784

785

/**

786

* Cookie data class

787

*/

788

data class Cookie(

789

val name: String,

790

val value: String,

791

val encoding: CookieEncoding = CookieEncoding.URI_ENCODING,

792

val maxAge: Int = 0,

793

val expires: GMTDate? = null,

794

val domain: String? = null,

795

val path: String? = null,

796

val secure: Boolean = false,

797

val httpOnly: Boolean = false,

798

val extensions: Map<String, String?> = emptyMap()

799

)

800

```