or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

content-handling.mdcore-client.mdengine-configuration.mdindex.mdplugin-system.mdrequest-building.mdresponse-processing.mdserver-sent-events.mdwebsocket-support.md
tile.json

plugin-system.mddocs/

Plugin System

Extensible plugin architecture for adding functionality like authentication, caching, logging, and custom interceptors. The plugin system provides a standardized way to extend HttpClient capabilities with both built-in and custom plugins.

Capabilities

HttpClientPlugin Interface

Base interface for all client plugins providing installation and configuration mechanisms.

/**
 * Base interface for client plugins
 */
interface HttpClientPlugin<TConfig : Any, TPlugin : Any> {
    /** Unique key for identifying the plugin */
    val key: AttributeKey<TPlugin>
    
    /**
     * Prepare a plugin instance with the given configuration
     */
    fun prepare(block: TConfig.() -> Unit = {}): TPlugin
    
    /**
     * Install the plugin into the specified client scope
     */
    fun install(plugin: TPlugin, scope: HttpClient)
}

Plugin Installation

Functions for installing and managing plugins in HttpClient instances.

/**
 * Install a plugin with configuration
 */
fun <TConfig : Any, TPlugin : Any> HttpClientConfig<*>.install(
    plugin: HttpClientPlugin<TConfig, TPlugin>,
    configure: TConfig.() -> Unit = {}
): TPlugin

/**
 * Install a custom interceptor with a string key
 */
fun HttpClientConfig<*>.install(
    key: String,
    block: HttpClient.() -> Unit
)

/**
 * Get an installed plugin instance (nullable)
 */
fun <TConfig : Any, TPlugin : Any> HttpClient.pluginOrNull(
    plugin: HttpClientPlugin<TConfig, TPlugin>
): TPlugin?

/**
 * Get an installed plugin instance (throws if not found)
 */
fun <TConfig : Any, TPlugin : Any> HttpClient.plugin(
    plugin: HttpClientPlugin<TConfig, TPlugin>
): TPlugin

Built-in Plugins

HttpTimeout Plugin

Request, connect, and socket timeout configuration.

/**
 * Plugin for configuring request timeouts
 */
object HttpTimeout : HttpClientPlugin<HttpTimeoutConfig, HttpTimeoutConfig> {
    override val key: AttributeKey<HttpTimeoutConfig>
}

/**
 * Configuration for HttpTimeout plugin
 */
class HttpTimeoutConfig {
    /** Request timeout in milliseconds */
    var requestTimeoutMillis: Long?
    
    /** Connection timeout in milliseconds */
    var connectTimeoutMillis: Long?
    
    /** Socket timeout in milliseconds */
    var socketTimeoutMillis: Long?
}

/**
 * Configure timeout for a specific request
 */
fun HttpRequestBuilder.timeout(block: HttpTimeoutConfig.() -> Unit)

HttpRedirect Plugin

Automatic redirect handling with customizable behavior.

/**
 * Plugin for handling HTTP redirects
 */
object HttpRedirect : HttpClientPlugin<HttpRedirectConfig, HttpRedirectConfig> {
    override val key: AttributeKey<HttpRedirectConfig>
}

/**
 * Configuration for HttpRedirect plugin
 */
class HttpRedirectConfig {
    /** Check HTTP method before following redirects */
    var checkHttpMethod: Boolean
    
    /** Allow HTTPS to HTTP downgrade during redirects */
    var allowHttpsDowngrade: Boolean
    
    /** Maximum number of redirects to follow */
    var maxJumps: Int
}

HttpCallValidator Plugin

Response validation and error handling.

/**
 * Plugin for validating HTTP responses
 */
object HttpCallValidator : HttpClientPlugin<HttpCallValidatorConfig, HttpCallValidatorConfig> {
    override val key: AttributeKey<HttpCallValidatorConfig>
}

/**
 * Configuration for HttpCallValidator plugin
 */
class HttpCallValidatorConfig {
    /**
     * Add a response validator
     */
    fun validateResponse(block: suspend (response: HttpResponse) -> Unit)
    
    /**
     * Add an exception handler
     */
    fun handleResponseExceptionWithRequest(
        block: suspend (exception: Throwable, request: HttpRequest) -> Unit
    )
    
    /** Enable/disable automatic status code validation */
    var expectSuccess: Boolean
}

UserAgent Plugin

User-Agent header management.

/**
 * Plugin for managing User-Agent header
 */
object UserAgent : HttpClientPlugin<UserAgentConfig, UserAgentConfig> {
    override val key: AttributeKey<UserAgentConfig>
}

/**
 * Configuration for UserAgent plugin
 */
class UserAgentConfig {
    /** User agent string */
    var agent: String
}

DefaultRequest Plugin

Default request configuration applied to all requests.

/**
 * Plugin for applying default request configuration
 */
object DefaultRequest : HttpClientPlugin<DefaultRequestBuilder, DefaultRequestBuilder> {
    override val key: AttributeKey<DefaultRequestBuilder>
}

/**
 * Builder for default request configuration
 */
class DefaultRequestBuilder : HttpRequestBuilder() {
    // Inherits all HttpRequestBuilder functionality
    // Configuration applied to every request
}

HttpSend Plugin

Request sending pipeline management.

/**
 * Plugin for managing request sending pipeline
 */
object HttpSend : HttpClientPlugin<HttpSendConfig, HttpSendInterceptor> {
    override val key: AttributeKey<HttpSendInterceptor>
}

/**
 * Configuration for HttpSend plugin
 */
class HttpSendConfig {
    /** Maximum number of send attempts */
    var maxSendCount: Int
}

SaveBody Plugin

Response body saving and replay functionality.

/**
 * Plugin for saving and replaying response bodies
 */
object SaveBody : HttpClientPlugin<SaveBodyConfig, SaveBodyConfig> {
    override val key: AttributeKey<SaveBodyConfig>
}

/**
 * Configuration for SaveBody plugin
 */
class SaveBodyConfig {
    /** Enable/disable body saving */
    var enable: Boolean
    
    /** Maximum body size to save */
    var maxBodySize: Long
}

BodyProgress Plugin

Upload/download progress monitoring.

/**
 * Plugin for monitoring upload/download progress
 */
object BodyProgress : HttpClientPlugin<BodyProgressConfig, BodyProgressConfig> {
    override val key: AttributeKey<BodyProgressConfig>
}

/**
 * Configuration for BodyProgress plugin
 */
class BodyProgressConfig {
    /**
     * Set upload progress listener
     */
    fun upload(listener: (bytesSentTotal: Long, contentLength: Long) -> Unit)
    
    /**
     * Set download progress listener
     */
    fun download(listener: (bytesReceivedTotal: Long, contentLength: Long) -> Unit)
}

HttpPlainText Plugin

Plain text content handling and charset support.

/**
 * Plugin for handling plain text content
 */
object HttpPlainText : HttpClientPlugin<HttpPlainTextConfig, HttpPlainTextConfig> {
    override val key: AttributeKey<HttpPlainTextConfig>
}

/**
 * Configuration for HttpPlainText plugin
 */
class HttpPlainTextConfig {
    /** Default charset for text content */
    var defaultCharset: Charset
    
    /** Accepted charsets */
    val acceptedCharsets: MutableSet<Charset>
    
    /**
     * Send text with specific charset
     */
    fun send(charset: Charset)
    
    /**
     * Accept text with specific charset
     */
    fun accept(charset: Charset)
}

Custom Plugin Development

Creating custom plugins for extending HttpClient functionality.

/**
 * Create a custom plugin using the plugin builder DSL
 */
fun <TConfig : Any> createClientPlugin(
    name: String,
    createConfiguration: () -> TConfig,
    body: PluginBuilder<TConfig>.() -> Unit
): HttpClientPlugin<TConfig, *>

/**
 * Builder for creating custom plugins
 */
class PluginBuilder<TConfig : Any> {
    /**
     * Configure what happens when the plugin is installed
     */
    fun onInstall(block: (plugin: TConfig, scope: HttpClient) -> Unit)
    
    /**
     * Add request transformation
     */
    fun transformRequestBody(
        block: suspend (context: HttpRequestBuilder, content: Any, bodyType: TypeInfo?) -> Any
    )
    
    /**
     * Add response transformation
     */
    fun transformResponseBody(
        block: suspend (context: HttpClientCall, content: Any, requestedType: TypeInfo) -> Any
    )
    
    /**
     * Intercept request pipeline
     */
    fun onRequest(block: suspend (request: HttpRequestBuilder, content: Any) -> Unit)
    
    /**
     * Intercept response pipeline
     */
    fun onResponse(block: suspend (response: HttpResponse) -> Unit)
    
    /**
     * Handle call lifecycle
     */
    fun onCallRequest(block: suspend (request: HttpRequest) -> Unit)
    fun onCallResponse(block: suspend (call: HttpClientCall) -> Unit)
}

Usage Examples:

import io.ktor.client.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.cache.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.cookies.*
import io.ktor.client.plugins.logging.*
import io.ktor.client.request.*
import kotlinx.serialization.json.Json

// Create client with built-in plugins
val client = HttpClient {
    // Timeout configuration
    install(HttpTimeout) {
        requestTimeoutMillis = 30000
        connectTimeoutMillis = 10000
        socketTimeoutMillis = 15000
    }
    
    // Redirect handling
    install(HttpRedirect) {
        checkHttpMethod = true
        allowHttpsDowngrade = false
        maxJumps = 5
    }
    
    // Response validation
    install(HttpCallValidator) {
        expectSuccess = true
        
        validateResponse { response ->
            if (response.status.value >= 400) {
                val errorBody = response.bodyAsText()
                throw Exception("HTTP ${response.status.value}: $errorBody")
            }
        }
        
        handleResponseExceptionWithRequest { exception, request ->
            println("Request to ${request.url} failed: ${exception.message}")
        }
    }
    
    // User agent
    install(UserAgent) {
        agent = "MyApp/1.0.0 (Kotlin Ktor Client)"
    }
    
    // Default request configuration
    install(DefaultRequest) {
        url {
            protocol = URLProtocol.HTTPS
            host = "api.example.com"
        }
        headers {
            append("X-API-Version", "v1")
        }
    }
    
    // Progress monitoring
    install(BodyProgress) {
        upload { bytesSent, totalBytes ->
            val progress = (bytesSent.toDouble() / totalBytes * 100).toInt()
            println("Upload progress: $progress%")
        }
        
        download { bytesReceived, totalBytes ->
            val progress = (bytesReceived.toDouble() / totalBytes * 100).toInt()
            println("Download progress: $progress%")
        }
    }
}

// Access installed plugins
val timeoutConfig = client.plugin(HttpTimeout)
println("Request timeout: ${timeoutConfig.requestTimeoutMillis}ms")

// Check if plugin is installed
val redirectConfig = client.pluginOrNull(HttpRedirect)
if (redirectConfig != null) {
    println("Redirect plugin is installed")
}

// Create custom plugin
val CustomLoggingPlugin = createClientPlugin("CustomLogging", { CustomLoggingConfig() }) {
    onRequest { request, _ ->
        println("Making request to: ${request.url}")
    }
    
    onResponse { response ->
        println("Received response: ${response.status}")
    }
    
    onInstall { config, scope ->
        println("Custom logging plugin installed with level: ${config.level}")
    }
}

data class CustomLoggingConfig(
    var level: String = "INFO"
)

// Install custom plugin
val customClient = HttpClient {
    install(CustomLoggingPlugin) {
        level = "DEBUG"
    }
}

// Request-specific timeout override
val response = client.get("/users") {
    timeout {
        requestTimeoutMillis = 5000
    }
}

client.close()
customClient.close()

Plugin Configuration Scope

Understanding plugin configuration and lifecycle management.

/**
 * Plugin configuration scope during client creation
 */
interface HttpClientConfig<T : HttpClientEngineConfig> {
    /** Engine-specific configuration */
    val engineConfig: T
    
    /** Enable/disable automatic redirects */
    var followRedirects: Boolean
    
    /** Enable/disable default transformers */
    var useDefaultTransformers: Boolean
    
    /** Enable/disable response validation */
    var expectSuccess: Boolean
    
    /**
     * Configure engine settings
     */
    fun engine(block: T.() -> Unit)
    
    /**
     * Install plugin with configuration
     */
    fun <TBuilder : Any, TPlugin : Any> install(
        plugin: HttpClientPlugin<TBuilder, TPlugin>,
        configure: TBuilder.() -> Unit = {}
    ): TPlugin
    
    /**
     * Copy configuration from another config
     */
    operator fun plusAssign(other: HttpClientConfig<out HttpClientEngineConfig>)
}