or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-configuration.mdcontent-handling.mdengine-architecture.mdevents-monitoring.mdhttp-statement.mdindex.mdplugin-system.mdrequest-building.mdresponse-handling.mdwebsocket-support.md

plugin-system.mddocs/

0

# Plugin System

1

2

Extensible plugin architecture for adding cross-cutting concerns like authentication, logging, caching, content negotiation, and custom request/response processing to the HTTP client.

3

4

## Capabilities

5

6

### HttpClientPlugin Interface

7

8

Core plugin interface for extending client functionality.

9

10

```kotlin { .api }

11

/**

12

* Interface for HTTP client plugins

13

* @param TConfig - Type of plugin configuration

14

* @param TPlugin - Type of plugin instance

15

*/

16

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

17

/** Unique key for the plugin */

18

val key: AttributeKey<TPlugin>

19

20

/** Prepare plugin instance from configuration */

21

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

22

23

/** Install plugin into HTTP client */

24

fun install(plugin: TPlugin, scope: HttpClient)

25

}

26

```

27

28

### Plugin Creation

29

30

Create custom plugins using the plugin builder DSL.

31

32

```kotlin { .api }

33

/**

34

* Create a custom HTTP client plugin

35

* @param name - Plugin name

36

* @param createConfiguration - Configuration factory function

37

* @param body - Plugin implementation block

38

*/

39

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

40

name: String,

41

createConfiguration: () -> TConfig,

42

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

43

): HttpClientPlugin<TConfig, TPlugin>

44

45

/**

46

* Create a plugin with default configuration

47

*/

48

fun <TPlugin : Any> createClientPlugin(

49

name: String,

50

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

51

): HttpClientPlugin<Unit, TPlugin>

52

53

/**

54

* Plugin builder for creating custom plugins

55

*/

56

class ClientPluginBuilder<TConfig : Any> {

57

/** Plugin configuration */

58

val pluginConfig: TConfig

59

60

/** Setup hook for plugin initialization */

61

fun onSetup(block: suspend () -> Unit)

62

63

/** Hook for transforming requests */

64

fun transformRequest(block: suspend (HttpRequestBuilder) -> Unit)

65

66

/** Hook for transforming responses */

67

fun transformResponse(block: suspend (HttpResponse) -> Unit)

68

69

/** Hook for request processing */

70

fun onRequest(block: suspend (HttpRequestBuilder, content: OutgoingContent) -> Unit)

71

72

/** Hook for response processing */

73

fun onResponse(block: suspend (HttpResponse) -> Unit)

74

75

/** Hook for call completion */

76

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

77

}

78

```

79

80

**Usage Examples:**

81

82

```kotlin

83

// Simple logging plugin

84

val RequestLoggingPlugin = createClientPlugin("RequestLogging") {

85

onRequest { request, _ ->

86

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

87

}

88

89

onResponse { response ->

90

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

91

}

92

}

93

94

// Plugin with configuration

95

data class RetryConfig(

96

var maxRetries: Int = 3,

97

var delayMillis: Long = 1000

98

)

99

100

val RetryPlugin = createClientPlugin("Retry", ::RetryConfig) { config ->

101

onRequest { request, content ->

102

repeat(config.maxRetries) { attempt ->

103

try {

104

// Attempt request

105

return@onRequest

106

} catch (e: Exception) {

107

if (attempt == config.maxRetries - 1) throw e

108

delay(config.delayMillis)

109

}

110

}

111

}

112

}

113

114

// Install plugins

115

val client = HttpClient {

116

install(RequestLoggingPlugin)

117

install(RetryPlugin) {

118

maxRetries = 5

119

delayMillis = 2000

120

}

121

}

122

```

123

124

### Built-in Plugins

125

126

Core plugins provided by Ktor.

127

128

```kotlin { .api }

129

/**

130

* Default request configuration plugin

131

*/

132

object DefaultRequest : HttpClientPlugin<DefaultRequest.Config, DefaultRequest> {

133

class Config {

134

var host: String? = null

135

var port: Int? = null

136

val headers: HeadersBuilder = HeadersBuilder()

137

val url: URLBuilder = URLBuilder()

138

}

139

}

140

141

/**

142

* HTTP redirect handling plugin

143

*/

144

object HttpRedirect : HttpClientPlugin<HttpRedirect.Config, HttpRedirect> {

145

class Config {

146

var checkHttpMethod: Boolean = true

147

var allowHttpsRedirect: Boolean = false

148

var maxJumps: Int = 20

149

}

150

}

151

152

/**

153

* Request retry plugin

154

*/

155

object HttpRequestRetry : HttpClientPlugin<HttpRequestRetry.Config, HttpRequestRetry> {

156

class Config {

157

var maxRetries: Int = 0

158

var retryOnServerErrors: Boolean = true

159

var retryOnTimeout: Boolean = true

160

var exponentialDelay: Boolean = false

161

var constantDelay: Duration? = null

162

}

163

}

164

165

/**

166

* HTTP timeout configuration plugin

167

*/

168

object HttpTimeout : HttpClientPlugin<HttpTimeout.Config, HttpTimeout> {

169

class Config {

170

var requestTimeoutMillis: Long? = null

171

var connectTimeoutMillis: Long? = null

172

var socketTimeoutMillis: Long? = null

173

}

174

}

175

176

/**

177

* User-Agent header plugin

178

*/

179

object UserAgent : HttpClientPlugin<UserAgent.Config, UserAgent> {

180

class Config {

181

var agent: String? = null

182

}

183

}

184

185

/**

186

* Response observer plugin

187

*/

188

object ResponseObserver : HttpClientPlugin<ResponseObserver.Config, ResponseObserver> {

189

class Config {

190

val responseHandlers: MutableList<suspend (HttpResponse) -> Unit> = mutableListOf()

191

}

192

}

193

```

194

195

**Usage Examples:**

196

197

```kotlin

198

val client = HttpClient {

199

// Configure default request parameters

200

install(DefaultRequest) {

201

host = "api.example.com"

202

port = 443

203

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

204

url {

205

protocol = URLProtocol.HTTPS

206

path("v1/")

207

}

208

}

209

210

// Configure redirects

211

install(HttpRedirect) {

212

checkHttpMethod = false

213

allowHttpsRedirect = true

214

maxJumps = 10

215

}

216

217

// Configure retries

218

install(HttpRequestRetry) {

219

maxRetries = 3

220

retryOnServerErrors = true

221

exponentialDelay = true

222

}

223

224

// Configure timeouts

225

install(HttpTimeout) {

226

requestTimeoutMillis = 30000

227

connectTimeoutMillis = 5000

228

socketTimeoutMillis = 10000

229

}

230

231

// Set user agent

232

install(UserAgent) {

233

agent = "MyApp/2.0 (Kotlin)"

234

}

235

236

// Observe responses

237

install(ResponseObserver) {

238

onResponse { response ->

239

if (!response.status.isSuccess()) {

240

println("Request failed: ${response.status}")

241

}

242

}

243

}

244

}

245

```

246

247

### Plugin Hooks

248

249

Available hooks for plugin development.

250

251

```kotlin { .api }

252

/**

253

* Setup hook - called during plugin installation

254

*/

255

class SetupHook {

256

suspend fun proceed()

257

}

258

259

/**

260

* Transform request hook - modify outgoing requests

261

*/

262

class TransformRequestHook {

263

suspend fun transformRequest(transform: suspend (HttpRequestBuilder) -> Unit)

264

}

265

266

/**

267

* Transform response hook - modify incoming responses

268

*/

269

class TransformResponseHook {

270

suspend fun transformResponse(transform: suspend (HttpResponse) -> HttpResponse)

271

}

272

273

/**

274

* Request hook - intercept request sending

275

*/

276

class OnRequestHook {

277

suspend fun onRequest(

278

handler: suspend (request: HttpRequestBuilder, content: OutgoingContent) -> Unit

279

)

280

}

281

282

/**

283

* Response hook - intercept response receiving

284

*/

285

class OnResponseHook {

286

suspend fun onResponse(

287

handler: suspend (response: HttpResponse) -> Unit

288

)

289

}

290

291

/**

292

* Close hook - cleanup when client closes

293

*/

294

class OnCloseHook {

295

suspend fun onClose(handler: suspend () -> Unit)

296

}

297

```

298

299

### Plugin Installation

300

301

Install and configure plugins in the client.

302

303

```kotlin { .api }

304

/**

305

* Install plugin with configuration

306

*/

307

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

308

plugin: HttpClientPlugin<TConfig, TPlugin>,

309

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

310

)

311

312

/**

313

* Install plugin by key

314

*/

315

fun <T : Any> HttpClientConfig<*>.install(

316

key: AttributeKey<T>,

317

block: () -> T

318

)

319

320

/**

321

* Get installed plugin

322

*/

323

fun <T : Any> HttpClient.plugin(plugin: HttpClientPlugin<*, T>): T

324

fun <T : Any> HttpClient.plugin(key: AttributeKey<T>): T

325

326

/**

327

* Check if plugin is installed

328

*/

329

fun <T : Any> HttpClient.pluginOrNull(plugin: HttpClientPlugin<*, T>): T?

330

fun <T : Any> HttpClient.pluginOrNull(key: AttributeKey<T>): T?

331

```

332

333

**Usage Examples:**

334

335

```kotlin

336

// Install with configuration

337

val client = HttpClient {

338

install(HttpTimeout) {

339

requestTimeoutMillis = 15000

340

connectTimeoutMillis = 3000

341

}

342

}

343

344

// Access installed plugin

345

val timeoutPlugin = client.plugin(HttpTimeout)

346

347

// Check if plugin is installed

348

val retryPlugin = client.pluginOrNull(HttpRequestRetry)

349

if (retryPlugin != null) {

350

println("Retry plugin is installed")

351

}

352

```

353

354

### Custom Plugin Examples

355

356

Examples of creating custom plugins for specific use cases.

357

358

```kotlin { .api }

359

// Request ID plugin

360

data class RequestIdConfig(

361

var headerName: String = "X-Request-ID",

362

var generateId: () -> String = { UUID.randomUUID().toString() }

363

)

364

365

val RequestIdPlugin = createClientPlugin("RequestId", ::RequestIdConfig) { config ->

366

onRequest { request, _ ->

367

if (!request.headers.contains(config.headerName)) {

368

request.header(config.headerName, config.generateId())

369

}

370

}

371

}

372

373

// Response time measurement plugin

374

class ResponseTimeConfig {

375

val handlers: MutableList<(Long) -> Unit> = mutableListOf()

376

377

fun onResponseTime(handler: (Long) -> Unit) {

378

handlers.add(handler)

379

}

380

}

381

382

val ResponseTimePlugin = createClientPlugin("ResponseTime", ::ResponseTimeConfig) { config ->

383

var startTime: Long = 0

384

385

onRequest { _, _ ->

386

startTime = System.currentTimeMillis()

387

}

388

389

onResponse { _ ->

390

val responseTime = System.currentTimeMillis() - startTime

391

config.handlers.forEach { it(responseTime) }

392

}

393

}

394

```

395

396

### Plugin Phases

397

398

Understanding plugin execution phases and order.

399

400

```kotlin { .api }

401

/**

402

* Plugin phases determine execution order

403

*/

404

enum class PipelinePhase {

405

Before, // Execute before built-in processing

406

Transform, // Transform request/response

407

State, // Modify client state

408

Monitoring, // Monitor and observe

409

Send, // Send request

410

Receive, // Receive response

411

After // Execute after built-in processing

412

}

413

```

414

415

## Types

416

417

```kotlin { .api }

418

// Plugin system types

419

class AttributeKey<T>(val name: String)

420

421

class Attributes {

422

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

423

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

424

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

425

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

426

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

427

}

428

429

// Plugin configuration types

430

interface ClientPluginConfig

431

432

// Hook types for pipeline processing

433

interface PipelineContext<TSubject : Any, TContext : Any> {

434

val context: TContext

435

val subject: TSubject

436

suspend fun proceed(): TSubject

437

suspend fun proceedWith(subject: TSubject): TSubject

438

}

439

```