or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-configuration.mdcookie-management.mdforms-and-uploads.mdhttp-caching.mdhttp-requests.mdindex.mdplugin-system.mdresponse-handling.mdserver-sent-events.mdwebsockets.md

plugin-system.mddocs/

0

# Plugin System

1

2

Comprehensive plugin architecture with built-in plugins for common functionality and custom plugin creation capabilities.

3

4

## Capabilities

5

6

### Core Plugin Interface

7

8

Base plugin interface and plugin management system.

9

10

```kotlin { .api }

11

/**

12

* Core plugin interface for HttpClient

13

*/

14

interface HttpClientPlugin<TBuilder : Any, TPlugin : Any> {

15

/** Unique key for this plugin type */

16

val key: AttributeKey<TPlugin>

17

18

/**

19

* Prepare plugin configuration

20

* @param block Configuration block

21

* @returns Configured plugin instance

22

*/

23

fun prepare(block: TBuilder.() -> Unit): TPlugin

24

25

/**

26

* Install plugin into HttpClient

27

* @param plugin Prepared plugin instance

28

* @param scope Target HttpClient

29

*/

30

fun install(plugin: TPlugin, scope: HttpClient)

31

}

32

33

/**

34

* Create custom client plugin

35

* @param name Plugin name

36

* @param createConfiguration Configuration factory

37

* @param body Plugin implementation

38

* @returns HttpClientPlugin instance

39

*/

40

fun <TConfig : Any, TPlugin : Any> createClientPlugin(

41

name: String,

42

createConfiguration: () -> TConfig,

43

body: ClientPluginBuilder<TConfig>.() -> TPlugin

44

): HttpClientPlugin<TConfig, TPlugin>

45

```

46

47

### Built-in Plugins

48

49

Core plugins provided by Ktor for common HTTP client functionality.

50

51

```kotlin { .api }

52

/**

53

* HTTP timeout configuration plugin

54

*/

55

object HttpTimeout : HttpClientPlugin<HttpTimeoutConfig, HttpTimeoutConfig> {

56

override val key: AttributeKey<HttpTimeoutConfig>

57

58

/**

59

* Timeout configuration

60

*/

61

class HttpTimeoutConfig {

62

/** Request timeout in milliseconds */

63

var requestTimeoutMillis: Long? = null

64

65

/** Connection timeout in milliseconds */

66

var connectTimeoutMillis: Long? = null

67

68

/** Socket timeout in milliseconds */

69

var socketTimeoutMillis: Long? = null

70

}

71

}

72

73

/**

74

* HTTP redirect handling plugin

75

*/

76

object HttpRedirect : HttpClientPlugin<HttpRedirectConfig, HttpRedirectConfig> {

77

override val key: AttributeKey<HttpRedirectConfig>

78

79

/**

80

* Redirect configuration

81

*/

82

class HttpRedirectConfig {

83

/** Check HTTP method on redirect (default: true) */

84

var checkHttpMethod: Boolean = true

85

86

/** Allow HTTPS to HTTP downgrade (default: false) */

87

var allowHttpsDowngrade: Boolean = false

88

89

/** Maximum number of redirects (default: 20) */

90

var maxJumps: Int = 20

91

}

92

}

93

94

/**

95

* Cookie management plugin

96

*/

97

object HttpCookies : HttpClientPlugin<HttpCookiesConfig, HttpCookiesConfig> {

98

override val key: AttributeKey<HttpCookiesConfig>

99

100

/**

101

* Cookies configuration

102

*/

103

class HttpCookiesConfig {

104

/** Cookie storage implementation */

105

var storage: CookiesStorage = AcceptAllCookiesStorage()

106

}

107

}

108

109

/**

110

* Response validation plugin

111

*/

112

object HttpCallValidator : HttpClientPlugin<HttpCallValidatorConfig, HttpCallValidatorConfig> {

113

override val key: AttributeKey<HttpCallValidatorConfig>

114

115

/**

116

* Validation configuration

117

*/

118

class HttpCallValidatorConfig {

119

/** Validate response status codes */

120

var validateResponse: (suspend (HttpResponse) -> Unit)? = null

121

122

/** Handle response exceptions */

123

var handleResponseException: (suspend (exception: Throwable) -> Unit)? = null

124

}

125

}

126

127

/**

128

* Default request configuration plugin

129

*/

130

object DefaultRequest : HttpClientPlugin<DefaultRequestConfig, DefaultRequestConfig> {

131

override val key: AttributeKey<DefaultRequestConfig>

132

133

/**

134

* Default request configuration

135

*/

136

class DefaultRequestConfig {

137

/** Default request builder configuration */

138

var block: HttpRequestBuilder.() -> Unit = {}

139

}

140

}

141

142

/**

143

* User agent header plugin

144

*/

145

object UserAgent : HttpClientPlugin<UserAgentConfig, UserAgentConfig> {

146

override val key: AttributeKey<UserAgentConfig>

147

148

/**

149

* User agent configuration

150

*/

151

class UserAgentConfig {

152

/** User agent string */

153

var agent: String = ""

154

}

155

}

156

157

/**

158

* Request retry plugin

159

*/

160

object HttpRequestRetry : HttpClientPlugin<HttpRequestRetryConfig, HttpRequestRetryConfig> {

161

override val key: AttributeKey<HttpRequestRetryConfig>

162

163

/**

164

* Retry configuration

165

*/

166

class HttpRequestRetryConfig {

167

/** Maximum retry attempts */

168

var maxRetryAttempts: Int = 3

169

170

/** Retry condition */

171

var retryIf: (suspend (request: HttpRequestBuilder, response: HttpResponse) -> Boolean)? = null

172

173

/** Delay between retries */

174

var delayMillis: (retry: Int) -> Long = { retry -> retry * 1000L }

175

}

176

}

177

```

178

179

**Usage Examples:**

180

181

```kotlin

182

val client = HttpClient {

183

// Install timeout plugin

184

install(HttpTimeout) {

185

requestTimeoutMillis = 30000

186

connectTimeoutMillis = 10000

187

socketTimeoutMillis = 15000

188

}

189

190

// Install redirect plugin

191

install(HttpRedirect) {

192

checkHttpMethod = true

193

allowHttpsDowngrade = false

194

maxJumps = 10

195

}

196

197

// Install cookies plugin

198

install(HttpCookies) {

199

storage = AcceptAllCookiesStorage()

200

}

201

202

// Install response validation

203

install(HttpCallValidator) {

204

validateResponse { response ->

205

when (response.status.value) {

206

in 300..399 -> throw RedirectResponseException(response, "Redirects not allowed")

207

in 400..499 -> throw ClientRequestException(response, "Client error")

208

in 500..599 -> throw ServerResponseException(response, "Server error")

209

}

210

}

211

212

handleResponseException { exception ->

213

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

214

}

215

}

216

217

// Install default request configuration

218

install(DefaultRequest) {

219

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

220

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

221

url("https://api.example.com/")

222

}

223

224

// Install user agent

225

install(UserAgent) {

226

agent = "MyApp/1.0 (Kotlin HTTP Client)"

227

}

228

229

// Install retry plugin

230

install(HttpRequestRetry) {

231

maxRetryAttempts = 3

232

retryIf { _, response ->

233

response.status.value >= 500

234

}

235

delayMillis { retry ->

236

retry * 2000L // Exponential backoff

237

}

238

}

239

}

240

```

241

242

### Custom Plugin Creation

243

244

Create custom plugins using the plugin builder DSL.

245

246

```kotlin { .api }

247

/**

248

* Plugin builder for creating custom plugins

249

*/

250

class ClientPluginBuilder<TConfig : Any> {

251

/**

252

* Hook into request pipeline phase

253

* @param phase Pipeline phase to intercept

254

* @param block Interceptor implementation

255

*/

256

fun onRequest(phase: PipelinePhase, block: suspend (HttpRequestBuilder, TConfig) -> Unit)

257

258

/**

259

* Hook into response pipeline phase

260

* @param phase Pipeline phase to intercept

261

* @param block Interceptor implementation

262

*/

263

fun onResponse(phase: PipelinePhase, block: suspend (HttpResponse, TConfig) -> Unit)

264

265

/**

266

* Hook into call processing

267

* @param block Call interceptor implementation

268

*/

269

fun onCall(block: suspend (HttpClientCall, TConfig) -> Unit)

270

271

/**

272

* Add cleanup logic when client is closed

273

* @param block Cleanup implementation

274

*/

275

fun onClose(block: (TConfig) -> Unit)

276

}

277

278

/**

279

* Plugin configuration attribute key

280

*/

281

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

282

companion object {

283

fun <T> create(name: String): AttributeKey<T>

284

}

285

}

286

```

287

288

**Usage Examples:**

289

290

```kotlin

291

// Custom logging plugin

292

val CustomLogging = createClientPlugin("CustomLogging", ::LoggingConfig) {

293

val config = pluginConfig

294

295

onRequest(HttpRequestPipeline.Before) { request, _ ->

296

if (config.logRequests) {

297

println("→ ${request.method.value} ${request.url}")

298

request.headers.forEach { name, values ->

299

println(" $name: ${values.joinToString()}")

300

}

301

}

302

}

303

304

onResponse(HttpResponsePipeline.Receive) { response, _ ->

305

if (config.logResponses) {

306

println("← ${response.status}")

307

response.headers.forEach { name, values ->

308

println(" $name: ${values.joinToString()}")

309

}

310

}

311

}

312

313

onClose { config ->

314

if (config.logCleanup) {

315

println("Logging plugin closed")

316

}

317

}

318

}

319

320

data class LoggingConfig(

321

var logRequests: Boolean = true,

322

var logResponses: Boolean = true,

323

var logCleanup: Boolean = false

324

)

325

326

// Use custom plugin

327

val client = HttpClient {

328

install(CustomLogging) {

329

logRequests = true

330

logResponses = true

331

logCleanup = true

332

}

333

}

334

335

// Custom authentication plugin

336

val BearerAuth = createClientPlugin("BearerAuth", ::BearerAuthConfig) {

337

val config = pluginConfig

338

339

onRequest(HttpRequestPipeline.State) { request, _ ->

340

val token = config.tokenProvider()

341

if (token != null) {

342

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

343

}

344

}

345

346

onResponse(HttpResponsePipeline.Receive) { response, _ ->

347

if (response.status == HttpStatusCode.Unauthorized) {

348

config.onUnauthorized?.invoke()

349

}

350

}

351

}

352

353

data class BearerAuthConfig(

354

var tokenProvider: () -> String? = { null },

355

var onUnauthorized: (() -> Unit)? = null

356

)

357

358

// Use authentication plugin

359

val client = HttpClient {

360

install(BearerAuth) {

361

tokenProvider = { getStoredToken() }

362

onUnauthorized = { refreshToken() }

363

}

364

}

365

```

366

367

### Plugin Hooks and Lifecycle

368

369

Plugin hooks for intercepting various stages of request/response processing.

370

371

```kotlin { .api }

372

/**

373

* Common plugin hooks

374

*/

375

object CommonHooks {

376

/** Before request is sent */

377

val BeforeRequest: ClientHook<suspend (HttpRequestBuilder) -> Unit>

378

379

/** After response is received */

380

val AfterResponse: ClientHook<suspend (HttpResponse) -> Unit>

381

382

/** On request exception */

383

val OnRequestException: ClientHook<suspend (Throwable) -> Unit>

384

385

/** On response exception */

386

val OnResponseException: ClientHook<suspend (Throwable) -> Unit>

387

}

388

389

/**

390

* Plugin hook interface

391

*/

392

interface ClientHook<T> {

393

val name: String

394

fun install(client: HttpClient, handler: T)

395

}

396

397

/**

398

* Plugin instance wrapper

399

*/

400

class ClientPluginInstance<TConfig : Any> {

401

val config: TConfig

402

val plugin: HttpClientPlugin<*, *>

403

404

fun close()

405

}

406

```

407

408

**Usage Examples:**

409

410

```kotlin

411

// Using common hooks

412

val client = HttpClient {

413

install("RequestLogger") {

414

CommonHooks.BeforeRequest.install(this) { request ->

415

println("Sending request to: ${request.url}")

416

}

417

418

CommonHooks.AfterResponse.install(this) { response ->

419

println("Received response: ${response.status}")

420

}

421

422

CommonHooks.OnRequestException.install(this) { exception ->

423

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

424

}

425

}

426

}

427

428

// Advanced plugin with multiple hooks

429

val AdvancedMetrics = createClientPlugin("AdvancedMetrics", ::MetricsConfig) {

430

val config = pluginConfig

431

val requestTimes = mutableMapOf<HttpClientCall, Long>()

432

433

// Track request start time

434

onRequest(HttpRequestPipeline.Before) { request, _ ->

435

requestTimes[request.executionContext] = System.currentTimeMillis()

436

}

437

438

// Calculate and log request duration

439

onResponse(HttpResponsePipeline.Receive) { response, _ ->

440

val startTime = requestTimes.remove(response.call)

441

if (startTime != null) {

442

val duration = System.currentTimeMillis() - startTime

443

config.onRequestComplete(response.call.request.url.toString(), duration, response.status)

444

}

445

}

446

447

// Handle request failures

448

onCall { call, _ ->

449

try {

450

proceed()

451

} catch (e: Exception) {

452

val startTime = requestTimes.remove(call)

453

if (startTime != null) {

454

val duration = System.currentTimeMillis() - startTime

455

config.onRequestFailed(call.request.url.toString(), duration, e)

456

}

457

throw e

458

}

459

}

460

}

461

462

data class MetricsConfig(

463

var onRequestComplete: (url: String, duration: Long, status: HttpStatusCode) -> Unit = { _, _, _ -> },

464

var onRequestFailed: (url: String, duration: Long, exception: Throwable) -> Unit = { _, _, _ -> }

465

)

466

```

467

468

### Plugin Configuration and Management

469

470

Managing plugin configurations and accessing installed plugins.

471

472

```kotlin { .api }

473

/**

474

* Access installed plugin configuration

475

* @param plugin Plugin to get configuration for

476

* @returns Plugin configuration instance

477

*/

478

fun <TBuilder : Any, TPlugin : Any> HttpClient.plugin(

479

plugin: HttpClientPlugin<TBuilder, TPlugin>

480

): TPlugin

481

482

/**

483

* Check if plugin is installed

484

* @param plugin Plugin to check

485

* @returns True if plugin is installed

486

*/

487

fun HttpClient.isPluginInstalled(plugin: HttpClientPlugin<*, *>): Boolean

488

489

/**

490

* Get all installed plugins

491

* @returns Map of plugin keys to plugin instances

492

*/

493

fun HttpClient.installedPlugins(): Map<AttributeKey<*>, Any>

494

```

495

496

**Usage Examples:**

497

498

```kotlin

499

val client = HttpClient {

500

install(HttpTimeout) {

501

requestTimeoutMillis = 30000

502

}

503

504

install(HttpCookies) {

505

storage = AcceptAllCookiesStorage()

506

}

507

}

508

509

// Access plugin configuration

510

val timeoutConfig = client.plugin(HttpTimeout)

511

println("Request timeout: ${timeoutConfig.requestTimeoutMillis}")

512

513

val cookiesConfig = client.plugin(HttpCookies)

514

val cookieStorage = cookiesConfig.storage

515

516

// Check if plugin is installed

517

if (client.isPluginInstalled(HttpTimeout)) {

518

println("Timeout plugin is installed")

519

}

520

521

// Get all installed plugins

522

val allPlugins = client.installedPlugins()

523

allPlugins.forEach { (key, instance) ->

524

println("Plugin: ${key.name}")

525

}

526

```

527

528

## Types

529

530

### Plugin System Types

531

532

```kotlin { .api }

533

/**

534

* Pipeline phase for request/response processing

535

*/

536

class PipelinePhase(val name: String) {

537

companion object {

538

fun create(name: String): PipelinePhase

539

}

540

}

541

542

/**

543

* Request pipeline phases

544

*/

545

object HttpRequestPipeline {

546

val Before: PipelinePhase

547

val State: PipelinePhase

548

val Transform: PipelinePhase

549

val Render: PipelinePhase

550

val Send: PipelinePhase

551

}

552

553

/**

554

* Response pipeline phases

555

*/

556

object HttpResponsePipeline {

557

val Receive: PipelinePhase

558

val Parse: PipelinePhase

559

val Transform: PipelinePhase

560

}

561

562

/**

563

* Send pipeline phases

564

*/

565

object HttpSendPipeline {

566

val Before: PipelinePhase

567

val State: PipelinePhase

568

val Monitoring: PipelinePhase

569

val Engine: PipelinePhase

570

val Receive: PipelinePhase

571

}

572

573

/**

574

* Attributes collection for plugin data

575

*/

576

interface Attributes {

577

fun <T : Any> get(key: AttributeKey<T>): T

578

fun <T : Any> getOrNull(key: AttributeKey<T>): T?

579

fun <T : Any> put(key: AttributeKey<T>, value: T)

580

fun <T : Any> remove(key: AttributeKey<T>): T?

581

fun <T : Any> computeIfAbsent(key: AttributeKey<T>, block: () -> T): T

582

583

fun allKeys(): List<AttributeKey<*>>

584

585

companion object {

586

fun concurrent(): Attributes

587

}

588

}

589

```

590

591

### Exception Types

592

593

```kotlin { .api }

594

/**

595

* Plugin installation exception

596

*/

597

class PluginInstallationException(

598

message: String,

599

cause: Throwable? = null

600

) : Exception(message, cause)

601

602

/**

603

* Plugin configuration exception

604

*/

605

class PluginConfigurationException(

606

message: String,

607

cause: Throwable? = null

608

) : Exception(message, cause)

609

```