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
```