From ef7e660e17dbf0403533ee6dcf9c08168ccad2f6 Mon Sep 17 00:00:00 2001 From: "leonid.stashevsky" Date: Mon, 11 Nov 2024 11:31:42 +0100 Subject: [PATCH 1/3] KTOR-6501 Add client KDocs for config and ObservableContent --- .../src/io/ktor/client/HttpClientConfig.kt | 67 +++++++++++++++++-- .../ktor/client/content/ObservableContent.kt | 3 + .../ktor/client/plugins/DefaultTransform.kt | 1 - 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt index 52fa222b113..c1ea1122a70 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt @@ -7,12 +7,48 @@ package io.ktor.client import io.ktor.client.engine.* import io.ktor.client.plugins.* import io.ktor.util.* -import io.ktor.util.collections.* import io.ktor.utils.io.* import kotlin.collections.set /** - * A mutable [HttpClient] configuration. + * A mutable [HttpClient] configuration used to adjust settings, install plugins and interceptors. + * + * This class is available as block in [HttpClient] constructor or [HttpClient.config] builder: + * ```kotlin + * val client = HttpClient { // HttpClientConfig() + * // Configure engine settings + * engine { // HttpClientEngineConfig + * threadsCount = 4 + * pipelining = true + * } + * + * // Install and configure plugins + * install(ContentNegotiation) { + * json() + * } + * + * // Configure default request parameters + * defaultRequest { + * url("https://api.example.com") + * header("X-Custom-Header", "value") + * } + * + * // Configure client-wide settings + * expectSuccess = true + * followRedirects = true + * } + * ``` + * ## Configuring [HttpClientEngine] + * + * If the engine is specified explicitly, engine specific properties will be available in the engine block: + * ```kotlin + * val client = HttpClient(CIO) { // HttpClientConfig.() -> Unit + * engine { // CIOEngineConfig.() -> Unit + * // engine specific properties + * } + * } + * ``` + * * Learn more about the client's configuration from * [Creating and configuring a client](https://ktor.io/docs/create-client.html). */ @@ -25,7 +61,15 @@ public class HttpClientConfig { internal var engineConfig: T.() -> Unit = {} /** - * Allows you to configure engine parameters. + * Builder for configuring engine-specific settings in [HttpClientEngineConfig] + * (like dispatcher, threads count, proxy, etc.) + * + * ```kotlin + * val client = HttpClient(CIO) { // HttpClientConfig + * engine { // CIOEngineConfig.() -> Unit + * proxy = ProxyBuilder.http("proxy.example.com", 8080) + * } + * ``` * * You can learn more from [Engines](https://ktor.io/docs/http-client-engines.html). */ @@ -40,11 +84,26 @@ public class HttpClientConfig { /** * Specifies whether the client redirects to URLs provided in the `Location` header. * You can disable redirections by setting this property to `false`. + * + * For the advanced redirection configuration, use [HttpRedirect] plugin. */ public var followRedirects: Boolean = true /** - * Uses [defaultTransformers] to automatically handle simple [ContentType]. + * Enable body transformations for many common types like [String], [ByteArray], [ByteReadChannel], etc. + * These transformations are applied to the request and response bodies. + * + * The transformers will be used when the response body is received with a type: + * ```kotlin + * val client = HttpClient() + * val bytes = client.get("https://ktor.io") + * .body() + * ``` + * + * The flag is enabled by default. + * You might want to disable it if you want to write your own transformers or handle body manually. + * + * Check [defaultTransformers] documentation for more details. */ public var useDefaultTransformers: Boolean = true diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/content/ObservableContent.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/content/ObservableContent.kt index 4a8cc205fdb..12fb1c4dcf9 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/content/ObservableContent.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/content/ObservableContent.kt @@ -15,6 +15,9 @@ import kotlin.coroutines.* /** * Callback that can be registered to listen for upload/download progress. + * + * This class is used for callbacks in [HttpRequestBuilder.onDownload] and [HttpRequestBuilder.onUpload]. + * * @param bytesSentTotal number of transmitted bytes. * @param contentLength body size. Can be null if the size is unknown. */ diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/DefaultTransform.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/DefaultTransform.kt index 6d35d2a0f6e..29b295283a8 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/DefaultTransform.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/DefaultTransform.kt @@ -23,7 +23,6 @@ private val LOGGER = KtorSimpleLogger("io.ktor.client.plugins.defaultTransformer * Usually installed by default so there is no need to use it * unless you have disabled it via [HttpClientConfig.useDefaultTransformers]. */ -@OptIn(InternalAPI::class) public fun HttpClient.defaultTransformers() { requestPipeline.intercept(HttpRequestPipeline.Render) { body -> if (context.headers[HttpHeaders.Accept] == null) { From fa6574274e3cd187408a9dd738c4d0c18deb65c5 Mon Sep 17 00:00:00 2001 From: "leonid.stashevsky" Date: Wed, 13 Nov 2024 14:13:00 +0100 Subject: [PATCH 2/3] fixup! KTOR-6501 Add client KDocs for config and ObservableContent --- .../common/src/io/ktor/client/HttpClientConfig.kt | 2 ++ .../common/src/io/ktor/client/engine/Utils.kt | 2 +- .../common/src/io/ktor/client/plugins/HttpCallValidator.kt | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt index c1ea1122a70..e21dc2d9af4 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt @@ -110,6 +110,8 @@ public class HttpClientConfig { /** * Terminates [HttpClient.receivePipeline] if the status code is not successful (>=300). * Learn more from [Response validation](https://ktor.io/docs/response-validation.html). + * + * Please check [HttpCallValidator] documentation to learn more details. */ public var expectSuccess: Boolean = false diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/engine/Utils.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/engine/Utils.kt index f0d2be53168..1257629f507 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/engine/Utils.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/engine/Utils.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.* import kotlin.coroutines.* /** - * Default user agent to use in ktor client. + * Default user agent to use in a ktor client. */ @InternalAPI public val KTOR_DEFAULT_USER_AGENT: String = "ktor-client" diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/HttpCallValidator.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/HttpCallValidator.kt index 168e9ec5edd..55be0c0af15 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/HttpCallValidator.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/HttpCallValidator.kt @@ -75,9 +75,9 @@ public typealias CallExceptionHandler = suspend (cause: Throwable) -> Unit public typealias CallRequestExceptionHandler = suspend (cause: Throwable, request: HttpRequest) -> Unit /** - * Response validator plugin is used for validate response and handle response exceptions. + * Response validator plugin is used for validating [HttpClient] response and handle response exceptions. * - * See also [Config] for additional details. + * See also [HttpCallValidatorConfig] for additional details. */ public val HttpCallValidator: ClientPlugin = createClientPlugin( "HttpResponseValidator", From 26ad74102c10945662c2057fef9da69adf8a7682 Mon Sep 17 00:00:00 2001 From: "leonid.stashevsky" Date: Mon, 18 Nov 2024 13:19:08 +0100 Subject: [PATCH 3/3] WIP install KDoc --- .../src/io/ktor/client/HttpClientConfig.kt | 12 +++++ .../common/test/HttpClientConfigTest.kt | 48 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 ktor-client/ktor-client-core/common/test/HttpClientConfigTest.kt diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt index e21dc2d9af4..df92c6de877 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/HttpClientConfig.kt @@ -127,6 +127,18 @@ public class HttpClientConfig { /** * Installs the specified [plugin] and optionally configures it using the [configure] block. + * + * ```kotlin + * val client = HttpClient { + * install(ContentNegotiation) { + * // configuration block + * json() + * } + * } + * ``` + * + * If the plugin is already installed + * * Learn more from [Plugins](https://ktor.io/docs/http-client-plugins.html). */ public fun install( diff --git a/ktor-client/ktor-client-core/common/test/HttpClientConfigTest.kt b/ktor-client/ktor-client-core/common/test/HttpClientConfigTest.kt new file mode 100644 index 00000000000..e467e0dda14 --- /dev/null +++ b/ktor-client/ktor-client-core/common/test/HttpClientConfigTest.kt @@ -0,0 +1,48 @@ +import io.ktor.client.HttpClient +import io.ktor.client.HttpClientConfig +import io.ktor.client.engine.HttpClientEngineConfig +import io.ktor.client.plugins.api.createClientPlugin +import kotlin.test.Test +import kotlin.test.assertEquals + +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +class HttpClientConfigTest { + + @Test + fun testPluginInstalledTwice() { + var configuration = 0 + var installation = 0 + var first = 0 + var second = 0 + + class Config { + var firstConfig = 0 + var secondConfig = 0 + init { + configuration++ + } + } + + val plugin = createClientPlugin("hey", ::Config) { + installation++ + } + + val client = HttpClient { + install(plugin) { + first += 1 + } + + install(plugin) { + second += 1 + } + } + + assertEquals(1, configuration) + assertEquals(1, installation) + assertEquals(1, first) + assertEquals(1, second) + } +}