Multiplatform asynchronous HTTP client core library for JVM that provides request/response handling, plugin architecture, and extensible HTTP communication capabilities.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
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)
}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>
): TPluginRequest, 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)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
}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
}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
}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
}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
}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
}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)
}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)
}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()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>)
}