From b2c7f3f1ff08ac3e7d700ca0d02d873dd7155622 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Thu, 23 May 2019 15:57:18 +0300 Subject: [PATCH 01/38] Upgrade dependencies versions --- build.gradle.kts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cb2e1fff..e7c3bc9c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - kotlin("jvm") version "1.3.20" + kotlin("jvm") version "1.3.31" jacoco id("org.jetbrains.dokka") version "0.9.16" @@ -11,7 +11,7 @@ plugins { } group = "io.github.rybalkinsd" -version = "0.9.0-SNAPSHOT" +version = "0.10.0-SNAPSHOT" repositories { mavenCentral() @@ -20,13 +20,13 @@ repositories { dependencies { implementation(kotlin("stdlib-jdk8")) implementation(kotlin("reflect")) - implementation("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.0.1") + implementation("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.2.1") - val jacksonVersion = "2.9.7" + val jacksonVersion = "2.9.9" implementation(jackson("core"), "jackson-databind", jacksonVersion) implementation(jackson("dataformat"), "jackson-dataformat-yaml", jacksonVersion) implementation(jackson("module"), "jackson-module-kotlin", jacksonVersion) - api("com.squareup.okhttp3", "okhttp", "3.12.0") + api("com.squareup.okhttp3", "okhttp", "3.14.2") testImplementation(kotlin("test-junit")) testImplementation("io.mockk:mockk:1.9.3") From b3b81b065bbf99b8bd358a90e482fb5f7e0cf139 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Thu, 23 May 2019 16:06:44 +0300 Subject: [PATCH 02/38] Rename asyncHttpGet to httpGetAsync, create SNAPSHOT version of httpPostAsync --- .../kohttp/dsl/async/AsyncHttpGetDsl.kt | 13 ++-- .../kohttp/dsl/async/AsyncHttpPostDsl.kt | 59 +++++++++++++++++++ .../rybalkinsd/kohttp/ext/HttpContextExt.kt | 7 ++- .../github/rybalkinsd/kohttp/ext/StringExt.kt | 20 ++++--- .../kohttp/dsl/async/AsyncHttpGetDslTest.kt | 2 +- .../kohttp/ext/AsyncStringExtKtTest.kt | 2 +- 6 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpPostDsl.kt diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt index c9ebcaf1..1410f51f 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt @@ -4,7 +4,7 @@ import io.github.rybalkinsd.kohttp.client.defaultHttpClient import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext import io.github.rybalkinsd.kohttp.ext.suspendCall import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers.Unconfined +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import okhttp3.Call @@ -16,7 +16,10 @@ import okhttp3.Response * @since 0.4.0 * @author sergey */ -fun asyncHttpGet(client: Call.Factory = defaultHttpClient, init: HttpGetContext.() -> Unit): Deferred = - GlobalScope.async(context = Unconfined) { - client.suspendCall(HttpGetContext().apply(init).makeRequest()) - } +fun httpGetAsync( + client: Call.Factory = defaultHttpClient, + init: HttpGetContext.() -> Unit +): Deferred = + GlobalScope.async(context = Dispatchers.IO) { + client.suspendCall(HttpGetContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpPostDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpPostDsl.kt new file mode 100644 index 00000000..357cf6ec --- /dev/null +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpPostDsl.kt @@ -0,0 +1,59 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.client.defaultHttpClient +import io.github.rybalkinsd.kohttp.dsl.context.HttpPostContext +import io.github.rybalkinsd.kohttp.ext.suspendCall +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import okhttp3.Call +import okhttp3.Response + + +/** + * Method provides an async DSL call of HTTP POST + * + * @return a `Response` instance. + * + * Usage example using the default `defaultHttpClient`: + * + *
+ *  val response: Response = httpPostAsync {
+ *      host = "yourhost"
+ *      scheme = "https"
+ *      port = 8080
+ *      path = "path/to/resource"
+ *      param {
+ *          "your param" to "value"
+ *      }
+ *      header { ... }
+ *      body { ... }
+ *  }
+ *  response.await().use {
+ *      your code here
+ *  }
+ *  
+ * + * @param client allow to use your own implementation of HttpClient. + * `defaultHttpClient` is used by default. + * + *
+ *  val response: Response? = httpPostAsync(customHttpClient) {
+ *      ...
+ *  }
+ * 
+ * + * @see Response + * @see HttpPostContext + * + * @since 0.2.0 + * @author sergey + */ +fun httpPostAsync( + client: Call.Factory = defaultHttpClient, + init: HttpPostContext.() -> Unit +): Deferred = + GlobalScope.async(context = Dispatchers.IO) { + client.suspendCall(HttpPostContext().apply(init).makeRequest()) + } \ No newline at end of file diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt index 4f2a49f5..d4206644 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt @@ -8,12 +8,15 @@ import java.net.URL * @author sergey */ fun HttpContext.url(url: URL) { - if (url.protocol != "http" && url.protocol != "https") throw IllegalArgumentException("unexpected scheme: $scheme") scheme = url.protocol + if (scheme != "http" && scheme != "https") + throw IllegalArgumentException("unexpected scheme: $scheme") host = url.host ?: throw IllegalArgumentException("unexpected host: $host") - if (url.port != -1) { port = url.port } + if (url.port != -1) { + port = url.port + } path = url.path } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt index d05af577..5cf7248c 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt @@ -2,7 +2,7 @@ package io.github.rybalkinsd.kohttp.ext import io.github.rybalkinsd.kohttp.client.defaultHttpClient import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers.Unconfined +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import okhttp3.Call @@ -32,8 +32,10 @@ import okhttp3.Response * @since 0.1.0 * @author sergey */ -fun String.httpGet(client: Call.Factory = defaultHttpClient): Response = - client.call(Request.Builder().url(this).build()) +fun String.httpGet( + client: Call.Factory = defaultHttpClient +): Response = + client.call(Request.Builder().url(this).build()) /** * Async version of http GET request with the provided `String` url. @@ -49,7 +51,7 @@ fun String.httpGet(client: Call.Factory = defaultHttpClient): Response = * value that may be consumed only once and then closed. All other properties are immutable. * * Usage example: - * val response = "http://host:port/path/?a=b".asyncHttpGet() + * val response = "http://host:port/path/?a=b".httpGetAsync() * ... * response.await().use { * your code here @@ -61,8 +63,10 @@ fun String.httpGet(client: Call.Factory = defaultHttpClient): Response = * @since 0.1.0 * @author sergey */ -fun String.asyncHttpGet(client: Call.Factory = defaultHttpClient): Deferred = - GlobalScope.async(context = Unconfined) { - client.suspendCall(Request.Builder().url(this@asyncHttpGet).build()) - } +fun String.httpGetAsync( + client: Call.Factory = defaultHttpClient +): Deferred = + GlobalScope.async(context = Dispatchers.IO) { + client.call(Request.Builder().url(this@httpGetAsync).build()) + } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDslTest.kt index 24f1d3d1..952b87c6 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDslTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDslTest.kt @@ -23,7 +23,7 @@ class AsyncHttpGetDslTest { "lr" to "213" ) - val response = asyncHttpGet { + val response = httpGetAsync { host = "postman-echo.com" path = "/get" diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/AsyncStringExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/AsyncStringExtKtTest.kt index 641f164e..64b7c464 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/AsyncStringExtKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/AsyncStringExtKtTest.kt @@ -15,7 +15,7 @@ class AsyncStringExtKtTest { measureTimeMillis { runBlocking { val tasks = List(100) { - "https://www.yandex.ru/search/?text=iphone".asyncHttpGet() + "https://www.yandex.ru/search/?text=iphone".httpGetAsync() } tasks.map { r -> r.await().also { it.close() } From 4fd1da5c0c42d27032176fbcc6ecc85cffeee56a Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Thu, 23 May 2019 16:08:47 +0300 Subject: [PATCH 03/38] Create performance test to compare different async realisations --- .../AsyncHttpGetPerformanceTest.kt | 179 ++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt new file mode 100644 index 00000000..405d0553 --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt @@ -0,0 +1,179 @@ +package io.github.rybalkinsd.kohttp.performance + +import io.github.rybalkinsd.kohttp.client.client +import io.github.rybalkinsd.kohttp.client.defaultHttpClient +import io.github.rybalkinsd.kohttp.configuration.ConnectionPoolConfig +import io.github.rybalkinsd.kohttp.configuration.config +import io.github.rybalkinsd.kohttp.dsl.async.httpGetAsync +import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext +import io.github.rybalkinsd.kohttp.dsl.httpGet +import io.github.rybalkinsd.kohttp.ext.url +import kotlinx.coroutines.* +import okhttp3.* +import org.junit.Test +import java.io.IOException +import java.util.concurrent.Executor +import java.util.concurrent.TimeUnit +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine +import kotlin.reflect.full.declaredMembers +import kotlin.reflect.jvm.isAccessible +import kotlin.system.measureTimeMillis + + +/** + * Compare performance of different execution variants + * + * Test proposal - run 100 simple get requests + * to measure time that will be spent on execution. + * Compare result with another alternatives + * that also presented in this class + * + * Time in millis will be written in console + * @since 0.10.0 + * @author evgeny + */ +class AsyncHttpGetPerformanceTest { + + /** + * Running 100 simple requests tu measure current realisation + */ + @Test + fun `100 simple async requests with Call-Factory-suspendCall extension`() { + measureTimeMillis { + val rs = List(100) { + httpGetAsync { + url("https://postman-echo.com/delay/2") + } + } + + runBlocking { + val code = rs.awaitAll().map { it.code() } + println(code) + } + }.also { print(it) } + } + + + /** + * Running 100 simple requests tu measure old realisation + */ + @Test + fun `100 simple async requests with old Call-Factory-suspendCall extension`() { + measureTimeMillis { + val rs = List(1000) { + oldAsyncHttpGet(client = pseudoDefault) { + url("https://postman-echo.com/delay/2") + } + } + + runBlocking { + val code = rs.awaitAll().map { it.code() } + println(code) + } + }.also { print(it) } + } + + + /** + * Running 100 simple requests tu measure realisation with kotlin + * async with Default dispatcher + */ + @Test + fun `100 simple async requests with kotlin coroutines Dispatcher-Default`() { + measureTimeMillis { + val rs = List(100) { + GlobalScope.async(context = Dispatchers.Default) { + httpGet { + url("https://postman-echo.com/delay/2") + } + } + } + + runBlocking { + val code = rs.awaitAll().map { it.code() } + println(code) + } + }.also { print(it) } + } + + + /** + * Running 100 simple requests tu measure realisation with kotlin + * async with IO dispatcher + */ + @Test + fun `100 simple async requests with kotlin coroutines Dispatcher-IO`() { + measureTimeMillis { + val rs = List(100) { + GlobalScope.async(context = Dispatchers.IO) { + httpGet { + url("https://postman-echo.com/delay/2") + } + } + } + + runBlocking { + val code = rs.awaitAll().map { it.code() } + println(code) + } + }.also { print(it) } + } + + @Test + fun `Check dispatcher`() { + val cd = Dispatchers.IO is Executor + println(cd) + println(Dispatchers.IO::class.java.name) + println(Runtime.getRuntime().availableProcessors()) + println(Dispatchers.IO::class.declaredMembers + .firstOrNull { it.name == "parallelism" } + ?.also { it.isAccessible = true } + ?.call(Dispatchers.IO) + ) + } + + companion object { + /** + * Old async httpGet method, before 0.10.0 + */ + private fun oldAsyncHttpGet( + client: Call.Factory = defaultHttpClient, + init: HttpGetContext.() -> Unit + ): Deferred = GlobalScope.async(context = Dispatchers.IO) { + client.oldSuspendCall(HttpGetContext().apply(init).makeRequest()) + } + + private suspend fun Call.Factory.oldSuspendCall( + request: Request + ): Response = suspendCoroutine { cont -> + newCall(request).enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + cont.resume(response) + } + + override fun onFailure(call: Call, e: IOException) { + cont.resumeWithException(e) + } + }) + } + } + + val pseudoDefault: OkHttpClient = config.client.let { + client { + connectionPool = it.connectionPoolConfig.create() + connectTimeout = it.connectTimeout + readTimeout = it.readTimeout + writeTimeout = it.writeTimeout + followRedirects = it.followRedirects + followSslRedirects = it.followSslRedirects + dispatcher = Dispatcher().also { + it.maxRequestsPerHost = 1000 + it.maxRequests = 1000 + } + } + } + + private fun ConnectionPoolConfig.create() = ConnectionPool(100, keepAliveDuration, TimeUnit.MILLISECONDS) +} \ No newline at end of file From 34f18069dc557b3fa3e8df99c955077804a723e3 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Thu, 23 May 2019 16:09:18 +0300 Subject: [PATCH 04/38] Alpha version of custom coroutine dispatcher --- .../kohttp/pool/CoroutineExecutorService.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt new file mode 100644 index 00000000..496c04a4 --- /dev/null +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt @@ -0,0 +1,42 @@ +package io.github.rybalkinsd.kohttp.pool + +import kotlinx.coroutines.* +import java.util.concurrent.Executor +import java.util.concurrent.ExecutorService +import java.util.concurrent.ForkJoinPool +import javax.xml.bind.JAXBElement +import kotlin.coroutines.CoroutineContext + +/** + * @Author: evgeny.vorobyev + * @Since: 0.10.0 + */ +class CoroutineExecutorService( + private val executorService: ExecutorService = ForkJoinPool() +) : ExecutorService by executorService, + ExecutorCoroutineDispatcher() { + override val executor: Executor + get() = this + + override fun close() { + throw UnsupportedOperationException("${CoroutineExecutorService::class.java.name} can not be closed") + } + + override fun dispatch(context: CoroutineContext, block: Runnable) { + println("Dispatch request with context ${context::class.java.name}") + executorService.execute(block) + } +} + +fun main() { + val context = CoroutineExecutorService() + val task = GlobalScope.async(context = context) { + println(Thread.currentThread().name) + withContext(context = context) { + println(Thread.currentThread().name) + } + } + runBlocking { + task.await() + } +} From fd2885f38a46d534da185b9275e3be7d0b63207c Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Thu, 23 May 2019 16:54:27 +0300 Subject: [PATCH 05/38] Add todo for future work --- .../github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt index 496c04a4..0cdd472d 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt @@ -22,6 +22,8 @@ class CoroutineExecutorService( throw UnsupportedOperationException("${CoroutineExecutorService::class.java.name} can not be closed") } + // todo research about DefaultExecutor.enqueue how it works, how can we implement same logic + // todo watch Roman Elizarov articles/youtube videos and other about coroutines and its logic override fun dispatch(context: CoroutineContext, block: Runnable) { println("Dispatch request with context ${context::class.java.name}") executorService.execute(block) From 4cb632221cbdd7110ced3e5f722454e5354b3ff7 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Mon, 27 May 2019 16:18:34 +0300 Subject: [PATCH 06/38] Created new http async dsl methods --- .../github/rybalkinsd/kohttp/dsl/UploadDsl.kt | 26 +++++++++ .../kohttp/dsl/async/AsyncHttpGetDsl.kt | 25 -------- .../kohttp/dsl/async/HttpDeleteAsyncDsl.kt | 58 +++++++++++++++++++ .../kohttp/dsl/async/HttpGetAsyncDsl.kt | 56 ++++++++++++++++++ .../kohttp/dsl/async/HttpHeadAsyncDsl.kt | 56 ++++++++++++++++++ .../kohttp/dsl/async/HttpPatchAsyncDsl.kt | 58 +++++++++++++++++++ ...syncHttpPostDsl.kt => HttpPostAsyncDsl.kt} | 14 ++--- .../kohttp/dsl/async/HttpPutAsyncDsl.kt | 58 +++++++++++++++++++ .../kohttp/dsl/async/UploadAsyncDsl.kt | 51 ++++++++++++++++ .../github/rybalkinsd/kohttp/ext/FileExt.kt | 19 ++++++ .../io/github/rybalkinsd/kohttp/ext/UriExt.kt | 19 ++++++ 11 files changed, 408 insertions(+), 32 deletions(-) delete mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt create mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt create mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt create mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt create mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt rename src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/{AsyncHttpPostDsl.kt => HttpPostAsyncDsl.kt} (78%) create mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt create mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt index 14876de2..456fcbdf 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt @@ -6,6 +6,32 @@ import okhttp3.Call import okhttp3.Response /** + * Method provides an synchronous DSL call of HTTP POST to UPLOAD file + * + * @return a `Response` instance. + * + * Usage example using the default `defaultHttpClient`: + * + *
+ *  val response: Response = upload {
+        url("http://postman-echo.com/post")
+        val fileUri = this.javaClass.getResource("/cat.gif").toURI()
+        file(fileUri)
+    }
+ *  response.use { ... }
+ *  
+ * + * @param client allow to use your own implementation of HttpClient. + * `defaultHttpClient` is used by default. + * + *
+ *  val response: Response = upload(customHttpClient) {
+ *      ...
+ *  }
+ * 
+ * + * @see Response + * @see HttpDeleteContext * * @since 0.8.0 * @author sergey diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt deleted file mode 100644 index 1410f51f..00000000 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.github.rybalkinsd.kohttp.dsl.async - -import io.github.rybalkinsd.kohttp.client.defaultHttpClient -import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext -import io.github.rybalkinsd.kohttp.ext.suspendCall -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async -import okhttp3.Call -import okhttp3.Response - - -/** - * - * @since 0.4.0 - * @author sergey - */ -fun httpGetAsync( - client: Call.Factory = defaultHttpClient, - init: HttpGetContext.() -> Unit -): Deferred = - GlobalScope.async(context = Dispatchers.IO) { - client.suspendCall(HttpGetContext().apply(init).makeRequest()) - } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt new file mode 100644 index 00000000..949e6313 --- /dev/null +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt @@ -0,0 +1,58 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.client.defaultHttpClient +import io.github.rybalkinsd.kohttp.dsl.context.HttpDeleteContext +import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext +import io.github.rybalkinsd.kohttp.ext.suspendCall +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import okhttp3.Call +import okhttp3.Response + + +/** + * Method provides an asynchronous DSL call of HTTP DELETE + * + * @return a deferred `Response` instance. + * + * Usage example using the default `defaultHttpClient`: + * + *
+ *  val response: Deferred = httpDeleteAsync {
+ *      host = "yourhost"
+ *      scheme = "https"
+ *      port = 8080
+ *      path = "path/to/resource"
+ *      param {
+ *          "your param" to "value"
+ *      }
+ *      header { ... }
+ *      body { ... }
+ *  }
+ *  response.await().use { ... }
+ *  
+ * + * @param client allow to use your own implementation of HttpClient. + * `defaultHttpClient` is used by default. + * + *
+ *  val response: Deferred = httpDeleteAsync(customHttpClient) {
+ *      ...
+ *  }
+ * 
+ * + * @see Response + * @see HttpDeleteContext + * + * @since 0.10.0 + * @author evgeny + */ +fun httpDeleteAsync( + client: Call.Factory = defaultHttpClient, + init: HttpDeleteContext.() -> Unit +): Deferred = + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpDeleteContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt new file mode 100644 index 00000000..b6cc12cc --- /dev/null +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt @@ -0,0 +1,56 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.client.defaultHttpClient +import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext +import io.github.rybalkinsd.kohttp.ext.suspendCall +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import okhttp3.Call +import okhttp3.Response + + +/** + * Method provides an asynchronous DSL call of HTTP GET + * + * @return a deferred `Response` instance. + * + * Usage example using the default `defaultHttpClient`: + * + *
+ *  val response: Deferred = httpGetAsync {
+ *      host = "yourhost"
+ *      scheme = "https"
+ *      port = 8080
+ *      path = "path/to/resource"
+ *      param {
+ *          "your param" to "value"
+ *      }
+ *      header { ... }
+ *  }
+ *  response.await().use { ... }
+ *  
+ * + * @param client allow to use your own implementation of HttpClient. + * `defaultHttpClient` is used by default. + * + *
+ *  val response: Deferred = httpGetAsync(customHttpClient) {
+ *      ...
+ *  }
+ * 
+ * + * @see Response + * @see HttpGetContext + * + * @since 0.4.0 + * @author sergey + */ +fun httpGetAsync( + client: Call.Factory = defaultHttpClient, + init: HttpGetContext.() -> Unit +): Deferred = + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpGetContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt new file mode 100644 index 00000000..0478926e --- /dev/null +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt @@ -0,0 +1,56 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.client.defaultHttpClient +import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext +import io.github.rybalkinsd.kohttp.dsl.context.HttpHeadContext +import io.github.rybalkinsd.kohttp.ext.suspendCall +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import okhttp3.Call +import okhttp3.Response + +/** + * Method provides an asynchronous DSL call of HTTP HEAD + * + * @return a deferred `Response` instance. + * + * Usage example using the default `defaultHttpClient`: + * + *
+ *  val response: Deferred = httpHeadAsync {
+ *      host = "yourhost"
+ *      scheme = "https"
+ *      port = 8080
+ *      path = "path/to/resource"
+ *      param {
+ *          "your param" to "value"
+ *      }
+ *      header { ... }
+ *  }
+ *  response.await().use { ... }
+ *  
+ * + * @param client allow to use your own implementation of HttpClient. + * `defaultHttpClient` is used by default. + * + *
+ *  val response: Deferred = httpHeadAsync(customHttpClient) {
+ *      ...
+ *  }
+ * 
+ * + * @see Response + * @see HttpHeadContext + * + * @since 0.10.0 + * @author evgeny + */ +fun httpHeadAsync( + client: Call.Factory = defaultHttpClient, + init: HttpHeadContext.() -> Unit +): Deferred = + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpHeadContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt new file mode 100644 index 00000000..c58dafc6 --- /dev/null +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt @@ -0,0 +1,58 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.client.defaultHttpClient +import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext +import io.github.rybalkinsd.kohttp.dsl.context.HttpPatchContext +import io.github.rybalkinsd.kohttp.ext.suspendCall +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import okhttp3.Call +import okhttp3.Response + + +/** + * Method provides an asynchronous DSL call of HTTP PATCH + * + * @return a deferred `Response` instance. + * + * Usage example using the default `defaultHttpClient`: + * + *
+ *  val response: Deferred = httpPatchAsync {
+ *      host = "yourhost"
+ *      scheme = "https"
+ *      port = 8080
+ *      path = "path/to/resource"
+ *      param {
+ *          "your param" to "value"
+ *      }
+ *      header { ... }
+ *      body { ... }
+ *  }
+ *  response.await().use { ... }
+ *  
+ * + * @param client allow to use your own implementation of HttpClient. + * `defaultHttpClient` is used by default. + * + *
+ *  val response: Deferred = httpPatchAsync(customHttpClient) {
+ *      ...
+ *  }
+ * 
+ * + * @see Response + * @see HttpPatchContext + * + * @since 0.10.0 + * @author evgeny + */ +fun httpPatchAsync( + client: Call.Factory = defaultHttpClient, + init: HttpPatchContext.() -> Unit +): Deferred = + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpPatchContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpPostDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt similarity index 78% rename from src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpPostDsl.kt rename to src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt index 357cf6ec..957c3fa2 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpPostDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt @@ -12,14 +12,14 @@ import okhttp3.Response /** - * Method provides an async DSL call of HTTP POST + * Method provides an asynchronous DSL call of HTTP POST * - * @return a `Response` instance. + * @return a deferred `Response` instance. * * Usage example using the default `defaultHttpClient`: * *
- *  val response: Response = httpPostAsync {
+ *  val response: Deferred = httpPostAsync {
  *      host = "yourhost"
  *      scheme = "https"
  *      port = 8080
@@ -39,7 +39,7 @@ import okhttp3.Response
  * `defaultHttpClient` is used by default.
  *
  * 
- *  val response: Response? = httpPostAsync(customHttpClient) {
+ *  val response: Deferred = httpPostAsync(customHttpClient) {
  *      ...
  *  }
  * 
@@ -47,13 +47,13 @@ import okhttp3.Response * @see Response * @see HttpPostContext * - * @since 0.2.0 - * @author sergey + * @since 0.10.0 + * @author evgeny */ fun httpPostAsync( client: Call.Factory = defaultHttpClient, init: HttpPostContext.() -> Unit ): Deferred = - GlobalScope.async(context = Dispatchers.IO) { + GlobalScope.async(context = Dispatchers.Unconfined) { client.suspendCall(HttpPostContext().apply(init).makeRequest()) } \ No newline at end of file diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt new file mode 100644 index 00000000..c6c23468 --- /dev/null +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt @@ -0,0 +1,58 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.client.defaultHttpClient +import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext +import io.github.rybalkinsd.kohttp.dsl.context.HttpPutContext +import io.github.rybalkinsd.kohttp.ext.suspendCall +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import okhttp3.Call +import okhttp3.Response + + +/** + * Method provides an asynchronous DSL call of HTTP PUT + * + * @return a deferred `Response` instance. + * + * Usage example using the default `defaultHttpClient`: + * + *
+ *  val response: Deferred = httpPutAsync {
+ *      host = "yourhost"
+ *      scheme = "https"
+ *      port = 8080
+ *      path = "path/to/resource"
+ *      param {
+ *          "your param" to "value"
+ *      }
+ *      header { ... }
+ *      body { ... }
+ *  }
+ *  response.await().use { ... }
+ *  
+ * + * @param client allow to use your own implementation of HttpClient. + * `defaultHttpClient` is used by default. + * + *
+ *  val response: Deferred = httpPutAsync(customHttpClient) {
+ *      ...
+ *  }
+ * 
+ * + * @see Response + * @see HttpPutContext + * + * @since 0.10.0 + * @author evgeny + */ +fun httpPutAsync( + client: Call.Factory = defaultHttpClient, + init: HttpPutContext.() -> Unit +): Deferred = + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpPutContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt new file mode 100644 index 00000000..7d5c34f0 --- /dev/null +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt @@ -0,0 +1,51 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.client.defaultHttpClient +import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext +import io.github.rybalkinsd.kohttp.dsl.context.UploadContext +import io.github.rybalkinsd.kohttp.ext.suspendCall +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import okhttp3.Call +import okhttp3.Response + +/** + * Method provides an asynchronous DSL call of HTTP POST to UPLOAD file + * + * @return a deferred `Response` instance. + * + * Usage example using the default `defaultHttpClient`: + * + *
+ *  val response: Deferred = uploadAsync {
+        url("http://postman-echo.com/post")
+        val fileUri = this.javaClass.getResource("/cat.gif").toURI()
+        file(fileUri)
+    }
+ *  response.await().use { ... }
+ *  
+ * + * @param client allow to use your own implementation of HttpClient. + * `defaultHttpClient` is used by default. + * + *
+ *  val response: Deferred = uploadAsync(customHttpClient) {
+ *      ...
+ *  }
+ * 
+ * + * @see Response + * @see HttpDeleteContext + * + * @since 0.10.0 + * @author evgeny + */ +fun uploadAsync( + client: Call.Factory = defaultHttpClient, + init: UploadContext.() -> Unit +): Deferred = + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(UploadContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/FileExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/FileExt.kt index b70b56ad..7824a00b 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/FileExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/FileExt.kt @@ -1,5 +1,6 @@ package io.github.rybalkinsd.kohttp.ext +import kotlinx.coroutines.Deferred import okhttp3.Response import java.io.File import java.net.URL @@ -22,3 +23,21 @@ fun File.upload(destination: String): Response = io.github.rybalkinsd.kohttp.dsl file(this@upload) } + +/** + * @since 0.10.0 + * @author evgeny + */ +fun File.uploadAsync(destination: URL): Deferred = io.github.rybalkinsd.kohttp.dsl.async.uploadAsync { + url(destination) + file(this@uploadAsync) +} + +/** + * @since 0.10.0 + * @author evgeny + */ +fun File.uploadAsync(destination: String): Deferred = io.github.rybalkinsd.kohttp.dsl.async.uploadAsync { + url(destination) + file(this@uploadAsync) +} diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/UriExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/UriExt.kt index 5e17dd64..2553ce0e 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/UriExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/UriExt.kt @@ -1,5 +1,6 @@ package io.github.rybalkinsd.kohttp.ext +import kotlinx.coroutines.Deferred import okhttp3.Response import java.net.URI import java.net.URL @@ -21,3 +22,21 @@ fun URI.upload(destination: String): Response = io.github.rybalkinsd.kohttp.dsl. url(destination) file(this@upload) } + +/** + * @since 0.10.0 + * @author evgeny + */ +fun URI.uploadAsync(destination: URL): Deferred = io.github.rybalkinsd.kohttp.dsl.async.uploadAsync { + url(destination) + file(this@uploadAsync) +} + +/** + * @since 0.10.0 + * @author evgeny + */ +fun URI.uploadAsync(destination: String): Deferred = io.github.rybalkinsd.kohttp.dsl.async.uploadAsync { + url(destination) + file(this@uploadAsync) +} From 6827deab56665a646ac82dd9e646902bf52ec52d Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Mon, 27 May 2019 16:47:16 +0300 Subject: [PATCH 07/38] Extended configuration for defaultHttpClient --- .../kohttp/client/DefaultHttpClient.kt | 12 ++++++++++- .../rybalkinsd/kohttp/configuration/Config.kt | 20 ++++++++++++++++++- src/test/resources/kohttp.yaml | 3 +++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/client/DefaultHttpClient.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/client/DefaultHttpClient.kt index f9d0dc2d..c33727a9 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/client/DefaultHttpClient.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/client/DefaultHttpClient.kt @@ -1,8 +1,10 @@ package io.github.rybalkinsd.kohttp.client import io.github.rybalkinsd.kohttp.configuration.ConnectionPoolConfig +import io.github.rybalkinsd.kohttp.configuration.DispatcherConfig import io.github.rybalkinsd.kohttp.configuration.config import okhttp3.ConnectionPool +import okhttp3.Dispatcher import okhttp3.OkHttpClient import java.util.concurrent.TimeUnit @@ -37,7 +39,15 @@ val defaultHttpClient: OkHttpClient = config.client.let { writeTimeout = it.writeTimeout followRedirects = it.followRedirects followSslRedirects = it.followSslRedirects + dispatcher = it.dispatcher.create() } } -private fun ConnectionPoolConfig.create() = ConnectionPool(maxIdleConnections, keepAliveDuration, TimeUnit.MILLISECONDS) +private fun DispatcherConfig.create() = + Dispatcher().apply { + this@apply.maxRequests = this@create.maxRequests + this@apply.maxRequestsPerHost = this@create.maxRequestsPerHost + } + +private fun ConnectionPoolConfig.create() = + ConnectionPool(maxIdleConnections, keepAliveDuration, TimeUnit.MILLISECONDS) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt index e96bdf83..5c3c9f26 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt @@ -7,6 +7,7 @@ import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.registerKotlinModule import java.util.concurrent.TimeUnit +const val DEFAULT_REQUEST_AMOUNT: Int = 256 internal val config = Config::class.java.getResource("/kohttp.yaml")?.let { val mapper = ObjectMapper(YAMLFactory()).registerKotlinModule() @@ -22,7 +23,24 @@ internal data class ClientConfig ( @JsonProperty("connectionPool") val connectionPoolConfig: ConnectionPoolConfig = ConnectionPoolConfig(), val followRedirects: Boolean = true, - val followSslRedirects: Boolean = true + val followSslRedirects: Boolean = true, + val dispatcher: DispatcherConfig = DispatcherConfig() +) + +internal data class DispatcherConfig ( + /** + * Set the maximum number of requests to execute concurrently. + * + * @see okhttp3.Dispatcher.setMaxRequests + */ + val maxRequests: Int = DEFAULT_REQUEST_AMOUNT, + + /** + * Set the maximum number of requests for each host to execute concurrently. + * + * @see okhttp3.Dispatcher.setMaxRequestsPerHost + */ + val maxRequestsPerHost: Int = DEFAULT_REQUEST_AMOUNT ) internal data class ConnectionPoolConfig( diff --git a/src/test/resources/kohttp.yaml b/src/test/resources/kohttp.yaml index d91467cf..85750056 100644 --- a/src/test/resources/kohttp.yaml +++ b/src/test/resources/kohttp.yaml @@ -7,3 +7,6 @@ client: connectionPool: maxIdleConnections: 42 keepAliveDuration: 10000 + dispatcher: + maxRequests: 256 + maxRequestsPerHost: 256 From 109ee34b1f4ec4ea74fdbe445e35320d5137bfc7 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Mon, 27 May 2019 17:20:01 +0300 Subject: [PATCH 08/38] Add tests for new dsl methods --- .../kohttp/dsl/async/AsyncHttpGetDslTest.kt | 50 ------------------- .../dsl/async/HttpDeleteAsyncDslTest.kt | 29 +++++++++++ .../kohttp/dsl/async/HttpGetAsyncDslTest.kt | 27 ++++++++++ .../kohttp/dsl/async/HttpHeadAsyncDslTest.kt | 31 ++++++++++++ .../kohttp/dsl/async/HttpPatchAsyncDslTest.kt | 30 +++++++++++ .../kohttp/dsl/async/HttpPostAsyncDslTest.kt | 32 ++++++++++++ .../kohttp/dsl/async/HttpPutAsyncDslTest.kt | 30 +++++++++++ .../kohttp/dsl/async/UploadAsyncDslTest.kt | 33 ++++++++++++ .../rybalkinsd/kohttp/ext/FileAsyncExtTest.kt | 32 ++++++++++++ .../ext/{FileExtKtTest.kt => FileExtTest.kt} | 2 +- ...ringExtKtTest.kt => StringAsyncExtTest.kt} | 2 +- .../{StringExtKtTest.kt => StringExtTest.kt} | 2 +- .../rybalkinsd/kohttp/ext/UriAsyncExtTest.kt | 30 +++++++++++ .../AsyncHttpGetPerformanceTest.kt | 4 +- 14 files changed, 279 insertions(+), 55 deletions(-) create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDslTest.kt create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDslTest.kt create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDslTest.kt create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDslTest.kt create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDslTest.kt create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDslTest.kt create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt rename src/test/kotlin/io/github/rybalkinsd/kohttp/ext/{FileExtKtTest.kt => FileExtTest.kt} (98%) rename src/test/kotlin/io/github/rybalkinsd/kohttp/ext/{AsyncStringExtKtTest.kt => StringAsyncExtTest.kt} (95%) rename src/test/kotlin/io/github/rybalkinsd/kohttp/ext/{StringExtKtTest.kt => StringExtTest.kt} (97%) create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDslTest.kt index 952b87c6..e69de29b 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDslTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDslTest.kt @@ -1,50 +0,0 @@ -package io.github.rybalkinsd.kohttp.dsl.async - -import io.github.rybalkinsd.kohttp.assertResponses -import io.github.rybalkinsd.kohttp.ext.asJson -import kotlinx.coroutines.runBlocking -import org.junit.Test -import kotlin.test.assertEquals - -/** - * @author sergey - */ -class AsyncHttpGetDslTest { - - @Test - fun `async http get request`() { - val expectedHeader = mapOf( - "one" to "42", - "two" to "123" - ) - - val expectedParams = mapOf( - "text" to "iphone", - "lr" to "213" - ) - - val response = httpGetAsync { - host = "postman-echo.com" - path = "/get" - - header { - "one" to 42 - "two" to "123" - } - - param { - "text" to "iphone" - "lr" to 213 - } - } - - runBlocking { - response.await().use { - val parsedResponse = it.asJson() - assertResponses(expectedHeader, parsedResponse["headers"]) - assertResponses(expectedParams, parsedResponse["args"]) - assertEquals(200, it.code()) - } - } - } -} diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDslTest.kt new file mode 100644 index 00000000..cddfebe0 --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDslTest.kt @@ -0,0 +1,29 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.assertResponses +import io.github.rybalkinsd.kohttp.util.asJson +import kotlinx.coroutines.runBlocking +import org.junit.Assert +import org.junit.Test +import kotlin.test.assertEquals + +/** + * @author sergey, gokul, evgeny + */ +class HttpDeleteAsyncDslTest { + + @Test + fun `delete request with form # postman echo`() { + val response = httpDeleteAsync { + host = "postman-echo.com" + path = "/delete" + } + Assert.assertFalse("After coroutine call, we must have not-ready response", response.isCompleted) + + runBlocking { + response.await().use { + assertEquals(200, it.code()) + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDslTest.kt new file mode 100644 index 00000000..8058560f --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDslTest.kt @@ -0,0 +1,27 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import kotlinx.coroutines.runBlocking +import org.junit.Assert +import org.junit.Test +import kotlin.test.assertEquals + +/** + * @author sergey + */ +class HttpGetAsyncDslTest { + + @Test + fun `async http get request`() { + val response = httpGetAsync { + host = "postman-echo.com" + path = "/delay/1" + } + Assert.assertFalse("After coroutine call, we must have not-ready response", response.isCompleted) + + runBlocking { + response.await().use { + assertEquals(200, it.code()) + } + } + } +} diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDslTest.kt new file mode 100644 index 00000000..cdf83e88 --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDslTest.kt @@ -0,0 +1,31 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.dsl.httpHead +import io.github.rybalkinsd.kohttp.util.json +import kotlinx.coroutines.runBlocking +import org.junit.Assert +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +/** + * @author sergey + */ +class HttpHeadAsyncDslTest { + + @Test + fun `single sync http head invocation with param`() { + val response = httpHeadAsync { + host = "yandex.ru" + path = "/search" + port = 80 + } + Assert.assertFalse("After coroutine call, we must have not-ready response", response.isCompleted) + + runBlocking { + response.await().use { + assertEquals(200, it.code()) + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDslTest.kt new file mode 100644 index 00000000..8e7093a8 --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDslTest.kt @@ -0,0 +1,30 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.assertResponses +import io.github.rybalkinsd.kohttp.dsl.httpPatch +import io.github.rybalkinsd.kohttp.util.asJson +import kotlinx.coroutines.runBlocking +import org.junit.Assert +import org.junit.Test +import kotlin.test.assertEquals + +/** + * @author bpaxio, gokul, evgeny + */ +class HttpPatchAsyncDslTest { + + @Test + fun `patch request with form # postman echo`() { + val response = httpPatchAsync { + host = "postman-echo.com" + path = "/patch" + } + Assert.assertFalse("After coroutine call, we must have not-ready response", response.isCompleted) + + runBlocking { + response.await().use { + assertEquals(200, it.code()) + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDslTest.kt new file mode 100644 index 00000000..2259b81f --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDslTest.kt @@ -0,0 +1,32 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.assertResponses +import io.github.rybalkinsd.kohttp.dsl.httpPost +import io.github.rybalkinsd.kohttp.util.asJson +import kotlinx.coroutines.runBlocking +import org.junit.Assert +import org.junit.Test +import java.io.File +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +/** + * @author sergey, alex, gokul + */ +class HttpPostAsyncDslTest { + + @Test + fun `post request with form # postman echo`() { + val response = httpPostAsync { + host = "postman-echo.com" + path = "/post" + } + Assert.assertFalse("After coroutine call, we must have not-ready response", response.isCompleted) + + runBlocking { + response.await().use { + assertEquals(200, it.code()) + } + } + } +} diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDslTest.kt new file mode 100644 index 00000000..679c9afc --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDslTest.kt @@ -0,0 +1,30 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.assertResponses +import io.github.rybalkinsd.kohttp.dsl.httpPut +import io.github.rybalkinsd.kohttp.util.asJson +import kotlinx.coroutines.runBlocking +import org.junit.Assert +import org.junit.Test +import kotlin.test.assertEquals + +/** + * @author sergey, gokul + */ +class HttpPutAsyncDslTest { + + @Test + fun `put request with form # postman echo`() { + val response = httpPutAsync { + host = "postman-echo.com" + path = "/put" + } + Assert.assertFalse("After coroutine call, we must have not-ready response", response.isCompleted) + + runBlocking { + response.await().use { + assertEquals(200, it.code()) + } + } + } +} \ No newline at end of file diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt new file mode 100644 index 00000000..2f6750c9 --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt @@ -0,0 +1,33 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.dsl.httpGet +import io.github.rybalkinsd.kohttp.dsl.upload +import io.github.rybalkinsd.kohttp.util.asJson +import kotlinx.coroutines.runBlocking +import org.junit.Assert +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class UploadAsyncDslTest { + + @Test + fun `small file upload`() { + val response = uploadAsync { + url("http://postman-echo.com/post") + val fileUri = this.javaClass.getResource("/cat.gif").toURI() + file(fileUri) + } + Assert.assertFalse("After coroutine call, we must have not-ready response", response.isCompleted) + + runBlocking { + response.await().use { + val parsedResponse = it.body()?.string().asJson() + + assertEquals(200, it.code()) + assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt()) + assertTrue { parsedResponse["headers"]["content-type"].asText().startsWith("multipart/mixed; boundary=") } + } + } + } +} diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt new file mode 100644 index 00000000..9c90818d --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt @@ -0,0 +1,32 @@ +package io.github.rybalkinsd.kohttp.ext + +import io.github.rybalkinsd.kohttp.util.asJson +import kotlinx.coroutines.runBlocking +import org.junit.Test +import java.io.File +import java.net.URL +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +/** + * @author evgeny + */ +class FileAsyncExtTest { + + @Test + fun `upload file using File and string destination`() { + val file = File(this.javaClass.getResource("/cat.gif").toURI()) + val response = file.uploadAsync("http://postman-echo.com/post") + + runBlocking { + response.await().use { + val parsedResponse = it.body()?.string().asJson() + + assertEquals(200, it.code()) + assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt()) + assertTrue { parsedResponse["headers"]["content-type"].asText().startsWith("multipart/mixed; boundary=") } + } + } + } + +} \ No newline at end of file diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileExtTest.kt similarity index 98% rename from src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileExtKtTest.kt rename to src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileExtTest.kt index 0de1ec2e..6478783c 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileExtKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileExtTest.kt @@ -6,7 +6,7 @@ import java.net.URL import kotlin.test.assertEquals import kotlin.test.assertTrue -class FileExtKtTest { +class FileExtTest { @Test fun `upload file using File and string destination`() { diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/AsyncStringExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/StringAsyncExtTest.kt similarity index 95% rename from src/test/kotlin/io/github/rybalkinsd/kohttp/ext/AsyncStringExtKtTest.kt rename to src/test/kotlin/io/github/rybalkinsd/kohttp/ext/StringAsyncExtTest.kt index 64b7c464..99d22de6 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/AsyncStringExtKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/StringAsyncExtTest.kt @@ -8,7 +8,7 @@ import kotlin.test.assertEquals /** * @author sergey */ -class AsyncStringExtKtTest { +class StringAsyncExtTest { @Test fun `many async invokes of httpGet`() { diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/StringExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/StringExtTest.kt similarity index 97% rename from src/test/kotlin/io/github/rybalkinsd/kohttp/ext/StringExtKtTest.kt rename to src/test/kotlin/io/github/rybalkinsd/kohttp/ext/StringExtTest.kt index 5aae4e1b..1b8b3aa1 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/StringExtKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/StringExtTest.kt @@ -7,7 +7,7 @@ import kotlin.test.assertEquals /** * @author sergey */ -class StringExtKtTest { +class StringExtTest { @Test fun `single sync invoke of httpGet`() { diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt new file mode 100644 index 00000000..94086a36 --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt @@ -0,0 +1,30 @@ +package io.github.rybalkinsd.kohttp.ext + +import io.github.rybalkinsd.kohttp.util.asJson +import kotlinx.coroutines.runBlocking +import org.junit.Test +import java.net.URL +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +/** + * @author evgeny + */ +class UriAsyncExtTest { + + @Test + fun `upload file using URI and string destination`() { + val fileUri = this.javaClass.getResource("/cat.gif").toURI() + val response = fileUri.uploadAsync("http://postman-echo.com/post") + + runBlocking { + response.await().use { + val parsedResponse = it.body()?.string().asJson() + + assertEquals(200, it.code()) + assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt()) + assertTrue { parsedResponse["headers"]["content-type"].asText().startsWith("multipart/mixed; boundary=") } + } + } + } +} diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt index 405d0553..3f019f9f 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt @@ -30,7 +30,7 @@ import kotlin.system.measureTimeMillis * Compare result with another alternatives * that also presented in this class * - * Time in millis will be written in console + * Requests' statuses and time in millis will be written in console * @since 0.10.0 * @author evgeny */ @@ -62,7 +62,7 @@ class AsyncHttpGetPerformanceTest { @Test fun `100 simple async requests with old Call-Factory-suspendCall extension`() { measureTimeMillis { - val rs = List(1000) { + val rs = List(100) { oldAsyncHttpGet(client = pseudoDefault) { url("https://postman-echo.com/delay/2") } From 07d1f8a5e08e5343e26913c0d8e31f296a4fce1c Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Mon, 27 May 2019 17:35:26 +0300 Subject: [PATCH 09/38] Optimized imports and base code style changes --- .../rybalkinsd/kohttp/client/ClientBuilder.kt | 2 +- .../rybalkinsd/kohttp/configuration/Config.kt | 8 +-- .../github/rybalkinsd/kohttp/dsl/UploadDsl.kt | 8 +-- .../kohttp/dsl/async/HttpDeleteAsyncDsl.kt | 1 - .../kohttp/dsl/async/HttpHeadAsyncDsl.kt | 1 - .../kohttp/dsl/async/HttpPatchAsyncDsl.kt | 1 - .../kohttp/dsl/async/HttpPutAsyncDsl.kt | 1 - .../kohttp/dsl/async/UploadAsyncDsl.kt | 9 ++- .../kohttp/dsl/context/CookieContext.kt | 2 +- .../dsl/context/MultipartBodyContext.kt | 8 +-- .../github/rybalkinsd/kohttp/ext/CallExt.kt | 20 +++--- .../kohttp/pool/CoroutineExecutorService.kt | 1 - .../io/github/rybalkinsd/kohttp/util/json.kt | 8 +-- .../kohttp/client/ClientBuilderTest.kt | 69 ++++++++----------- .../kohttp/client/ForkClientBuilderTest.kt | 20 +++--- .../kohttp/client/dsl/InterceptorsDslTest.kt | 6 +- .../kohttp/dsl/HttpDeleteDslKtTest.kt | 2 +- .../rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt | 14 ++-- .../kohttp/dsl/HttpPostDslKtTest.kt | 18 ++--- .../rybalkinsd/kohttp/dsl/UploadDslKtTest.kt | 1 - .../dsl/async/HttpDeleteAsyncDslTest.kt | 2 - .../kohttp/dsl/async/HttpHeadAsyncDslTest.kt | 3 - .../kohttp/dsl/async/HttpPatchAsyncDslTest.kt | 3 - .../kohttp/dsl/async/HttpPostAsyncDslTest.kt | 5 -- .../kohttp/dsl/async/HttpPutAsyncDslTest.kt | 3 - .../kohttp/dsl/async/UploadAsyncDslTest.kt | 4 +- .../rybalkinsd/kohttp/ext/FileAsyncExtTest.kt | 1 - .../rybalkinsd/kohttp/ext/HeadersExtKtTest.kt | 12 ++-- .../kohttp/ext/HttpContextExtKtTest.kt | 6 +- .../rybalkinsd/kohttp/ext/UriAsyncExtTest.kt | 1 - .../interceptors/LoggingInterceptorTest.kt | 2 +- .../github/rybalkinsd/kohttp/util/FormTest.kt | 2 +- .../rybalkinsd/kohttp/util/JsonKtTest.kt | 4 +- .../rybalkinsd/kohttp/util/StringExt.kt | 0 34 files changed, 106 insertions(+), 142 deletions(-) create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/util/StringExt.kt diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilder.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilder.kt index 471f51fd..39f2d53b 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilder.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilder.kt @@ -74,7 +74,7 @@ interface ClientBuilder : ForkClientBuilder { interface ForkClientBuilder { @get:Deprecated(level = DeprecationLevel.ERROR, message = "Write only field") - var interceptors: List + var interceptors: List @get:Deprecated(level = DeprecationLevel.ERROR, message = "Write only field") var networkInterceptors: List diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt index 5c3c9f26..231e1aa4 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt @@ -16,7 +16,7 @@ internal val config = Config::class.java.getResource("/kohttp.yaml")?.let { internal data class Config(val client: ClientConfig = ClientConfig()) -internal data class ClientConfig ( +internal data class ClientConfig( val connectTimeout: Long = TimeUnit.SECONDS.toMillis(10), val readTimeout: Long = TimeUnit.SECONDS.toMillis(10), val writeTimeout: Long = TimeUnit.SECONDS.toMillis(10), @@ -27,7 +27,7 @@ internal data class ClientConfig ( val dispatcher: DispatcherConfig = DispatcherConfig() ) -internal data class DispatcherConfig ( +internal data class DispatcherConfig( /** * Set the maximum number of requests to execute concurrently. * @@ -44,8 +44,8 @@ internal data class DispatcherConfig ( ) internal data class ConnectionPoolConfig( - val maxIdleConnections: Int = 5, - val keepAliveDuration: Long = TimeUnit.MINUTES.toMillis(5) + val maxIdleConnections: Int = 5, + val keepAliveDuration: Long = TimeUnit.MINUTES.toMillis(5) ) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt index 456fcbdf..d7c9799d 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt @@ -14,10 +14,10 @@ import okhttp3.Response * *
  *  val response: Response = upload {
-        url("http://postman-echo.com/post")
-        val fileUri = this.javaClass.getResource("/cat.gif").toURI()
-        file(fileUri)
-    }
+ *      url("http://postman-echo.com/post")
+ *      val fileUri = this.javaClass.getResource("/cat.gif").toURI()
+ *      file(fileUri)
+ *  }
  *  response.use { ... }
  *  
* diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt index 949e6313..47c5996f 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt @@ -2,7 +2,6 @@ package io.github.rybalkinsd.kohttp.dsl.async import io.github.rybalkinsd.kohttp.client.defaultHttpClient import io.github.rybalkinsd.kohttp.dsl.context.HttpDeleteContext -import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext import io.github.rybalkinsd.kohttp.ext.suspendCall import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt index 0478926e..eac326e5 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt @@ -1,7 +1,6 @@ package io.github.rybalkinsd.kohttp.dsl.async import io.github.rybalkinsd.kohttp.client.defaultHttpClient -import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext import io.github.rybalkinsd.kohttp.dsl.context.HttpHeadContext import io.github.rybalkinsd.kohttp.ext.suspendCall import kotlinx.coroutines.Deferred diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt index c58dafc6..19887c4b 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt @@ -1,7 +1,6 @@ package io.github.rybalkinsd.kohttp.dsl.async import io.github.rybalkinsd.kohttp.client.defaultHttpClient -import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext import io.github.rybalkinsd.kohttp.dsl.context.HttpPatchContext import io.github.rybalkinsd.kohttp.ext.suspendCall import kotlinx.coroutines.Deferred diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt index c6c23468..0f0d2b0b 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt @@ -1,7 +1,6 @@ package io.github.rybalkinsd.kohttp.dsl.async import io.github.rybalkinsd.kohttp.client.defaultHttpClient -import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext import io.github.rybalkinsd.kohttp.dsl.context.HttpPutContext import io.github.rybalkinsd.kohttp.ext.suspendCall import kotlinx.coroutines.Deferred diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt index 7d5c34f0..3fb2bf98 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt @@ -1,7 +1,6 @@ package io.github.rybalkinsd.kohttp.dsl.async import io.github.rybalkinsd.kohttp.client.defaultHttpClient -import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext import io.github.rybalkinsd.kohttp.dsl.context.UploadContext import io.github.rybalkinsd.kohttp.ext.suspendCall import kotlinx.coroutines.Deferred @@ -20,10 +19,10 @@ import okhttp3.Response * *
  *  val response: Deferred = uploadAsync {
-        url("http://postman-echo.com/post")
-        val fileUri = this.javaClass.getResource("/cat.gif").toURI()
-        file(fileUri)
-    }
+ *      url("http://postman-echo.com/post")
+ *      val fileUri = this.javaClass.getResource("/cat.gif").toURI()
+ *      file(fileUri)
+ *  }
  *  response.await().use { ... }
  *  
* diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/CookieContext.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/CookieContext.kt index fdf4fa3f..35640999 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/CookieContext.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/CookieContext.kt @@ -12,5 +12,5 @@ class CookieContext { cookies += Pair(this, v) } - internal fun collect(): String = cookies.joinToString(separator = "; ") { (k, v) -> "$k=$v"} + internal fun collect(): String = cookies.joinToString(separator = "; ") { (k, v) -> "$k=$v" } } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/MultipartBodyContext.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/MultipartBodyContext.kt index 4cfd4bd5..4682900d 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/MultipartBodyContext.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/MultipartBodyContext.kt @@ -21,11 +21,11 @@ class MultipartBodyContext(type: String?) { builder.addFormDataPart(first, second, third) } - fun form(name: String, file: File): FormDataPart - = FormDataPart(name, file.name, RequestBody.create(null, file)) + fun form(name: String, file: File): FormDataPart = + FormDataPart(name, file.name, RequestBody.create(null, file)) - fun form(name: String, filename: String, content: ByteArray): FormDataPart - = FormDataPart(name, filename, RequestBody.create(null, content)) + fun form(name: String, filename: String, content: ByteArray): FormDataPart = + FormDataPart(name, filename, RequestBody.create(null, content)) fun build(): MultipartBody = builder.build() } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt index 0d45fe6a..a89ea473 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt @@ -12,15 +12,15 @@ import kotlin.coroutines.suspendCoroutine internal fun Call.Factory.call(request: Request): Response = newCall(request).execute() internal suspend fun Call.Factory.suspendCall(request: Request): Response = - suspendCoroutine { cont -> - newCall(request).enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - cont.resume(response) - } + suspendCoroutine { cont -> + newCall(request).enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + cont.resume(response) + } - override fun onFailure(call: Call, e: IOException) { - cont.resumeWithException(e) - } - }) - } + override fun onFailure(call: Call, e: IOException) { + cont.resumeWithException(e) + } + }) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt index 0cdd472d..26420cbe 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt @@ -4,7 +4,6 @@ import kotlinx.coroutines.* import java.util.concurrent.Executor import java.util.concurrent.ExecutorService import java.util.concurrent.ForkJoinPool -import javax.xml.bind.JAXBElement import kotlin.coroutines.CoroutineContext /** diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt index 332d4dcb..22aaf67b 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt @@ -20,7 +20,7 @@ class Json { infix fun String.to(obj: List<*>) { val v = obj.joinToString(separator = ",", prefix = "[", postfix = "]") { - when(it) { + when (it) { is Number, is Json -> it.toString() else -> """"$it"""" } @@ -41,7 +41,7 @@ class Json { } override fun toString(): String = - elements.joinToString(separator = ",", prefix = "{", postfix = "}") { - (k,v) -> """"$k":$v""" - } + elements.joinToString(separator = ",", prefix = "{", postfix = "}") { (k, v) -> + """"$k":$v""" + } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilderTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilderTest.kt index af4b7650..c8ef5e73 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilderTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilderTest.kt @@ -1,15 +1,6 @@ package io.github.rybalkinsd.kohttp.client -import okhttp3.Authenticator -import okhttp3.CertificatePinner -import okhttp3.ConnectionPool -import okhttp3.ConnectionSpec -import okhttp3.CookieJar -import okhttp3.Dispatcher -import okhttp3.Dns -import okhttp3.EventListener -import okhttp3.OkHttpClient -import okhttp3.Protocol +import okhttp3.* import okhttp3.internal.tls.OkHostnameVerifier import org.junit.Test import sun.security.ssl.SSLSocketFactoryImpl @@ -70,29 +61,29 @@ class ClientBuilderTest { } val client = OkHttpClient.Builder() - .dispatcher(defaultDispatcher) - .proxy(defaultProxy) - .protocols(defaultProtocols) - .connectionSpecs(defaultConnectionSpecs) - .eventListenerFactory(defaultFactory) - .proxySelector(defaultProxySelector) - .cookieJar(defaultCookieJar) - .socketFactory(defaultSocketFactory) - .sslSocketFactory(defaultSslSocketFactory) - .hostnameVerifier(defaultHostnameVerifier) - .certificatePinner(defaultCertificatePinner) - .proxyAuthenticator(defaultAuth) - .authenticator(defaultAuth) - .connectionPool(defaultConnectionPool) - .dns(defaultDns) - .followSslRedirects(true) - .followRedirects(true) - .retryOnConnectionFailure(true) - .connectTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .readTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .writeTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .pingInterval(0, TimeUnit.MILLISECONDS) - .build() + .dispatcher(defaultDispatcher) + .proxy(defaultProxy) + .protocols(defaultProtocols) + .connectionSpecs(defaultConnectionSpecs) + .eventListenerFactory(defaultFactory) + .proxySelector(defaultProxySelector) + .cookieJar(defaultCookieJar) + .socketFactory(defaultSocketFactory) + .sslSocketFactory(defaultSslSocketFactory) + .hostnameVerifier(defaultHostnameVerifier) + .certificatePinner(defaultCertificatePinner) + .proxyAuthenticator(defaultAuth) + .authenticator(defaultAuth) + .connectionPool(defaultConnectionPool) + .dns(defaultDns) + .followSslRedirects(true) + .followRedirects(true) + .retryOnConnectionFailure(true) + .connectTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .readTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .writeTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .pingInterval(0, TimeUnit.MILLISECONDS) + .build() with(client) { assertEquals(dispatcher(), dslClient.dispatcher()) @@ -124,13 +115,13 @@ class ClientBuilderTest { fun `client builder getters throw exceptions`() { val clientBuilder = ClientBuilderImpl() ClientBuilderImpl::class.declaredMemberProperties.filter { !it.isFinal } - .map { - try { - it.call(clientBuilder) - assertTrue(false, "${it.name} call is successful, but not expected to be") - } catch (ignored: InvocationTargetException) { + .map { + try { + it.call(clientBuilder) + assertTrue(false, "${it.name} call is successful, but not expected to be") + } catch (ignored: InvocationTargetException) { + } } - } } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ForkClientBuilderTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ForkClientBuilderTest.kt index 180f105d..12afa598 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ForkClientBuilderTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ForkClientBuilderTest.kt @@ -16,16 +16,16 @@ class ForkClientBuilderTest { val defaultDns = Dns { emptyList() } val client = defaultHttpClient.newBuilder() - .cache(null) - .dns(defaultDns) - .followSslRedirects(false) - .followRedirects(false) - .retryOnConnectionFailure(false) - .connectTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .readTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .writeTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .pingInterval(defaultTimeout, TimeUnit.MILLISECONDS) - .build() + .cache(null) + .dns(defaultDns) + .followSslRedirects(false) + .followRedirects(false) + .retryOnConnectionFailure(false) + .connectTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .readTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .writeTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .pingInterval(defaultTimeout, TimeUnit.MILLISECONDS) + .build() val dslClient = defaultHttpClient.fork { interceptors = emptyList() diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/client/dsl/InterceptorsDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/client/dsl/InterceptorsDslTest.kt index b1ef9863..2f9576fd 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/client/dsl/InterceptorsDslTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/client/dsl/InterceptorsDslTest.kt @@ -14,7 +14,7 @@ private class TestInterceptor : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val requestTime = Instant.now().epochSecond val response = chain.proceed(chain.request()) - return with(response.newBuilder()) { + return with(response.newBuilder()) { addHeader("x-response-time", "${Instant.now().epochSecond - requestTime}") build() } @@ -27,12 +27,12 @@ class InterceptorsDslTest { fun `adds interceptor to client`() { val client = defaultHttpClient.fork { interceptors { - + TestInterceptor() + +TestInterceptor() } } "https://postman-echo.com/get".httpGet(client = client).use { - assertTrue { it.header("x-response-time") != null } + assertTrue { it.header("x-response-time") != null } } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt index 69152f99..781e2c78 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt @@ -68,7 +68,7 @@ class HttpDeleteDslKtTest { "arg" to "iphone" ) - val expectedJson= mapOf( + val expectedJson = mapOf( "login" to "user", "email" to "john.doe@gmail.com" ) diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt index 629bd8a9..f70e5abf 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt @@ -103,15 +103,15 @@ class HttpGetDslKtTest { fun `single sync http get invocation with url`() { val variable = 123L val expectedHeader = mapOf( - "one" to "42", - "two" to variable.toString(), - "three" to """{"a":$variable,"b":{"b1":"512"},"c":[1,2.0,3]}""", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "two" to variable.toString(), + "three" to """{"a":$variable,"b":{"b1":"512"},"c":[1,2.0,3]}""", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "text" to "iphone", - "lr" to "213" + "text" to "iphone", + "lr" to "213" ) val response = httpGet { url("http://postman-echo.com/get") @@ -157,7 +157,7 @@ class HttpGetDslKtTest { } val expectedParams = mapOf( - "c" to "exists" + "c" to "exists" ) response.use { diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt index add2a12f..544e1f31 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt @@ -62,8 +62,8 @@ class HttpPostDslKtTest { @Test fun `post request with form encoded # postman echo`() { val expectedForm = mapOf( - "encoded" to " ", - "notEncoded" to "%20" + "encoded" to " ", + "notEncoded" to "%20" ) httpPost { @@ -109,7 +109,7 @@ class HttpPostDslKtTest { "arg" to "iphone" ) - val expectedJson= mapOf( + val expectedJson = mapOf( "login" to "user", "email" to "john.doe@gmail.com" ) @@ -157,9 +157,9 @@ class HttpPostDslKtTest { } } - val expectedJson= mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + val expectedJson = mapOf( + "login" to "user", + "email" to "john.doe@gmail.com" ) response.use { @@ -180,9 +180,9 @@ class HttpPostDslKtTest { } } - val expectedJson= mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + val expectedJson = mapOf( + "login" to "user", + "email" to "john.doe@gmail.com" ) response.use { diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt index b5802481..ab12496f 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt @@ -11,7 +11,6 @@ class UploadDslKtTest { @Test fun `small file upload`() { val r = upload { - url("http://postman-echo.com/post") val fileUri = this.javaClass.getResource("/cat.gif").toURI() file(fileUri) diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDslTest.kt index cddfebe0..f329ceb5 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDslTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDslTest.kt @@ -1,7 +1,5 @@ package io.github.rybalkinsd.kohttp.dsl.async -import io.github.rybalkinsd.kohttp.assertResponses -import io.github.rybalkinsd.kohttp.util.asJson import kotlinx.coroutines.runBlocking import org.junit.Assert import org.junit.Test diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDslTest.kt index cdf83e88..c5381b86 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDslTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDslTest.kt @@ -1,12 +1,9 @@ package io.github.rybalkinsd.kohttp.dsl.async -import io.github.rybalkinsd.kohttp.dsl.httpHead -import io.github.rybalkinsd.kohttp.util.json import kotlinx.coroutines.runBlocking import org.junit.Assert import org.junit.Test import kotlin.test.assertEquals -import kotlin.test.assertTrue /** * @author sergey diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDslTest.kt index 8e7093a8..60893125 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDslTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDslTest.kt @@ -1,8 +1,5 @@ package io.github.rybalkinsd.kohttp.dsl.async -import io.github.rybalkinsd.kohttp.assertResponses -import io.github.rybalkinsd.kohttp.dsl.httpPatch -import io.github.rybalkinsd.kohttp.util.asJson import kotlinx.coroutines.runBlocking import org.junit.Assert import org.junit.Test diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDslTest.kt index 2259b81f..6ba4260c 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDslTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDslTest.kt @@ -1,14 +1,9 @@ package io.github.rybalkinsd.kohttp.dsl.async -import io.github.rybalkinsd.kohttp.assertResponses -import io.github.rybalkinsd.kohttp.dsl.httpPost -import io.github.rybalkinsd.kohttp.util.asJson import kotlinx.coroutines.runBlocking import org.junit.Assert import org.junit.Test -import java.io.File import kotlin.test.assertEquals -import kotlin.test.assertTrue /** * @author sergey, alex, gokul diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDslTest.kt index 679c9afc..59a2215b 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDslTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDslTest.kt @@ -1,8 +1,5 @@ package io.github.rybalkinsd.kohttp.dsl.async -import io.github.rybalkinsd.kohttp.assertResponses -import io.github.rybalkinsd.kohttp.dsl.httpPut -import io.github.rybalkinsd.kohttp.util.asJson import kotlinx.coroutines.runBlocking import org.junit.Assert import org.junit.Test diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt index 2f6750c9..21bde975 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt @@ -1,7 +1,5 @@ package io.github.rybalkinsd.kohttp.dsl.async -import io.github.rybalkinsd.kohttp.dsl.httpGet -import io.github.rybalkinsd.kohttp.dsl.upload import io.github.rybalkinsd.kohttp.util.asJson import kotlinx.coroutines.runBlocking import org.junit.Assert @@ -13,7 +11,7 @@ class UploadAsyncDslTest { @Test fun `small file upload`() { - val response = uploadAsync { + val response = uploadAsync { url("http://postman-echo.com/post") val fileUri = this.javaClass.getResource("/cat.gif").toURI() file(fileUri) diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt index 9c90818d..662bbc6b 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt @@ -4,7 +4,6 @@ import io.github.rybalkinsd.kohttp.util.asJson import kotlinx.coroutines.runBlocking import org.junit.Test import java.io.File -import java.net.URL import kotlin.test.assertEquals import kotlin.test.assertTrue diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt index 002f5fcd..532a349d 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt @@ -16,16 +16,16 @@ class HeadersExtKtTest { @Test fun `filter and sum`() { val sum = headers.asSequence().filter { it.name.contains('o') } - .sumBy { it.value.toInt() } + .sumBy { it.value.toInt() } assertEquals(3, sum) } companion object { val headers = Headers.Builder() - .add("zero", "0") - .add("one", "1") - .add("two", "2") - .add("three", "3") - .build() + .add("zero", "0") + .add("one", "1") + .add("two", "2") + .add("three", "3") + .build() } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt index 10b5a8f4..afe3e68a 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt @@ -44,18 +44,18 @@ class HttpContextExtKtTest { // expecting NPE for b/c of protocol @Test(expected = NullPointerException::class) fun `null protocol url`() { - HttpGetContext().apply { url(URL(null, null,0, "cat.gif")) } + HttpGetContext().apply { url(URL(null, null, 0, "cat.gif")) } } @Test(expected = IllegalArgumentException::class) fun `not http or https protocol url`() { - HttpGetContext().apply { url(URL("file", null,0, "cat.gif")) } + HttpGetContext().apply { url(URL("file", null, 0, "cat.gif")) } } @Test(expected = IllegalArgumentException::class) fun `null host url`() { - HttpGetContext().apply { url(URL("https", null,0, "cat.gif")) } + HttpGetContext().apply { url(URL("https", null, 0, "cat.gif")) } } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt index 94086a36..db0ee75d 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt @@ -3,7 +3,6 @@ package io.github.rybalkinsd.kohttp.ext import io.github.rybalkinsd.kohttp.util.asJson import kotlinx.coroutines.runBlocking import org.junit.Test -import java.net.URL import kotlin.test.assertEquals import kotlin.test.assertTrue diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/interceptors/LoggingInterceptorTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/interceptors/LoggingInterceptorTest.kt index 83b5fa7d..91083194 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/interceptors/LoggingInterceptorTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/interceptors/LoggingInterceptorTest.kt @@ -33,7 +33,7 @@ class LoggingInterceptorTest { } } - val response = "https://postman-echo.com/get".httpGet(client) + val response = "https://postman-echo.com/get".httpGet(client) assertEquals(200, response.code()) } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/util/FormTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/util/FormTest.kt index 57d7e011..4da65f68 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/util/FormTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/util/FormTest.kt @@ -32,6 +32,6 @@ class FormTest { } private operator fun FormBody.contains(k: String) = - (0 until size()).asSequence().any { encodedName(it) == k } + (0 until size()).asSequence().any { encodedName(it) == k } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/util/JsonKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/util/JsonKtTest.kt index 95d02b63..2851d90b 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/util/JsonKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/util/JsonKtTest.kt @@ -13,7 +13,7 @@ class JsonKtTest { val json = json { "a" to "1" } - assertEquals( """{"a":"1"}""", json) + assertEquals("""{"a":"1"}""", json) } @Test @@ -22,7 +22,7 @@ class JsonKtTest { "a" to "1" "b" to 2 } - assertEquals( """{"a":"1","b":2}""", json) + assertEquals("""{"a":"1","b":2}""", json) } @Test diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/util/StringExt.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/util/StringExt.kt new file mode 100644 index 00000000..e69de29b From 5fbe0f4f8009d78180dcb86c453836abc32442c8 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Mon, 27 May 2019 17:48:46 +0300 Subject: [PATCH 10/38] Solve problem with upload test --- .../kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt index ab12496f..f671e87e 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt @@ -38,6 +38,7 @@ class UploadDslKtTest { } assertEquals(200, uploadResponse.code()) + assertEquals(1 * 1024 * 1024 + 173, uploadResponse.asJson()["headers"]["content-length"].asInt()) assertEquals(1048749, uploadResponse.asJson()["headers"]["content-length"].asInt()) } From 5bc077209d28f8c048fe95bf847d282c7c691a13 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Mon, 27 May 2019 17:50:36 +0300 Subject: [PATCH 11/38] Change project coroutine dispatchers to Unconfined --- src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt index 5cf7248c..33ce423f 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt @@ -66,7 +66,7 @@ fun String.httpGet( fun String.httpGetAsync( client: Call.Factory = defaultHttpClient ): Deferred = - GlobalScope.async(context = Dispatchers.IO) { + GlobalScope.async(context = Dispatchers.Unconfined) { client.call(Request.Builder().url(this@httpGetAsync).build()) } From 1388ad03c49c2a7ee3a72319679643633f542181 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Mon, 27 May 2019 18:07:45 +0300 Subject: [PATCH 12/38] Add URL tests for extensions --- .../rybalkinsd/kohttp/ext/FileAsyncExtTest.kt | 18 +++++++++++++++++- .../rybalkinsd/kohttp/ext/UriAsyncExtTest.kt | 19 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt index 662bbc6b..519c5580 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt @@ -4,6 +4,7 @@ import io.github.rybalkinsd.kohttp.util.asJson import kotlinx.coroutines.runBlocking import org.junit.Test import java.io.File +import java.net.URL import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -13,7 +14,7 @@ import kotlin.test.assertTrue class FileAsyncExtTest { @Test - fun `upload file using File and string destination`() { + fun `async upload file using File and string destination`() { val file = File(this.javaClass.getResource("/cat.gif").toURI()) val response = file.uploadAsync("http://postman-echo.com/post") @@ -28,4 +29,19 @@ class FileAsyncExtTest { } } + @Test + fun `async upload file using File and URL destination`() { + val file = File(this.javaClass.getResource("/cat.gif").toURI()) + val response = file.uploadAsync(URL("http://postman-echo.com/post")) + + runBlocking { + response.await().use { + val parsedResponse = it.body()?.string().asJson() + + assertEquals(200, it.code()) + assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt()) + assertTrue { parsedResponse["headers"]["content-type"].asText().startsWith("multipart/mixed; boundary=") } + } + } + } } \ No newline at end of file diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt index db0ee75d..6592c30c 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt @@ -3,6 +3,7 @@ package io.github.rybalkinsd.kohttp.ext import io.github.rybalkinsd.kohttp.util.asJson import kotlinx.coroutines.runBlocking import org.junit.Test +import java.net.URL import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -12,7 +13,7 @@ import kotlin.test.assertTrue class UriAsyncExtTest { @Test - fun `upload file using URI and string destination`() { + fun `async upload file using URI and string destination`() { val fileUri = this.javaClass.getResource("/cat.gif").toURI() val response = fileUri.uploadAsync("http://postman-echo.com/post") @@ -26,4 +27,20 @@ class UriAsyncExtTest { } } } + + @Test + fun `async upload file using URI and URL destination`() { + val fileUri = this.javaClass.getResource("/cat.gif").toURI() + val response = fileUri.uploadAsync(URL("http://postman-echo.com/post")) + + runBlocking { + response.await().use { + val parsedResponse = it.body()?.string().asJson() + + assertEquals(200, it.code()) + assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt()) + assertTrue { parsedResponse["headers"]["content-type"].asText().startsWith("multipart/mixed; boundary=") } + } + } + } } From bdbc876602803209a78561e66e2bae93ead26ed1 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Tue, 28 May 2019 13:31:00 +0300 Subject: [PATCH 13/38] Change performance test, add khttp performance test to compare them --- build.gradle.kts | 6 ++ .../kohttp/pool/CoroutineExecutorService.kt | 6 +- .../io/github/rybalkinsd/kohttp/Utils.kt | 24 +++++ .../KhttpAsyncHttpGetPerformanceTest.kt | 99 +++++++++++++++++++ ...t => KohttpAsyncHttpGetPerformanceTest.kt} | 72 +++++--------- 5 files changed, 158 insertions(+), 49 deletions(-) create mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KhttpAsyncHttpGetPerformanceTest.kt rename src/test/kotlin/io/github/rybalkinsd/kohttp/performance/{AsyncHttpGetPerformanceTest.kt => KohttpAsyncHttpGetPerformanceTest.kt} (69%) diff --git a/build.gradle.kts b/build.gradle.kts index e7c3bc9c..a9c7224c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,12 +15,18 @@ version = "0.10.0-SNAPSHOT" repositories { mavenCentral() + maven { + url = uri("http://repo.spring.io/libs-release/") + } } dependencies { implementation(kotlin("stdlib-jdk8")) implementation(kotlin("reflect")) implementation("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.2.1") + // https://mvnrepository.com/artifact/khttp/khttp + implementation("khttp:khttp:1.0.0") + val jacksonVersion = "2.9.9" implementation(jackson("core"), "jackson-databind", jacksonVersion) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt index 26420cbe..afe8a08b 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt @@ -1,6 +1,7 @@ package io.github.rybalkinsd.kohttp.pool import kotlinx.coroutines.* +import org.jetbrains.spek.meta.Experimental import java.util.concurrent.Executor import java.util.concurrent.ExecutorService import java.util.concurrent.ForkJoinPool @@ -10,6 +11,7 @@ import kotlin.coroutines.CoroutineContext * @Author: evgeny.vorobyev * @Since: 0.10.0 */ +@Experimental class CoroutineExecutorService( private val executorService: ExecutorService = ForkJoinPool() ) : ExecutorService by executorService, @@ -21,10 +23,8 @@ class CoroutineExecutorService( throw UnsupportedOperationException("${CoroutineExecutorService::class.java.name} can not be closed") } - // todo research about DefaultExecutor.enqueue how it works, how can we implement same logic - // todo watch Roman Elizarov articles/youtube videos and other about coroutines and its logic override fun dispatch(context: CoroutineContext, block: Runnable) { - println("Dispatch request with context ${context::class.java.name}") +// println("Dispatch request with context ${context::class.java.name}") executorService.execute(block) } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt index 245edd89..48220cc8 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt @@ -1,6 +1,11 @@ package io.github.rybalkinsd.kohttp import com.fasterxml.jackson.databind.JsonNode +import io.github.rybalkinsd.kohttp.dsl.async.httpGetAsync +import io.github.rybalkinsd.kohttp.ext.url +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.runBlocking +import okhttp3.Response import kotlin.test.assertEquals /** @@ -11,3 +16,22 @@ fun assertResponses(expected: Map, actual: JsonNode) { assertEquals(u, actual[t]?.asText()) } } + +fun getSuccessfulResponsesAmount( + results: List> +): Int = runBlocking { + results.map { getCode(it) } + .filter { it == 200 } + .size +} + +suspend fun getCode( + result: Deferred +): Int = + try { + result.await().use { + it.code() + } + } catch (_: Throwable) { + -1 + } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KhttpAsyncHttpGetPerformanceTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KhttpAsyncHttpGetPerformanceTest.kt new file mode 100644 index 00000000..00487b73 --- /dev/null +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KhttpAsyncHttpGetPerformanceTest.kt @@ -0,0 +1,99 @@ +package io.github.rybalkinsd.kohttp.performance + +import io.github.rybalkinsd.kohttp.dsl.async.httpGetAsync +import io.github.rybalkinsd.kohttp.ext.url +import io.github.rybalkinsd.kohttp.getCode +import io.github.rybalkinsd.kohttp.getSuccessfulResponsesAmount +import io.github.rybalkinsd.kohttp.pool.CoroutineExecutorService +import khttp.responses.Response +import kotlinx.coroutines.* +import org.junit.Test +import java.util.concurrent.ForkJoinPool +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger +import kotlin.system.measureTimeMillis + +public class KhttpAsyncHttpGetPerformanceTest { + private val parallelizm = 256 + private val requestsAmount = 10000 + private val targetUrl = "https://postman-echo.com/get" + + val completed = AtomicInteger(0) + val failed = AtomicInteger(0) + + @Test + fun `khttp request through forkJoinPool`() { + val pool = ForkJoinPool(parallelizm) + + measureTimeMillis { + repeat(requestsAmount) { + pool.execute { + try { + val response = khttp.get(targetUrl) + + if (response.statusCode == 200) { + completed.incrementAndGet() + } else { + failed.incrementAndGet() + } + } catch (e: Exception) { + failed.incrementAndGet() + } + } + } + pool.shutdown() + pool.awaitTermination(1000, TimeUnit.SECONDS) + }.also { + println(completed) + println(it) + } + } + + @Test + fun `khttp request through kotlin coroutines Dispatchers_IO`() { + measureTimeMillis { + val responses = List(requestsAmount) { + GlobalScope.async(context = Dispatchers.IO) { + khttp.get(targetUrl) + } + } + + runBlocking { + responses.map { getCode(it) } + .filter { it == 200 } + .size + }.also { + println(it) + } + }.also { print(it) } + } + + @Test + fun `khttp request through kotlin coroutines CoroutineExecutorService`() { + val pool = ForkJoinPool(parallelizm) + val context = CoroutineExecutorService(pool) + + measureTimeMillis { + val responses = List(requestsAmount) { + GlobalScope.async(context = context) { + khttp.get(targetUrl) + } + } + + runBlocking(context) { + responses.map { getCode(it) } + .filter { it == 200 } + .size + }.also { + println(it) + } + }.also { print(it) } + } + + private suspend fun getCode(response: Deferred) = + try { + response.await().statusCode + } catch (_: Throwable) { + -1 + } +} \ No newline at end of file diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt similarity index 69% rename from src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt rename to src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt index 3f019f9f..f786ee4f 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/AsyncHttpGetPerformanceTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt @@ -8,17 +8,19 @@ import io.github.rybalkinsd.kohttp.dsl.async.httpGetAsync import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext import io.github.rybalkinsd.kohttp.dsl.httpGet import io.github.rybalkinsd.kohttp.ext.url -import kotlinx.coroutines.* +import io.github.rybalkinsd.kohttp.getSuccessfulResponsesAmount +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async import okhttp3.* import org.junit.Test import java.io.IOException -import java.util.concurrent.Executor +import java.util.concurrent.ForkJoinPool import java.util.concurrent.TimeUnit import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine -import kotlin.reflect.full.declaredMembers -import kotlin.reflect.jvm.isAccessible import kotlin.system.measureTimeMillis @@ -34,24 +36,24 @@ import kotlin.system.measureTimeMillis * @since 0.10.0 * @author evgeny */ -class AsyncHttpGetPerformanceTest { +class KohttpAsyncHttpGetPerformanceTest { + private val requestsAmount = 10000 + private val targetUrl = "https://postman-echo.com/get" + /** * Running 100 simple requests tu measure current realisation */ @Test - fun `100 simple async requests with Call-Factory-suspendCall extension`() { + fun `1000 simple async requests with Call-Factory-suspendCall extension`() { measureTimeMillis { - val rs = List(100) { + val responses = List(requestsAmount) { httpGetAsync { - url("https://postman-echo.com/delay/2") + url(targetUrl) } } - runBlocking { - val code = rs.awaitAll().map { it.code() } - println(code) - } + println(getSuccessfulResponsesAmount(responses)) }.also { print(it) } } @@ -62,16 +64,13 @@ class AsyncHttpGetPerformanceTest { @Test fun `100 simple async requests with old Call-Factory-suspendCall extension`() { measureTimeMillis { - val rs = List(100) { + val responses = List(requestsAmount) { oldAsyncHttpGet(client = pseudoDefault) { - url("https://postman-echo.com/delay/2") + url(targetUrl) } } - runBlocking { - val code = rs.awaitAll().map { it.code() } - println(code) - } + println(getSuccessfulResponsesAmount(responses)) }.also { print(it) } } @@ -83,18 +82,15 @@ class AsyncHttpGetPerformanceTest { @Test fun `100 simple async requests with kotlin coroutines Dispatcher-Default`() { measureTimeMillis { - val rs = List(100) { + val responses = List(requestsAmount) { GlobalScope.async(context = Dispatchers.Default) { httpGet { - url("https://postman-echo.com/delay/2") + url(targetUrl) } } } - runBlocking { - val code = rs.awaitAll().map { it.code() } - println(code) - } + println(getSuccessfulResponsesAmount(responses)) }.also { print(it) } } @@ -106,34 +102,18 @@ class AsyncHttpGetPerformanceTest { @Test fun `100 simple async requests with kotlin coroutines Dispatcher-IO`() { measureTimeMillis { - val rs = List(100) { + val responses = List(requestsAmount) { GlobalScope.async(context = Dispatchers.IO) { httpGet { - url("https://postman-echo.com/delay/2") + url(targetUrl) } } } - runBlocking { - val code = rs.awaitAll().map { it.code() } - println(code) - } + println(getSuccessfulResponsesAmount(responses)) }.also { print(it) } } - @Test - fun `Check dispatcher`() { - val cd = Dispatchers.IO is Executor - println(cd) - println(Dispatchers.IO::class.java.name) - println(Runtime.getRuntime().availableProcessors()) - println(Dispatchers.IO::class.declaredMembers - .firstOrNull { it.name == "parallelism" } - ?.also { it.isAccessible = true } - ?.call(Dispatchers.IO) - ) - } - companion object { /** * Old async httpGet method, before 0.10.0 @@ -168,9 +148,9 @@ class AsyncHttpGetPerformanceTest { writeTimeout = it.writeTimeout followRedirects = it.followRedirects followSslRedirects = it.followSslRedirects - dispatcher = Dispatcher().also { - it.maxRequestsPerHost = 1000 - it.maxRequests = 1000 + dispatcher = Dispatcher(ForkJoinPool(256)).also { + it.maxRequestsPerHost = 256 + it.maxRequests = 256 } } } From 0497f88312fa9f863c949815b58fe914234d59d0 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Sun, 2 Jun 2019 00:59:09 +0300 Subject: [PATCH 14/38] Some beauty changes due to pull request #99 review --- .../kohttp/client/DefaultHttpClient.kt | 10 +-- .../rybalkinsd/kohttp/configuration/Config.kt | 6 ++ .../github/rybalkinsd/kohttp/dsl/UploadDsl.kt | 2 +- .../kohttp/dsl/async/HttpDeleteAsyncDsl.kt | 10 +-- .../kohttp/dsl/async/HttpGetAsyncDsl.kt | 9 +-- .../kohttp/dsl/async/HttpHeadAsyncDsl.kt | 9 +-- .../kohttp/dsl/async/HttpPatchAsyncDsl.kt | 10 +-- .../kohttp/dsl/async/HttpPostAsyncDsl.kt | 10 +-- .../kohttp/dsl/async/HttpPutAsyncDsl.kt | 10 +-- .../github/rybalkinsd/kohttp/ext/CallExt.kt | 20 +++--- .../kohttp/pool/CoroutineExecutorService.kt | 4 +- .../io/github/rybalkinsd/kohttp/util/json.kt | 6 +- .../io/github/rybalkinsd/kohttp/Utils.kt | 20 +++--- .../kohttp/client/ClientBuilderTest.kt | 69 +++++++++++-------- .../KohttpAsyncHttpGetPerformanceTest.kt | 33 +++++++-- 15 files changed, 117 insertions(+), 111 deletions(-) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/client/DefaultHttpClient.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/client/DefaultHttpClient.kt index c33727a9..d0c68cee 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/client/DefaultHttpClient.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/client/DefaultHttpClient.kt @@ -44,10 +44,10 @@ val defaultHttpClient: OkHttpClient = config.client.let { } private fun DispatcherConfig.create() = - Dispatcher().apply { - this@apply.maxRequests = this@create.maxRequests - this@apply.maxRequestsPerHost = this@create.maxRequestsPerHost - } + Dispatcher().apply { + this.maxRequests = this@create.maxRequests + this.maxRequestsPerHost = this@create.maxRequestsPerHost + } private fun ConnectionPoolConfig.create() = - ConnectionPool(maxIdleConnections, keepAliveDuration, TimeUnit.MILLISECONDS) + ConnectionPool(maxIdleConnections, keepAliveDuration, TimeUnit.MILLISECONDS) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt index 231e1aa4..b0cdbf60 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt @@ -27,6 +27,12 @@ internal data class ClientConfig( val dispatcher: DispatcherConfig = DispatcherConfig() ) +/** + * okhttp {@link Dispatcher} configuration + * + * @since 0.10.0 + * @author evgeny + */ internal data class DispatcherConfig( /** * Set the maximum number of requests to execute concurrently. diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt index d7c9799d..28d704d0 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt @@ -6,7 +6,7 @@ import okhttp3.Call import okhttp3.Response /** - * Method provides an synchronous DSL call of HTTP POST to UPLOAD file + * Method provides a synchronous DSL call of HTTP POST to UPLOAD file * * @return a `Response` instance. * diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt index 47c5996f..d57dd608 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt @@ -20,15 +20,9 @@ import okhttp3.Response * *
  *  val response: Deferred = httpDeleteAsync {
- *      host = "yourhost"
- *      scheme = "https"
- *      port = 8080
- *      path = "path/to/resource"
- *      param {
- *          "your param" to "value"
- *      }
+ *      url("https://yourhost:port/path/to/resource")
  *      header { ... }
- *      body { ... }
+ *      ...
  *  }
  *  response.await().use { ... }
  *  
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt index b6cc12cc..3879c5ce 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt @@ -20,14 +20,9 @@ import okhttp3.Response * *
  *  val response: Deferred = httpGetAsync {
- *      host = "yourhost"
- *      scheme = "https"
- *      port = 8080
- *      path = "path/to/resource"
- *      param {
- *          "your param" to "value"
- *      }
+ *      url("https://yourhost:port/path/to/resource")
  *      header { ... }
+ *      ...
  *  }
  *  response.await().use { ... }
  *  
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt index eac326e5..8211d13b 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt @@ -19,14 +19,9 @@ import okhttp3.Response * *
  *  val response: Deferred = httpHeadAsync {
- *      host = "yourhost"
- *      scheme = "https"
- *      port = 8080
- *      path = "path/to/resource"
- *      param {
- *          "your param" to "value"
- *      }
+ *      url("https://yourhost:port/path/to/resource")
  *      header { ... }
+ *      ...
  *  }
  *  response.await().use { ... }
  *  
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt index 19887c4b..8f801e9b 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt @@ -20,15 +20,9 @@ import okhttp3.Response * *
  *  val response: Deferred = httpPatchAsync {
- *      host = "yourhost"
- *      scheme = "https"
- *      port = 8080
- *      path = "path/to/resource"
- *      param {
- *          "your param" to "value"
- *      }
+ *      url("https://yourhost:port/path/to/resource")
  *      header { ... }
- *      body { ... }
+ *      ...
  *  }
  *  response.await().use { ... }
  *  
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt index 957c3fa2..b0ce095f 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt @@ -20,15 +20,9 @@ import okhttp3.Response * *
  *  val response: Deferred = httpPostAsync {
- *      host = "yourhost"
- *      scheme = "https"
- *      port = 8080
- *      path = "path/to/resource"
- *      param {
- *          "your param" to "value"
- *      }
+ *      url("https://yourhost:port/path/to/resource")
  *      header { ... }
- *      body { ... }
+ *      ...
  *  }
  *  response.await().use {
  *      your code here
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt
index 0f0d2b0b..afd22123 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt
@@ -20,15 +20,9 @@ import okhttp3.Response
  *
  *  
  *  val response: Deferred = httpPutAsync {
- *      host = "yourhost"
- *      scheme = "https"
- *      port = 8080
- *      path = "path/to/resource"
- *      param {
- *          "your param" to "value"
- *      }
+ *      url("https://yourhost:port/path/to/resource")
  *      header { ... }
- *      body { ... }
+ *      ...
  *  }
  *  response.await().use { ... }
  *  
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt index a89ea473..0d45fe6a 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt @@ -12,15 +12,15 @@ import kotlin.coroutines.suspendCoroutine internal fun Call.Factory.call(request: Request): Response = newCall(request).execute() internal suspend fun Call.Factory.suspendCall(request: Request): Response = - suspendCoroutine { cont -> - newCall(request).enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - cont.resume(response) - } + suspendCoroutine { cont -> + newCall(request).enqueue(object : Callback { + override fun onResponse(call: Call, response: Response) { + cont.resume(response) + } - override fun onFailure(call: Call, e: IOException) { - cont.resumeWithException(e) - } - }) - } + override fun onFailure(call: Call, e: IOException) { + cont.resumeWithException(e) + } + }) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt index afe8a08b..67ba6e4d 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt @@ -8,8 +8,8 @@ import java.util.concurrent.ForkJoinPool import kotlin.coroutines.CoroutineContext /** - * @Author: evgeny.vorobyev - * @Since: 0.10.0 + * @since 0.10.0 + * @author evgeny */ @Experimental class CoroutineExecutorService( diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt index 22aaf67b..9acf21f2 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt @@ -41,7 +41,7 @@ class Json { } override fun toString(): String = - elements.joinToString(separator = ",", prefix = "{", postfix = "}") { (k, v) -> - """"$k":$v""" - } + elements.joinToString(separator = ",", prefix = "{", postfix = "}") { (k, v) -> + """"$k":$v""" + } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt index 48220cc8..2bf5481c 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt @@ -18,20 +18,20 @@ fun assertResponses(expected: Map, actual: JsonNode) { } fun getSuccessfulResponsesAmount( - results: List> + results: List> ): Int = runBlocking { results.map { getCode(it) } - .filter { it == 200 } - .size + .filter { it == 200 } + .size } suspend fun getCode( - result: Deferred + result: Deferred ): Int = - try { - result.await().use { - it.code() - } - } catch (_: Throwable) { - -1 + try { + result.await().use { + it.code() } + } catch (_: Throwable) { + -1 + } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilderTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilderTest.kt index c8ef5e73..af4b7650 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilderTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ClientBuilderTest.kt @@ -1,6 +1,15 @@ package io.github.rybalkinsd.kohttp.client -import okhttp3.* +import okhttp3.Authenticator +import okhttp3.CertificatePinner +import okhttp3.ConnectionPool +import okhttp3.ConnectionSpec +import okhttp3.CookieJar +import okhttp3.Dispatcher +import okhttp3.Dns +import okhttp3.EventListener +import okhttp3.OkHttpClient +import okhttp3.Protocol import okhttp3.internal.tls.OkHostnameVerifier import org.junit.Test import sun.security.ssl.SSLSocketFactoryImpl @@ -61,29 +70,29 @@ class ClientBuilderTest { } val client = OkHttpClient.Builder() - .dispatcher(defaultDispatcher) - .proxy(defaultProxy) - .protocols(defaultProtocols) - .connectionSpecs(defaultConnectionSpecs) - .eventListenerFactory(defaultFactory) - .proxySelector(defaultProxySelector) - .cookieJar(defaultCookieJar) - .socketFactory(defaultSocketFactory) - .sslSocketFactory(defaultSslSocketFactory) - .hostnameVerifier(defaultHostnameVerifier) - .certificatePinner(defaultCertificatePinner) - .proxyAuthenticator(defaultAuth) - .authenticator(defaultAuth) - .connectionPool(defaultConnectionPool) - .dns(defaultDns) - .followSslRedirects(true) - .followRedirects(true) - .retryOnConnectionFailure(true) - .connectTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .readTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .writeTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .pingInterval(0, TimeUnit.MILLISECONDS) - .build() + .dispatcher(defaultDispatcher) + .proxy(defaultProxy) + .protocols(defaultProtocols) + .connectionSpecs(defaultConnectionSpecs) + .eventListenerFactory(defaultFactory) + .proxySelector(defaultProxySelector) + .cookieJar(defaultCookieJar) + .socketFactory(defaultSocketFactory) + .sslSocketFactory(defaultSslSocketFactory) + .hostnameVerifier(defaultHostnameVerifier) + .certificatePinner(defaultCertificatePinner) + .proxyAuthenticator(defaultAuth) + .authenticator(defaultAuth) + .connectionPool(defaultConnectionPool) + .dns(defaultDns) + .followSslRedirects(true) + .followRedirects(true) + .retryOnConnectionFailure(true) + .connectTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .readTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .writeTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .pingInterval(0, TimeUnit.MILLISECONDS) + .build() with(client) { assertEquals(dispatcher(), dslClient.dispatcher()) @@ -115,13 +124,13 @@ class ClientBuilderTest { fun `client builder getters throw exceptions`() { val clientBuilder = ClientBuilderImpl() ClientBuilderImpl::class.declaredMemberProperties.filter { !it.isFinal } - .map { - try { - it.call(clientBuilder) - assertTrue(false, "${it.name} call is successful, but not expected to be") - } catch (ignored: InvocationTargetException) { - } + .map { + try { + it.call(clientBuilder) + assertTrue(false, "${it.name} call is successful, but not expected to be") + } catch (ignored: InvocationTargetException) { } + } } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt index f786ee4f..7696072c 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt @@ -9,6 +9,7 @@ import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext import io.github.rybalkinsd.kohttp.dsl.httpGet import io.github.rybalkinsd.kohttp.ext.url import io.github.rybalkinsd.kohttp.getSuccessfulResponsesAmount +import io.github.rybalkinsd.kohttp.pool.CoroutineExecutorService import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -39,6 +40,7 @@ import kotlin.system.measureTimeMillis class KohttpAsyncHttpGetPerformanceTest { private val requestsAmount = 10000 private val targetUrl = "https://postman-echo.com/get" + private val parallelizm = 256 /** @@ -62,7 +64,7 @@ class KohttpAsyncHttpGetPerformanceTest { * Running 100 simple requests tu measure old realisation */ @Test - fun `100 simple async requests with old Call-Factory-suspendCall extension`() { + fun `10000 simple async requests with old Call-Factory-suspendCall extension`() { measureTimeMillis { val responses = List(requestsAmount) { oldAsyncHttpGet(client = pseudoDefault) { @@ -80,7 +82,7 @@ class KohttpAsyncHttpGetPerformanceTest { * async with Default dispatcher */ @Test - fun `100 simple async requests with kotlin coroutines Dispatcher-Default`() { + fun `10000 simple async requests with kotlin coroutines Dispatcher-Default`() { measureTimeMillis { val responses = List(requestsAmount) { GlobalScope.async(context = Dispatchers.Default) { @@ -100,7 +102,7 @@ class KohttpAsyncHttpGetPerformanceTest { * async with IO dispatcher */ @Test - fun `100 simple async requests with kotlin coroutines Dispatcher-IO`() { + fun `10000 simple async requests with kotlin coroutines Dispatcher-IO`() { measureTimeMillis { val responses = List(requestsAmount) { GlobalScope.async(context = Dispatchers.IO) { @@ -114,6 +116,29 @@ class KohttpAsyncHttpGetPerformanceTest { }.also { print(it) } } + + /** + * Running 100 simple requests tu measure realisation with kotlin + * async with IO dispatcher + */ + @Test + fun `10000 simple async requests with custom coroutines Dispatcher`() { + val pool = ForkJoinPool(parallelizm) + val context = CoroutineExecutorService(pool) + + measureTimeMillis { + val responses = List(requestsAmount) { + GlobalScope.async(context = context) { + httpGet { + url(targetUrl) + } + } + } + + println(getSuccessfulResponsesAmount(responses)) + }.also { print(it) } + } + companion object { /** * Old async httpGet method, before 0.10.0 @@ -148,7 +173,7 @@ class KohttpAsyncHttpGetPerformanceTest { writeTimeout = it.writeTimeout followRedirects = it.followRedirects followSslRedirects = it.followSslRedirects - dispatcher = Dispatcher(ForkJoinPool(256)).also { + dispatcher = Dispatcher(ForkJoinPool(parallelizm)).also { it.maxRequestsPerHost = 256 it.maxRequests = 256 } From 6b289c62569e312bed774590f8d89def2c009b87 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Sun, 2 Jun 2019 01:26:15 +0300 Subject: [PATCH 15/38] Removed experimental features in another branch --- .../kohttp/pool/CoroutineExecutorService.kt | 43 ---- .../KhttpAsyncHttpGetPerformanceTest.kt | 99 ---------- .../KohttpAsyncHttpGetPerformanceTest.kt | 184 ------------------ 3 files changed, 326 deletions(-) delete mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt delete mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KhttpAsyncHttpGetPerformanceTest.kt delete mode 100644 src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt deleted file mode 100644 index 67ba6e4d..00000000 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/pool/CoroutineExecutorService.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.github.rybalkinsd.kohttp.pool - -import kotlinx.coroutines.* -import org.jetbrains.spek.meta.Experimental -import java.util.concurrent.Executor -import java.util.concurrent.ExecutorService -import java.util.concurrent.ForkJoinPool -import kotlin.coroutines.CoroutineContext - -/** - * @since 0.10.0 - * @author evgeny - */ -@Experimental -class CoroutineExecutorService( - private val executorService: ExecutorService = ForkJoinPool() -) : ExecutorService by executorService, - ExecutorCoroutineDispatcher() { - override val executor: Executor - get() = this - - override fun close() { - throw UnsupportedOperationException("${CoroutineExecutorService::class.java.name} can not be closed") - } - - override fun dispatch(context: CoroutineContext, block: Runnable) { -// println("Dispatch request with context ${context::class.java.name}") - executorService.execute(block) - } -} - -fun main() { - val context = CoroutineExecutorService() - val task = GlobalScope.async(context = context) { - println(Thread.currentThread().name) - withContext(context = context) { - println(Thread.currentThread().name) - } - } - runBlocking { - task.await() - } -} diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KhttpAsyncHttpGetPerformanceTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KhttpAsyncHttpGetPerformanceTest.kt deleted file mode 100644 index 00487b73..00000000 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KhttpAsyncHttpGetPerformanceTest.kt +++ /dev/null @@ -1,99 +0,0 @@ -package io.github.rybalkinsd.kohttp.performance - -import io.github.rybalkinsd.kohttp.dsl.async.httpGetAsync -import io.github.rybalkinsd.kohttp.ext.url -import io.github.rybalkinsd.kohttp.getCode -import io.github.rybalkinsd.kohttp.getSuccessfulResponsesAmount -import io.github.rybalkinsd.kohttp.pool.CoroutineExecutorService -import khttp.responses.Response -import kotlinx.coroutines.* -import org.junit.Test -import java.util.concurrent.ForkJoinPool -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicInteger -import kotlin.system.measureTimeMillis - -public class KhttpAsyncHttpGetPerformanceTest { - private val parallelizm = 256 - private val requestsAmount = 10000 - private val targetUrl = "https://postman-echo.com/get" - - val completed = AtomicInteger(0) - val failed = AtomicInteger(0) - - @Test - fun `khttp request through forkJoinPool`() { - val pool = ForkJoinPool(parallelizm) - - measureTimeMillis { - repeat(requestsAmount) { - pool.execute { - try { - val response = khttp.get(targetUrl) - - if (response.statusCode == 200) { - completed.incrementAndGet() - } else { - failed.incrementAndGet() - } - } catch (e: Exception) { - failed.incrementAndGet() - } - } - } - pool.shutdown() - pool.awaitTermination(1000, TimeUnit.SECONDS) - }.also { - println(completed) - println(it) - } - } - - @Test - fun `khttp request through kotlin coroutines Dispatchers_IO`() { - measureTimeMillis { - val responses = List(requestsAmount) { - GlobalScope.async(context = Dispatchers.IO) { - khttp.get(targetUrl) - } - } - - runBlocking { - responses.map { getCode(it) } - .filter { it == 200 } - .size - }.also { - println(it) - } - }.also { print(it) } - } - - @Test - fun `khttp request through kotlin coroutines CoroutineExecutorService`() { - val pool = ForkJoinPool(parallelizm) - val context = CoroutineExecutorService(pool) - - measureTimeMillis { - val responses = List(requestsAmount) { - GlobalScope.async(context = context) { - khttp.get(targetUrl) - } - } - - runBlocking(context) { - responses.map { getCode(it) } - .filter { it == 200 } - .size - }.also { - println(it) - } - }.also { print(it) } - } - - private suspend fun getCode(response: Deferred) = - try { - response.await().statusCode - } catch (_: Throwable) { - -1 - } -} \ No newline at end of file diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt deleted file mode 100644 index 7696072c..00000000 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/performance/KohttpAsyncHttpGetPerformanceTest.kt +++ /dev/null @@ -1,184 +0,0 @@ -package io.github.rybalkinsd.kohttp.performance - -import io.github.rybalkinsd.kohttp.client.client -import io.github.rybalkinsd.kohttp.client.defaultHttpClient -import io.github.rybalkinsd.kohttp.configuration.ConnectionPoolConfig -import io.github.rybalkinsd.kohttp.configuration.config -import io.github.rybalkinsd.kohttp.dsl.async.httpGetAsync -import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext -import io.github.rybalkinsd.kohttp.dsl.httpGet -import io.github.rybalkinsd.kohttp.ext.url -import io.github.rybalkinsd.kohttp.getSuccessfulResponsesAmount -import io.github.rybalkinsd.kohttp.pool.CoroutineExecutorService -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async -import okhttp3.* -import org.junit.Test -import java.io.IOException -import java.util.concurrent.ForkJoinPool -import java.util.concurrent.TimeUnit -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine -import kotlin.system.measureTimeMillis - - -/** - * Compare performance of different execution variants - * - * Test proposal - run 100 simple get requests - * to measure time that will be spent on execution. - * Compare result with another alternatives - * that also presented in this class - * - * Requests' statuses and time in millis will be written in console - * @since 0.10.0 - * @author evgeny - */ -class KohttpAsyncHttpGetPerformanceTest { - private val requestsAmount = 10000 - private val targetUrl = "https://postman-echo.com/get" - private val parallelizm = 256 - - - /** - * Running 100 simple requests tu measure current realisation - */ - @Test - fun `1000 simple async requests with Call-Factory-suspendCall extension`() { - measureTimeMillis { - val responses = List(requestsAmount) { - httpGetAsync { - url(targetUrl) - } - } - - println(getSuccessfulResponsesAmount(responses)) - }.also { print(it) } - } - - - /** - * Running 100 simple requests tu measure old realisation - */ - @Test - fun `10000 simple async requests with old Call-Factory-suspendCall extension`() { - measureTimeMillis { - val responses = List(requestsAmount) { - oldAsyncHttpGet(client = pseudoDefault) { - url(targetUrl) - } - } - - println(getSuccessfulResponsesAmount(responses)) - }.also { print(it) } - } - - - /** - * Running 100 simple requests tu measure realisation with kotlin - * async with Default dispatcher - */ - @Test - fun `10000 simple async requests with kotlin coroutines Dispatcher-Default`() { - measureTimeMillis { - val responses = List(requestsAmount) { - GlobalScope.async(context = Dispatchers.Default) { - httpGet { - url(targetUrl) - } - } - } - - println(getSuccessfulResponsesAmount(responses)) - }.also { print(it) } - } - - - /** - * Running 100 simple requests tu measure realisation with kotlin - * async with IO dispatcher - */ - @Test - fun `10000 simple async requests with kotlin coroutines Dispatcher-IO`() { - measureTimeMillis { - val responses = List(requestsAmount) { - GlobalScope.async(context = Dispatchers.IO) { - httpGet { - url(targetUrl) - } - } - } - - println(getSuccessfulResponsesAmount(responses)) - }.also { print(it) } - } - - - /** - * Running 100 simple requests tu measure realisation with kotlin - * async with IO dispatcher - */ - @Test - fun `10000 simple async requests with custom coroutines Dispatcher`() { - val pool = ForkJoinPool(parallelizm) - val context = CoroutineExecutorService(pool) - - measureTimeMillis { - val responses = List(requestsAmount) { - GlobalScope.async(context = context) { - httpGet { - url(targetUrl) - } - } - } - - println(getSuccessfulResponsesAmount(responses)) - }.also { print(it) } - } - - companion object { - /** - * Old async httpGet method, before 0.10.0 - */ - private fun oldAsyncHttpGet( - client: Call.Factory = defaultHttpClient, - init: HttpGetContext.() -> Unit - ): Deferred = GlobalScope.async(context = Dispatchers.IO) { - client.oldSuspendCall(HttpGetContext().apply(init).makeRequest()) - } - - private suspend fun Call.Factory.oldSuspendCall( - request: Request - ): Response = suspendCoroutine { cont -> - newCall(request).enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - cont.resume(response) - } - - override fun onFailure(call: Call, e: IOException) { - cont.resumeWithException(e) - } - }) - } - } - - val pseudoDefault: OkHttpClient = config.client.let { - client { - connectionPool = it.connectionPoolConfig.create() - connectTimeout = it.connectTimeout - readTimeout = it.readTimeout - writeTimeout = it.writeTimeout - followRedirects = it.followRedirects - followSslRedirects = it.followSslRedirects - dispatcher = Dispatcher(ForkJoinPool(parallelizm)).also { - it.maxRequestsPerHost = 256 - it.maxRequests = 256 - } - } - } - - private fun ConnectionPoolConfig.create() = ConnectionPool(100, keepAliveDuration, TimeUnit.MILLISECONDS) -} \ No newline at end of file From 5b0cfe44878ba1e563ec590736f0ffc3a0ff1943 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Sun, 2 Jun 2019 01:44:05 +0300 Subject: [PATCH 16/38] Changed tabulation to proper one due to kotlin code style --- .../kohttp/client/OkHttpClientExt.kt | 2 +- .../rybalkinsd/kohttp/configuration/Config.kt | 46 ++++++++-------- .../kohttp/dsl/async/HttpDeleteAsyncDsl.kt | 10 ++-- .../kohttp/dsl/async/HttpGetAsyncDsl.kt | 10 ++-- .../kohttp/dsl/async/HttpHeadAsyncDsl.kt | 10 ++-- .../kohttp/dsl/async/HttpPatchAsyncDsl.kt | 10 ++-- .../kohttp/dsl/async/HttpPostAsyncDsl.kt | 10 ++-- .../kohttp/dsl/async/HttpPutAsyncDsl.kt | 10 ++-- .../kohttp/dsl/async/UploadAsyncDsl.kt | 12 ++--- .../dsl/context/MultipartBodyContext.kt | 4 +- .../rybalkinsd/kohttp/ext/ResponseExt.kt | 52 +++++++++---------- .../github/rybalkinsd/kohttp/ext/StringExt.kt | 12 ++--- .../kohttp/interceptors/SigningInterceptor.kt | 20 +++---- .../kohttp/client/ForkClientBuilderTest.kt | 20 +++---- .../kohttp/dsl/HttpDeleteDslKtTest.kt | 20 +++---- .../rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt | 26 +++++----- .../kohttp/dsl/HttpPatchDslKtTest.kt | 20 +++---- .../kohttp/dsl/HttpPostDslKtTest.kt | 38 +++++++------- .../rybalkinsd/kohttp/dsl/HttpPutDslKtTest.kt | 20 +++---- .../rybalkinsd/kohttp/ext/HeadersExtKtTest.kt | 12 ++--- .../github/rybalkinsd/kohttp/util/FormTest.kt | 2 +- 21 files changed, 183 insertions(+), 183 deletions(-) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/client/OkHttpClientExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/client/OkHttpClientExt.kt index e279bed3..19996d63 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/client/OkHttpClientExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/client/OkHttpClientExt.kt @@ -8,4 +8,4 @@ import okhttp3.OkHttpClient * @author sergey */ fun OkHttpClient.fork(block: ForkClientBuilder.() -> Unit): OkHttpClient = - ClientBuilderImpl(this).apply(block).build() + ClientBuilderImpl(this).apply(block).build() diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt index b0cdbf60..43a77e09 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/configuration/Config.kt @@ -17,14 +17,14 @@ internal val config = Config::class.java.getResource("/kohttp.yaml")?.let { internal data class Config(val client: ClientConfig = ClientConfig()) internal data class ClientConfig( - val connectTimeout: Long = TimeUnit.SECONDS.toMillis(10), - val readTimeout: Long = TimeUnit.SECONDS.toMillis(10), - val writeTimeout: Long = TimeUnit.SECONDS.toMillis(10), - @JsonProperty("connectionPool") - val connectionPoolConfig: ConnectionPoolConfig = ConnectionPoolConfig(), - val followRedirects: Boolean = true, - val followSslRedirects: Boolean = true, - val dispatcher: DispatcherConfig = DispatcherConfig() + val connectTimeout: Long = TimeUnit.SECONDS.toMillis(10), + val readTimeout: Long = TimeUnit.SECONDS.toMillis(10), + val writeTimeout: Long = TimeUnit.SECONDS.toMillis(10), + @JsonProperty("connectionPool") + val connectionPoolConfig: ConnectionPoolConfig = ConnectionPoolConfig(), + val followRedirects: Boolean = true, + val followSslRedirects: Boolean = true, + val dispatcher: DispatcherConfig = DispatcherConfig() ) /** @@ -34,24 +34,24 @@ internal data class ClientConfig( * @author evgeny */ internal data class DispatcherConfig( - /** - * Set the maximum number of requests to execute concurrently. - * - * @see okhttp3.Dispatcher.setMaxRequests - */ - val maxRequests: Int = DEFAULT_REQUEST_AMOUNT, - - /** - * Set the maximum number of requests for each host to execute concurrently. - * - * @see okhttp3.Dispatcher.setMaxRequestsPerHost - */ - val maxRequestsPerHost: Int = DEFAULT_REQUEST_AMOUNT + /** + * Set the maximum number of requests to execute concurrently. + * + * @see okhttp3.Dispatcher.setMaxRequests + */ + val maxRequests: Int = DEFAULT_REQUEST_AMOUNT, + + /** + * Set the maximum number of requests for each host to execute concurrently. + * + * @see okhttp3.Dispatcher.setMaxRequestsPerHost + */ + val maxRequestsPerHost: Int = DEFAULT_REQUEST_AMOUNT ) internal data class ConnectionPoolConfig( - val maxIdleConnections: Int = 5, - val keepAliveDuration: Long = TimeUnit.MINUTES.toMillis(5) + val maxIdleConnections: Int = 5, + val keepAliveDuration: Long = TimeUnit.MINUTES.toMillis(5) ) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt index d57dd608..5c3b7da2 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt @@ -43,9 +43,9 @@ import okhttp3.Response * @author evgeny */ fun httpDeleteAsync( - client: Call.Factory = defaultHttpClient, - init: HttpDeleteContext.() -> Unit + client: Call.Factory = defaultHttpClient, + init: HttpDeleteContext.() -> Unit ): Deferred = - GlobalScope.async(context = Dispatchers.Unconfined) { - client.suspendCall(HttpDeleteContext().apply(init).makeRequest()) - } + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpDeleteContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt index 3879c5ce..bc1e2302 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt @@ -43,9 +43,9 @@ import okhttp3.Response * @author sergey */ fun httpGetAsync( - client: Call.Factory = defaultHttpClient, - init: HttpGetContext.() -> Unit + client: Call.Factory = defaultHttpClient, + init: HttpGetContext.() -> Unit ): Deferred = - GlobalScope.async(context = Dispatchers.Unconfined) { - client.suspendCall(HttpGetContext().apply(init).makeRequest()) - } + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpGetContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt index 8211d13b..9c3d056e 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt @@ -42,9 +42,9 @@ import okhttp3.Response * @author evgeny */ fun httpHeadAsync( - client: Call.Factory = defaultHttpClient, - init: HttpHeadContext.() -> Unit + client: Call.Factory = defaultHttpClient, + init: HttpHeadContext.() -> Unit ): Deferred = - GlobalScope.async(context = Dispatchers.Unconfined) { - client.suspendCall(HttpHeadContext().apply(init).makeRequest()) - } + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpHeadContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt index 8f801e9b..2a3b2574 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt @@ -43,9 +43,9 @@ import okhttp3.Response * @author evgeny */ fun httpPatchAsync( - client: Call.Factory = defaultHttpClient, - init: HttpPatchContext.() -> Unit + client: Call.Factory = defaultHttpClient, + init: HttpPatchContext.() -> Unit ): Deferred = - GlobalScope.async(context = Dispatchers.Unconfined) { - client.suspendCall(HttpPatchContext().apply(init).makeRequest()) - } + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpPatchContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt index b0ce095f..4b0d8e29 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt @@ -45,9 +45,9 @@ import okhttp3.Response * @author evgeny */ fun httpPostAsync( - client: Call.Factory = defaultHttpClient, - init: HttpPostContext.() -> Unit + client: Call.Factory = defaultHttpClient, + init: HttpPostContext.() -> Unit ): Deferred = - GlobalScope.async(context = Dispatchers.Unconfined) { - client.suspendCall(HttpPostContext().apply(init).makeRequest()) - } \ No newline at end of file + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpPostContext().apply(init).makeRequest()) + } \ No newline at end of file diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt index afd22123..f98b3ca6 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt @@ -43,9 +43,9 @@ import okhttp3.Response * @author evgeny */ fun httpPutAsync( - client: Call.Factory = defaultHttpClient, - init: HttpPutContext.() -> Unit + client: Call.Factory = defaultHttpClient, + init: HttpPutContext.() -> Unit ): Deferred = - GlobalScope.async(context = Dispatchers.Unconfined) { - client.suspendCall(HttpPutContext().apply(init).makeRequest()) - } + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(HttpPutContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt index 3fb2bf98..be14f0f2 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt @@ -36,15 +36,15 @@ import okhttp3.Response *
* * @see Response - * @see HttpDeleteContext + * @see UploadContext * * @since 0.10.0 * @author evgeny */ fun uploadAsync( - client: Call.Factory = defaultHttpClient, - init: UploadContext.() -> Unit + client: Call.Factory = defaultHttpClient, + init: UploadContext.() -> Unit ): Deferred = - GlobalScope.async(context = Dispatchers.Unconfined) { - client.suspendCall(UploadContext().apply(init).makeRequest()) - } + GlobalScope.async(context = Dispatchers.Unconfined) { + client.suspendCall(UploadContext().apply(init).makeRequest()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/MultipartBodyContext.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/MultipartBodyContext.kt index 4682900d..af4ec696 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/MultipartBodyContext.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/MultipartBodyContext.kt @@ -22,10 +22,10 @@ class MultipartBodyContext(type: String?) { } fun form(name: String, file: File): FormDataPart = - FormDataPart(name, file.name, RequestBody.create(null, file)) + FormDataPart(name, file.name, RequestBody.create(null, file)) fun form(name: String, filename: String, content: ByteArray): FormDataPart = - FormDataPart(name, filename, RequestBody.create(null, content)) + FormDataPart(name, filename, RequestBody.create(null, content)) fun build(): MultipartBody = builder.build() } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExt.kt index 635ac3e4..161bdc5b 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExt.kt @@ -33,20 +33,20 @@ import okhttp3.Response * @author sergey */ fun Response.eager() = EagerResponse( - request = request(), - protocol = protocol(), - code = code(), - message = message(), - handshake = handshake(), - headers = (0 until headers().size()).map { - Header(headers().name(it), headers().value(it)) - }, - body = body()?.string(), - networkResponse = networkResponse(), - cacheResponse = cacheResponse(), - priorResponse = priorResponse(), - sentRequestAtMillis = sentRequestAtMillis(), - receivedResponseAtMillis = receivedResponseAtMillis() + request = request(), + protocol = protocol(), + code = code(), + message = message(), + handshake = handshake(), + headers = (0 until headers().size()).map { + Header(headers().name(it), headers().value(it)) + }, + body = body()?.string(), + networkResponse = networkResponse(), + cacheResponse = cacheResponse(), + priorResponse = priorResponse(), + sentRequestAtMillis = sentRequestAtMillis(), + receivedResponseAtMillis = receivedResponseAtMillis() ) /** @@ -59,18 +59,18 @@ fun Response.eager() = EagerResponse( * @author sergey */ data class EagerResponse( - val request: Request, - val protocol: Protocol, - val code: Int, - val message: String, - val handshake: Handshake?, - val headers: List
, - val body: String?, - val networkResponse: Response?, - val cacheResponse: Response?, - val priorResponse: Response?, - val sentRequestAtMillis: Long, - val receivedResponseAtMillis: Long + val request: Request, + val protocol: Protocol, + val code: Int, + val message: String, + val handshake: Handshake?, + val headers: List
, + val body: String?, + val networkResponse: Response?, + val cacheResponse: Response?, + val priorResponse: Response?, + val sentRequestAtMillis: Long, + val receivedResponseAtMillis: Long ) /** diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt index 33ce423f..12bd0339 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt @@ -33,9 +33,9 @@ import okhttp3.Response * @author sergey */ fun String.httpGet( - client: Call.Factory = defaultHttpClient + client: Call.Factory = defaultHttpClient ): Response = - client.call(Request.Builder().url(this).build()) + client.call(Request.Builder().url(this).build()) /** * Async version of http GET request with the provided `String` url. @@ -64,9 +64,9 @@ fun String.httpGet( * @author sergey */ fun String.httpGetAsync( - client: Call.Factory = defaultHttpClient + client: Call.Factory = defaultHttpClient ): Deferred = - GlobalScope.async(context = Dispatchers.Unconfined) { - client.call(Request.Builder().url(this@httpGetAsync).build()) - } + GlobalScope.async(context = Dispatchers.Unconfined) { + client.call(Request.Builder().url(this@httpGetAsync).build()) + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/interceptors/SigningInterceptor.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/interceptors/SigningInterceptor.kt index 645c3283..236d5a7d 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/interceptors/SigningInterceptor.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/interceptors/SigningInterceptor.kt @@ -21,18 +21,18 @@ import okhttp3.Response class SigningInterceptor(private val parameterName: String, private val signer: HttpUrl.() -> String) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response = - with(chain.request()) { - val signedKey = signer(url()) + with(chain.request()) { + val signedKey = signer(url()) - val requestUrl = with(url().newBuilder()) { - addQueryParameter(parameterName, signedKey) - build() - } + val requestUrl = with(url().newBuilder()) { + addQueryParameter(parameterName, signedKey) + build() + } - with(newBuilder()) { - url(requestUrl) - chain.proceed(build()) - } + with(newBuilder()) { + url(requestUrl) + chain.proceed(build()) } + } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ForkClientBuilderTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ForkClientBuilderTest.kt index 12afa598..180f105d 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ForkClientBuilderTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/client/ForkClientBuilderTest.kt @@ -16,16 +16,16 @@ class ForkClientBuilderTest { val defaultDns = Dns { emptyList() } val client = defaultHttpClient.newBuilder() - .cache(null) - .dns(defaultDns) - .followSslRedirects(false) - .followRedirects(false) - .retryOnConnectionFailure(false) - .connectTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .readTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .writeTimeout(defaultTimeout, TimeUnit.MILLISECONDS) - .pingInterval(defaultTimeout, TimeUnit.MILLISECONDS) - .build() + .cache(null) + .dns(defaultDns) + .followSslRedirects(false) + .followRedirects(false) + .retryOnConnectionFailure(false) + .connectTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .readTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .writeTimeout(defaultTimeout, TimeUnit.MILLISECONDS) + .pingInterval(defaultTimeout, TimeUnit.MILLISECONDS) + .build() val dslClient = defaultHttpClient.fork { interceptors = emptyList() diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt index 781e2c78..7895ada2 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt @@ -13,17 +13,17 @@ class HttpDeleteDslKtTest { @Test fun `delete request with form # postman echo`() { val expectedHeader = mapOf( - "one" to "42", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "arg" to "iphone" + "arg" to "iphone" ) val expectedForm = mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + "login" to "user", + "email" to "john.doe@gmail.com" ) httpDelete { @@ -60,17 +60,17 @@ class HttpDeleteDslKtTest { @Test fun `delete request with json # postman echo`() { val expectedHeader = mapOf( - "one" to "42", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "arg" to "iphone" + "arg" to "iphone" ) val expectedJson = mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + "login" to "user", + "email" to "john.doe@gmail.com" ) val response = httpDelete { diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt index f70e5abf..ef999ee9 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt @@ -55,15 +55,15 @@ class HttpGetDslKtTest { fun `single sync http get invocation with param and header`() { val variable = 123L val expectedHeader = mapOf( - "one" to "42", - "two" to variable.toString(), - "three" to """{"a":$variable,"b":{"b1":"512"},"c":[1,2.0,3]}""", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "two" to variable.toString(), + "three" to """{"a":$variable,"b":{"b1":"512"},"c":[1,2.0,3]}""", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "text" to "iphone", - "lr" to "213" + "text" to "iphone", + "lr" to "213" ) val response = httpGet { host = "postman-echo.com" @@ -103,15 +103,15 @@ class HttpGetDslKtTest { fun `single sync http get invocation with url`() { val variable = 123L val expectedHeader = mapOf( - "one" to "42", - "two" to variable.toString(), - "three" to """{"a":$variable,"b":{"b1":"512"},"c":[1,2.0,3]}""", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "two" to variable.toString(), + "three" to """{"a":$variable,"b":{"b1":"512"},"c":[1,2.0,3]}""", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "text" to "iphone", - "lr" to "213" + "text" to "iphone", + "lr" to "213" ) val response = httpGet { url("http://postman-echo.com/get") @@ -157,7 +157,7 @@ class HttpGetDslKtTest { } val expectedParams = mapOf( - "c" to "exists" + "c" to "exists" ) response.use { diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDslKtTest.kt index 3b146eda..f3772346 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDslKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDslKtTest.kt @@ -13,17 +13,17 @@ class HttpPatchDslKtTest { @Test fun `patch request with form # postman echo`() { val expectedHeader = mapOf( - "one" to "42", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "arg" to "iphone" + "arg" to "iphone" ) val expectedForm = mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + "login" to "user", + "email" to "john.doe@gmail.com" ) httpPatch { @@ -60,17 +60,17 @@ class HttpPatchDslKtTest { @Test fun `patch request with json # postman echo`() { val expectedHeader = mapOf( - "one" to "42", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "arg" to "iphone" + "arg" to "iphone" ) val expectedJson = mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + "login" to "user", + "email" to "john.doe@gmail.com" ) val response = httpPatch { diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt index 544e1f31..e071307f 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt @@ -15,17 +15,17 @@ class HttpPostDslKtTest { @Test fun `post request with form # postman echo`() { val expectedHeader = mapOf( - "one" to "42", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "arg" to "iphone" + "arg" to "iphone" ) val expectedForm = mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + "login" to "user", + "email" to "john.doe@gmail.com" ) httpPost { @@ -62,8 +62,8 @@ class HttpPostDslKtTest { @Test fun `post request with form encoded # postman echo`() { val expectedForm = mapOf( - "encoded" to " ", - "notEncoded" to "%20" + "encoded" to " ", + "notEncoded" to "%20" ) httpPost { @@ -101,17 +101,17 @@ class HttpPostDslKtTest { @Test fun `post request with json # postman echo`() { val expectedHeader = mapOf( - "one" to "42", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "arg" to "iphone" + "arg" to "iphone" ) val expectedJson = mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + "login" to "user", + "email" to "john.doe@gmail.com" ) val response = httpPost { host = "postman-echo.com" @@ -158,8 +158,8 @@ class HttpPostDslKtTest { } val expectedJson = mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + "login" to "user", + "email" to "john.doe@gmail.com" ) response.use { @@ -181,8 +181,8 @@ class HttpPostDslKtTest { } val expectedJson = mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + "login" to "user", + "email" to "john.doe@gmail.com" ) response.use { @@ -233,12 +233,12 @@ class HttpPostDslKtTest { @Test fun `post request with empty body# postman echo`() { val expectedHeader = mapOf( - "one" to "42", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "arg" to "iphone" + "arg" to "iphone" ) httpPost { diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDslKtTest.kt index a027fa8f..78ac6f62 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDslKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDslKtTest.kt @@ -13,17 +13,17 @@ class HttpPutDslKtTest { @Test fun `put request with form # postman echo`() { val expectedHeader = mapOf( - "one" to "42", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "arg" to "iphone" + "arg" to "iphone" ) val expectedForm = mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + "login" to "user", + "email" to "john.doe@gmail.com" ) httpPut { @@ -60,17 +60,17 @@ class HttpPutDslKtTest { @Test fun `put request with json # postman echo`() { val expectedHeader = mapOf( - "one" to "42", - "cookie" to "aaa=bbb; ccc=42" + "one" to "42", + "cookie" to "aaa=bbb; ccc=42" ) val expectedParams = mapOf( - "arg" to "iphone" + "arg" to "iphone" ) val expectedJson = mapOf( - "login" to "user", - "email" to "john.doe@gmail.com" + "login" to "user", + "email" to "john.doe@gmail.com" ) val response = httpPut { diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt index 532a349d..002f5fcd 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt @@ -16,16 +16,16 @@ class HeadersExtKtTest { @Test fun `filter and sum`() { val sum = headers.asSequence().filter { it.name.contains('o') } - .sumBy { it.value.toInt() } + .sumBy { it.value.toInt() } assertEquals(3, sum) } companion object { val headers = Headers.Builder() - .add("zero", "0") - .add("one", "1") - .add("two", "2") - .add("three", "3") - .build() + .add("zero", "0") + .add("one", "1") + .add("two", "2") + .add("three", "3") + .build() } } diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/util/FormTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/util/FormTest.kt index 4da65f68..57d7e011 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/util/FormTest.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/util/FormTest.kt @@ -32,6 +32,6 @@ class FormTest { } private operator fun FormBody.contains(k: String) = - (0 until size()).asSequence().any { encodedName(it) == k } + (0 until size()).asSequence().any { encodedName(it) == k } } From 9a46e0ea6d4da0f2e9b6746946fe565bbaf2ce47 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Sun, 2 Jun 2019 01:50:18 +0300 Subject: [PATCH 17/38] Removed unnecessary dependency from build.gradle.kts --- build.gradle.kts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a9c7224c..e7c3bc9c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,18 +15,12 @@ version = "0.10.0-SNAPSHOT" repositories { mavenCentral() - maven { - url = uri("http://repo.spring.io/libs-release/") - } } dependencies { implementation(kotlin("stdlib-jdk8")) implementation(kotlin("reflect")) implementation("org.jetbrains.kotlinx", "kotlinx-coroutines-core", "1.2.1") - // https://mvnrepository.com/artifact/khttp/khttp - implementation("khttp:khttp:1.0.0") - val jacksonVersion = "2.9.9" implementation(jackson("core"), "jackson-databind", jacksonVersion) From c0563b46e38e93cacc3ba2d4cfce2bd29002dcf0 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Fri, 7 Jun 2019 12:28:11 +0300 Subject: [PATCH 18/38] Made suspendCall to be cancellable coroutine in case that jobs in dispatcher can be cancelled --- .../kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt index 0d45fe6a..818167e0 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/CallExt.kt @@ -1,5 +1,6 @@ package io.github.rybalkinsd.kohttp.ext +import kotlinx.coroutines.suspendCancellableCoroutine import okhttp3.Call import okhttp3.Callback import okhttp3.Request @@ -7,13 +8,16 @@ import okhttp3.Response import java.io.IOException import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException -import kotlin.coroutines.suspendCoroutine internal fun Call.Factory.call(request: Request): Response = newCall(request).execute() internal suspend fun Call.Factory.suspendCall(request: Request): Response = - suspendCoroutine { cont -> - newCall(request).enqueue(object : Callback { + suspendCancellableCoroutine { cont -> + val call = newCall(request) + cont.invokeOnCancellation { + call.cancel() + } + call.enqueue(object : Callback { override fun onResponse(call: Call, response: Response) { cont.resume(response) } From 7f65cd4387b201f0c82e08164b3cb5d3c55ca815 Mon Sep 17 00:00:00 2001 From: rybalkinsd Date: Thu, 20 Jun 2019 14:59:16 +0300 Subject: [PATCH 19/38] + deprecations for previously existing async calls --- README.md | 8 +++---- .../kohttp/dsl/async/AsyncHttpGetDsl.kt | 24 +++++++++++++++++++ .../kohttp/dsl/async/HttpPostAsyncDsl.kt | 2 +- .../github/rybalkinsd/kohttp/ext/StringExt.kt | 19 ++++++++++----- .../io/github/rybalkinsd/kohttp/Utils.kt | 24 ------------------- 5 files changed, 42 insertions(+), 35 deletions(-) create mode 100644 src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt diff --git a/README.md b/README.md index 386e408b..b59e8e6a 100644 --- a/README.md +++ b/README.md @@ -248,16 +248,16 @@ val response = fileUri.upload( string or url ) #### async GET -##### `String.asyncHttpGet()` extension function +##### `String.httpGetAsync()` extension function This function starts a new coroutine with *Unconfined* dispatcher. ```kotlin -val response: Deferred = "https://google.com/search?q=iphone".asyncHttpGet() +val response: Deferred = "https://google.com/search?q=iphone".httpGetAsync() ``` -##### `asyncHttpGet` call +##### `httpGetAsync` call ```kotlin -val response: Deferred = asyncHttpGet { +val response: Deferred = httpGetAsync { host = "google.com" path = "/search" header { ... } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt new file mode 100644 index 00000000..3c283b76 --- /dev/null +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/AsyncHttpGetDsl.kt @@ -0,0 +1,24 @@ +package io.github.rybalkinsd.kohttp.dsl.async + +import io.github.rybalkinsd.kohttp.client.defaultHttpClient +import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext +import kotlinx.coroutines.Deferred +import okhttp3.Call +import okhttp3.Response + + +/** + * + * @since 0.4.0 + * @author sergey + */ +@Suppress("DeferredIsResult") +@Deprecated( + message = "Use httpGetAsync instead. This function was renamed according to Kotlin Style Guide." + + "This function will be removed in version 0.12.0", + replaceWith = ReplaceWith( + "httpGetAsync(client, init)", + "io.github.rybalkinsd.kohttp.dsl.async.httpGetAsync") +) +fun asyncHttpGet(client: Call.Factory = defaultHttpClient, init: HttpGetContext.() -> Unit): Deferred = + httpGetAsync(client, init) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt index 4b0d8e29..d9458694 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt @@ -50,4 +50,4 @@ fun httpPostAsync( ): Deferred = GlobalScope.async(context = Dispatchers.Unconfined) { client.suspendCall(HttpPostContext().apply(init).makeRequest()) - } \ No newline at end of file + } diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt index 12bd0339..28f2f104 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt @@ -32,9 +32,7 @@ import okhttp3.Response * @since 0.1.0 * @author sergey */ -fun String.httpGet( - client: Call.Factory = defaultHttpClient -): Response = +fun String.httpGet(client: Call.Factory = defaultHttpClient): Response = client.call(Request.Builder().url(this).build()) /** @@ -63,10 +61,19 @@ fun String.httpGet( * @since 0.1.0 * @author sergey */ -fun String.httpGetAsync( - client: Call.Factory = defaultHttpClient -): Deferred = +fun String.httpGetAsync(client: Call.Factory = defaultHttpClient): Deferred = GlobalScope.async(context = Dispatchers.Unconfined) { client.call(Request.Builder().url(this@httpGetAsync).build()) } +@Suppress("DeferredIsResult") +@Deprecated( + message = "Use httpGetAsync instead. This function was renamed according to Kotlin Style Guide." + + "This function will be removed in version 0.12.0", + replaceWith = ReplaceWith( + "httpGetAsync(client)", + "io.github.rybalkinsd.kohttp.dsl.async.httpGetAsync") +) +fun String.asyncHttpGet(client: Call.Factory = defaultHttpClient): Deferred = + httpGetAsync(client) + diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt index 2bf5481c..245edd89 100644 --- a/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt +++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt @@ -1,11 +1,6 @@ package io.github.rybalkinsd.kohttp import com.fasterxml.jackson.databind.JsonNode -import io.github.rybalkinsd.kohttp.dsl.async.httpGetAsync -import io.github.rybalkinsd.kohttp.ext.url -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.runBlocking -import okhttp3.Response import kotlin.test.assertEquals /** @@ -16,22 +11,3 @@ fun assertResponses(expected: Map, actual: JsonNode) { assertEquals(u, actual[t]?.asText()) } } - -fun getSuccessfulResponsesAmount( - results: List> -): Int = runBlocking { - results.map { getCode(it) } - .filter { it == 200 } - .size -} - -suspend fun getCode( - result: Deferred -): Int = - try { - result.await().use { - it.code() - } - } catch (_: Throwable) { - -1 - } From d59ebaf481d1403895f2088d2bfe3df2b1aece82 Mon Sep 17 00:00:00 2001 From: DeviantBadge Date: Mon, 24 Jun 2019 12:18:35 +0300 Subject: [PATCH 20/38] Change comments to proper one --- .../io/github/rybalkinsd/kohttp/client/dsl/InterceptorDsl.kt | 2 +- .../kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDsl.kt | 2 +- src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDsl.kt | 2 +- src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpHeadDsl.kt | 2 +- src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDsl.kt | 2 +- src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDsl.kt | 2 +- src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDsl.kt | 2 +- src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt | 2 +- .../io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt | 2 +- .../io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt | 2 +- .../io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt | 2 +- .../io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt | 2 +- .../io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt | 2 +- .../io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt | 2 +- .../io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/client/dsl/InterceptorDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/client/dsl/InterceptorDsl.kt index ee2dd33e..2d93efae 100644 --- a/src/main/kotlin/io/github/rybalkinsd/kohttp/client/dsl/InterceptorDsl.kt +++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/client/dsl/InterceptorDsl.kt @@ -5,7 +5,7 @@ import okhttp3.Interceptor /** * Provides a DSL class to define Interceptors for HTTP Client * - * Usage example using the default `InterceptorsDsl`: + * Usage example with default `InterceptorsDsl`: * *
  *  val forkedClient = defaultHttpClient.fork {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDsl.kt
index c5b97e57..e364a2f5 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDsl.kt
@@ -11,7 +11,7 @@ import okhttp3.Response
  *
  * @return a `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Response = httpDelete {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDsl.kt
index 0404f5ef..7252cecd 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDsl.kt
@@ -10,7 +10,7 @@ import okhttp3.Response
  *
  * @return a `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Response = httpGet {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpHeadDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpHeadDsl.kt
index ec0695cb..9117501e 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpHeadDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpHeadDsl.kt
@@ -10,7 +10,7 @@ import okhttp3.Response
  *
  * @return a `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Response = httpHead {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDsl.kt
index bd9d52b9..9801d9d1 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDsl.kt
@@ -11,7 +11,7 @@ import okhttp3.Response
  *
  * @return a `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Response = httpPatch {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDsl.kt
index 2889240e..ac9136f4 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDsl.kt
@@ -11,7 +11,7 @@ import okhttp3.Response
  *
  * @return a `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Response = httpPost {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDsl.kt
index 8664044e..47e9d87f 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDsl.kt
@@ -11,7 +11,7 @@ import okhttp3.Response
  *
  * @return a `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Response = httpPut {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt
index 28d704d0..c82aec65 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDsl.kt
@@ -10,7 +10,7 @@ import okhttp3.Response
  *
  * @return a `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Response = upload {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt
index 5c3b7da2..ddba46cb 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpDeleteAsyncDsl.kt
@@ -16,7 +16,7 @@ import okhttp3.Response
  *
  * @return a deferred `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Deferred = httpDeleteAsync {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt
index bc1e2302..e168e247 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpGetAsyncDsl.kt
@@ -16,7 +16,7 @@ import okhttp3.Response
  *
  * @return a deferred `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Deferred = httpGetAsync {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt
index 9c3d056e..8261c8c2 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpHeadAsyncDsl.kt
@@ -15,7 +15,7 @@ import okhttp3.Response
  *
  * @return a deferred `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Deferred = httpHeadAsync {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt
index 2a3b2574..59227f6d 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPatchAsyncDsl.kt
@@ -16,7 +16,7 @@ import okhttp3.Response
  *
  * @return a deferred `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Deferred = httpPatchAsync {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt
index d9458694..5c0563fc 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPostAsyncDsl.kt
@@ -16,7 +16,7 @@ import okhttp3.Response
  *
  * @return a deferred `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Deferred = httpPostAsync {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt
index f98b3ca6..7b0ee4f2 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/HttpPutAsyncDsl.kt
@@ -16,7 +16,7 @@ import okhttp3.Response
  *
  * @return a deferred `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Deferred = httpPutAsync {
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt
index be14f0f2..0864a762 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDsl.kt
@@ -15,7 +15,7 @@ import okhttp3.Response
  *
  * @return a deferred `Response` instance.
  *
- * Usage example using the default `defaultHttpClient`:
+ * Usage example with default `defaultHttpClient`:
  *
  *  
  *  val response: Deferred = uploadAsync {

From 21d7c2edd1a435b47a5fb79247a03cb22ae22bd9 Mon Sep 17 00:00:00 2001
From: DeviantBadge 
Date: Mon, 24 Jun 2019 13:24:15 +0300
Subject: [PATCH 21/38] Change README.md (add async methods)

---
 README.md | 46 +++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 39 insertions(+), 7 deletions(-)

diff --git a/README.md b/README.md
index b59e8e6a..4e9bd526 100644
--- a/README.md
+++ b/README.md
@@ -249,20 +249,52 @@ val response = fileUri.upload( string or url )
 #### async GET
 
 ##### `String.httpGetAsync()` extension function
-This function starts a new coroutine with *Unconfined* dispatcher. 
+This function starts a new coroutine with *Unconfined* dispatcher.
 
 ```kotlin
 val response: Deferred = "https://google.com/search?q=iphone".httpGetAsync()
 ```
 
 ##### `httpGetAsync` call
+
+You can use same syntax as in [GET](#get)
 ```kotlin
-val response: Deferred = httpGetAsync {
-    host = "google.com"
-    path = "/search"
-    header { ... }
-    param { ... }
-}
+val response: Deferred = httpGetAsync { }
+```
+
+#### async POST
+
+You can use same syntax as in [POST](#post)
+```kotlin
+val response: Deferred = httpPostAsync { }
+```
+
+#### async HEAD
+
+You can use same syntax as in [GET](#get)
+```kotlin
+val response: Deferred = httpHeadAsync { }
+```
+
+#### async PUT
+
+You can use same syntax as in [POST](#post)
+```kotlin
+val response: Deferred = httpPutAsync { }
+```
+
+#### async PATCH
+
+You can use same syntax as in [POST](#post)
+```kotlin
+val response: Deferred = httpPatchAsync { }
+```
+
+#### async DELETE
+
+You can use same syntax as in [POST](#post)
+```kotlin
+val response: Deferred = httpDeleteAsync { }
 ```
 
 ### Response usage

From c7898c8a505d9c143f30ce776b022bff9c0b8f80 Mon Sep 17 00:00:00 2001
From: Sergei Rybalkin 
Date: Mon, 24 Jun 2019 16:06:43 +0300
Subject: [PATCH 22/38] #113 fix: (#114)

+ Boolean support for `json { }` builder
+ Number?, String? and inner Json? support
---
 .../io/github/rybalkinsd/kohttp/util/json.kt  | 15 ++--
 .../rybalkinsd/kohttp/util/JsonKtTest.kt      | 77 ++++++++++++++++++-
 2 files changed, 86 insertions(+), 6 deletions(-)

diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt
index 9acf21f2..eec99b69 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt
@@ -21,25 +21,30 @@ class Json {
     infix fun String.to(obj: List<*>) {
         val v = obj.joinToString(separator = ",", prefix = "[", postfix = "]") {
             when (it) {
-                is Number, is Json -> it.toString()
+                null -> "null"
+                is Number, is Json, is Boolean -> it.toString()
                 else -> """"$it""""
             }
         }
         elements += Pair(this, v)
     }
 
-    infix fun String.to(str: String) {
-        elements += Pair(this, """"$str"""")
+    infix fun String.to(str: String?) {
+        elements += if (str == null) Pair(this, "null") else Pair(this, """"$str"""")
     }
 
-    infix fun String.to(num: Number) {
+    infix fun String.to(num: Number?) {
         elements += Pair(this, num.toString())
     }
 
-    infix fun String.to(json: Json) {
+    infix fun String.to(json: Json?) {
         elements += Pair(this, json.toString())
     }
 
+    infix fun String.to(bool: Boolean?) {
+        elements += Pair(this, bool.toString())
+    }
+
     override fun toString(): String =
         elements.joinToString(separator = ",", prefix = "{", postfix = "}") { (k, v) ->
             """"$k":$v"""
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/util/JsonKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/util/JsonKtTest.kt
index 2851d90b..278f39f4 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/util/JsonKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/util/JsonKtTest.kt
@@ -9,13 +9,22 @@ import kotlin.test.assertEquals
 class JsonKtTest {
 
     @Test
-    fun `string to string one field json`() {
+    fun `string one field json`() {
         val json = json {
             "a" to "1"
         }
         assertEquals("""{"a":"1"}""", json)
     }
 
+    @Test
+    fun `nullable string one field json`() {
+        val nString: String? = null
+        val json = json {
+            "a" to nString
+        }
+        assertEquals("""{"a":null}""", json)
+    }
+
     @Test
     fun `two fields json`() {
         val json = json {
@@ -25,6 +34,15 @@ class JsonKtTest {
         assertEquals("""{"a":"1","b":2}""", json)
     }
 
+    @Test
+    fun `nullable number json`() {
+        val nNumber: Number? = null
+        val json = json {
+            "a" to nNumber
+        }
+        assertEquals("""{"a":null}""", json)
+    }
+
     @Test
     fun `json with list of Number`() {
         val json = json {
@@ -34,6 +52,17 @@ class JsonKtTest {
         assertEquals("""{"a":[1,2.0,3],"b":2}""", json)
     }
 
+    @Test
+    fun `json with list of nullable Number`() {
+        val nNumber: Number? = null
+        val json = json {
+            "a" to listOf(1, 2f, nNumber, 3L)
+            "b" to 2
+        }
+        assertEquals("""{"a":[1,2.0,null,3],"b":2}""", json)
+    }
+
+
     @Test
     fun `json with list of String`() {
         val json = json {
@@ -43,6 +72,15 @@ class JsonKtTest {
         assertEquals("""{"a":["x1","x2","x3"],"b":2}""", json)
     }
 
+    @Test
+    fun `json with list of nullable String`() {
+        val nString: String? = null
+        val json = json {
+            "a" to listOf("x1", nString, "x3")
+        }
+        assertEquals("""{"a":["x1",null,"x3"]}""", json)
+    }
+
     @Test
     fun `json with inner json`() {
         val json = json {
@@ -56,6 +94,16 @@ class JsonKtTest {
         assertEquals("""{"a":{"i":42,"ii":"abc","iii":["x","y","z"]},"b":2}""", json)
     }
 
+    @Test
+    fun `json with inner nullable json`() {
+        val nJson: Json? = null
+        val json = json {
+            "a" to nJson
+        }
+        assertEquals("""{"a":null}""", json)
+    }
+
+
     @Test
     fun `json with inner list of any`() {
         val json = json {
@@ -66,4 +114,31 @@ class JsonKtTest {
         assertEquals("""{"a":{"i":["x",42,2.0,"any"]}}""", json)
     }
 
+    @Test
+    fun `json with inner list of nullable any`() {
+        val json = json {
+            "a" to json {
+                "i" to listOf("x", 42, 2.0, null, "any")
+            }
+        }
+        assertEquals("""{"a":{"i":["x",42,2.0,null,"any"]}}""", json)
+    }
+
+    @Test
+    fun `json with boolean`() {
+        val json = json {
+            "a" to true
+        }
+        assertEquals("""{"a":true}""", json)
+    }
+
+    @Test
+    fun `json with nullable boolean`() {
+        val nBoolean: Boolean? = null
+        val json = json {
+            "a" to nBoolean
+        }
+        assertEquals("""{"a":null}""", json)
+    }
+
 }
\ No newline at end of file

From a8c5a73d1cbae34118d7a2a4f3f8273a42b9bb6d Mon Sep 17 00:00:00 2001
From: DeviantBadge 
Date: Thu, 27 Jun 2019 11:11:02 +0300
Subject: [PATCH 23/38] Changes due to review #112

---
 build.gradle.kts                                             | 2 +-
 src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/build.gradle.kts b/build.gradle.kts
index e7c3bc9c..46b075aa 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,7 +1,7 @@
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
-    kotlin("jvm") version "1.3.31"
+    kotlin("jvm") version "1.3.40"
     jacoco
 
     id("org.jetbrains.dokka") version "0.9.16"
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt
index 28f2f104..fb2ec645 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/StringExt.kt
@@ -63,7 +63,7 @@ fun String.httpGet(client: Call.Factory = defaultHttpClient): Response =
  */
 fun String.httpGetAsync(client: Call.Factory = defaultHttpClient): Deferred =
     GlobalScope.async(context = Dispatchers.Unconfined) {
-        client.call(Request.Builder().url(this@httpGetAsync).build())
+        client.suspendCall(Request.Builder().url(this@httpGetAsync).build())
     }
 
 @Suppress("DeferredIsResult")

From 85e165a26f1f5f0e2b474a7936f9f9163d33ec8c Mon Sep 17 00:00:00 2001
From: DeviantBadge 
Date: Thu, 27 Jun 2019 11:31:53 +0300
Subject: [PATCH 24/38] Rebased on master

---
 .../io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt    | 1 -
 .../rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt     | 4 ++--
 .../io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt   | 6 +++---
 .../io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt  | 8 ++++----
 .../io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt    | 6 +++---
 5 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt
index f671e87e..24e620bc 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt
@@ -39,7 +39,6 @@ class UploadDslKtTest {
 
         assertEquals(200, uploadResponse.code())
         assertEquals(1 * 1024 * 1024 + 173, uploadResponse.asJson()["headers"]["content-length"].asInt())
-        assertEquals(1048749, uploadResponse.asJson()["headers"]["content-length"].asInt())
     }
 
     @Test
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt
index 21bde975..b580c6cd 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/async/UploadAsyncDslTest.kt
@@ -1,6 +1,6 @@
 package io.github.rybalkinsd.kohttp.dsl.async
 
-import io.github.rybalkinsd.kohttp.util.asJson
+import io.github.rybalkinsd.kohttp.ext.asJson
 import kotlinx.coroutines.runBlocking
 import org.junit.Assert
 import org.junit.Test
@@ -20,7 +20,7 @@ class UploadAsyncDslTest {
 
         runBlocking {
             response.await().use {
-                val parsedResponse = it.body()?.string().asJson()
+                val parsedResponse = it.asJson()
 
                 assertEquals(200, it.code())
                 assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt())
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt
index 519c5580..b19774ea 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/FileAsyncExtTest.kt
@@ -1,6 +1,6 @@
 package io.github.rybalkinsd.kohttp.ext
 
-import io.github.rybalkinsd.kohttp.util.asJson
+import io.github.rybalkinsd.kohttp.ext.asJson
 import kotlinx.coroutines.runBlocking
 import org.junit.Test
 import java.io.File
@@ -20,7 +20,7 @@ class FileAsyncExtTest {
 
         runBlocking {
             response.await().use {
-                val parsedResponse = it.body()?.string().asJson()
+                val parsedResponse = it.asJson()
 
                 assertEquals(200, it.code())
                 assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt())
@@ -36,7 +36,7 @@ class FileAsyncExtTest {
 
         runBlocking {
             response.await().use {
-                val parsedResponse = it.body()?.string().asJson()
+                val parsedResponse = it.asJson()
 
                 assertEquals(200, it.code())
                 assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt())
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt
index c85f3466..1a5ec1f0 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt
@@ -58,7 +58,7 @@ class ResponseExtKtTest {
     fun `gets response as string # ext`() {
 
         val response = getUrl.httpGet().asString()
-        val expected = """{"args":{},"headers":{"x-forwarded-proto":"https","host":"postman-echo.com","accept-encoding":"gzip","user-agent":"okhttp/3.12.0","x-forwarded-port":"443"},"url":"https://postman-echo.com/get"}"""
+        val expected = """{"args":{},"headers":{"x-forwarded-proto":"https","host":"postman-echo.com","accept-encoding":"gzip","user-agent":"okhttp/3.14.2","x-forwarded-port":"443"},"url":"https://postman-echo.com/get"}"""
         assertEquals(expected, response)
     }
 
@@ -71,7 +71,7 @@ class ResponseExtKtTest {
                 "x-forwarded-proto" to "https"
                 "host" to "postman-echo.com"
                 "accept-encoding" to "gzip"
-                "user-agent" to "okhttp/3.12.0"
+                "user-agent" to "okhttp/3.14.2"
                 "x-forwarded-port" to "443"
             }
             "url" to getUrl
@@ -92,7 +92,7 @@ class ResponseExtKtTest {
         |    "x-forwarded-proto": "https",
         |    "host": "postman-echo.com",
         |    "accept-encoding": "gzip",
-        |    "user-agent": "okhttp/3.12.0",
+        |    "user-agent": "okhttp/3.14.2",
         |    "x-forwarded-port": "443"
         |  },
         |  "url": "https://postman-echo.com/stream/2"
@@ -104,7 +104,7 @@ class ResponseExtKtTest {
         |    "x-forwarded-proto": "https",
         |    "host": "postman-echo.com",
         |    "accept-encoding": "gzip",
-        |    "user-agent": "okhttp/3.12.0",
+        |    "user-agent": "okhttp/3.14.2",
         |    "x-forwarded-port": "443"
         |  },
         |  "url": "https://postman-echo.com/stream/2"
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt
index 6592c30c..4ea13a27 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/UriAsyncExtTest.kt
@@ -1,6 +1,6 @@
 package io.github.rybalkinsd.kohttp.ext
 
-import io.github.rybalkinsd.kohttp.util.asJson
+import io.github.rybalkinsd.kohttp.ext.asJson
 import kotlinx.coroutines.runBlocking
 import org.junit.Test
 import java.net.URL
@@ -19,7 +19,7 @@ class UriAsyncExtTest {
 
         runBlocking {
             response.await().use {
-                val parsedResponse = it.body()?.string().asJson()
+                val parsedResponse = it.asJson()
 
                 assertEquals(200, it.code())
                 assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt())
@@ -35,7 +35,7 @@ class UriAsyncExtTest {
 
         runBlocking {
             response.await().use {
-                val parsedResponse = it.body()?.string().asJson()
+                val parsedResponse = it.asJson()
 
                 assertEquals(200, it.code())
                 assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt())

From eb8d48907f97ae605a796336f6ff5dec5b54f429 Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Wed, 26 Jun 2019 12:38:20 +0300
Subject: [PATCH 25/38] initial

---
 .../kohttp/dsl/context/ParamContext.kt        |  7 +--
 .../rybalkinsd/kohttp/ext/HttpContextExt.kt   | 12 +++++
 .../io/github/rybalkinsd/kohttp/Utils.kt      | 17 +++++-
 .../kohttp/dsl/HttpDeleteDslKtTest.kt         | 15 +++---
 .../rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt | 51 ++++++++++++++----
 .../kohttp/dsl/HttpPatchDslKtTest.kt          | 16 +++---
 .../kohttp/dsl/HttpPostDslKtTest.kt           | 26 ++++-----
 .../rybalkinsd/kohttp/dsl/HttpPutDslKtTest.kt | 15 +++---
 .../rybalkinsd/kohttp/dsl/UploadDslKtTest.kt  |  4 +-
 .../kohttp/ext/HttpContextExtKtTest.kt        | 53 +++++++++++++++++++
 10 files changed, 165 insertions(+), 51 deletions(-)

diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
index fe0764a0..29dba1e2 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
@@ -2,11 +2,12 @@ package io.github.rybalkinsd.kohttp.dsl.context
 
 @HttpDslMarker
 class ParamContext {
-    private val map: MutableMap = mutableMapOf()
+    private val params: MutableList> = mutableListOf()
 
     infix fun String.to(v: Any) {
-        map[this] = v
+        params += Pair(this, v)
     }
 
-    internal fun forEach(action: (k: String, v: Any) -> Unit) = map.forEach(action)
+    internal fun forEach(action: (k: String, v: Any) -> Unit) =
+        params.forEach { (k, v) -> action(k, v) }
 }
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
index d4206644..df858d52 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
@@ -17,6 +17,18 @@ fun HttpContext.url(url: URL) {
     if (url.port != -1) {
         port = url.port
     }
+
+    url.query?.let { query ->
+        param {
+            query.split("&")
+                .map { it.split("=") }
+                .groupBy({ it[0] }, { it.getOrElse(1) { "" } })
+                .forEach { (k, v) ->
+                    k to (if (v.size == 1) v.first() else v)
+                }
+        }
+    }
+
     path = url.path
 }
 
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt
index 245edd89..51b495e6 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt
@@ -2,12 +2,25 @@ package io.github.rybalkinsd.kohttp
 
 import com.fasterxml.jackson.databind.JsonNode
 import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+/**
+ * @author sergey
+ */
+fun assertContainsExactly(expected: Map, actual: JsonNode) {
+    assertContainsAtLeast(expected, actual)
+
+    actual.fieldNames().forEach {
+        assertTrue(expected.containsKey(it), message = "Expected does not contain $it")
+        assertEquals(expected[it].toString(), actual[it].asText())
+    }
+}
 
 /**
  * @author gokul
  */
-fun assertResponses(expected: Map, actual: JsonNode) {
+fun assertContainsAtLeast(expected: Map, actual: JsonNode) {
     expected.forEach { (t, u) ->
-        assertEquals(u, actual[t]?.asText())
+        assertEquals(u.toString(), actual[t]?.asText())
     }
 }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt
index 7895ada2..e4f53b0b 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpDeleteDslKtTest.kt
@@ -1,6 +1,7 @@
 package io.github.rybalkinsd.kohttp.dsl
 
-import io.github.rybalkinsd.kohttp.assertResponses
+import io.github.rybalkinsd.kohttp.assertContainsAtLeast
+import io.github.rybalkinsd.kohttp.assertContainsExactly
 import io.github.rybalkinsd.kohttp.ext.asJson
 import org.junit.Test
 import kotlin.test.assertEquals
@@ -50,9 +51,9 @@ class HttpDeleteDslKtTest {
             }
         }.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedHeader, parsedResponse["headers"])
-            assertResponses(expectedParams, parsedResponse["args"])
-            assertResponses(expectedForm, parsedResponse["form"])
+            assertContainsAtLeast(expectedHeader, parsedResponse["headers"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
+            assertContainsExactly(expectedForm, parsedResponse["form"])
             assertEquals(200, it.code())
         }
     }
@@ -99,9 +100,9 @@ class HttpDeleteDslKtTest {
 
         response.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedHeader, parsedResponse["headers"])
-            assertResponses(expectedParams, parsedResponse["args"])
-            assertResponses(expectedJson, parsedResponse["json"])
+            assertContainsAtLeast(expectedHeader, parsedResponse["headers"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
+            assertContainsExactly(expectedJson, parsedResponse["json"])
             assertEquals(200, it.code())
         }
     }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt
index ef999ee9..ee76271b 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt
@@ -1,8 +1,9 @@
 package io.github.rybalkinsd.kohttp.dsl
 
-import io.github.rybalkinsd.kohttp.assertResponses
-import io.github.rybalkinsd.kohttp.ext.asJson
+import io.github.rybalkinsd.kohttp.assertContainsAtLeast
+import io.github.rybalkinsd.kohttp.assertContainsExactly
 import io.github.rybalkinsd.kohttp.ext.url
+import io.github.rybalkinsd.kohttp.ext.asJson
 import io.github.rybalkinsd.kohttp.util.json
 import org.junit.Test
 import kotlin.test.assertEquals
@@ -42,7 +43,7 @@ class HttpGetDslKtTest {
                 "text" to listOf("iphone", "not iphone")
                 "lr" to 213
             }
-        }.also { println(it) }
+        }
 
         response.use {
             assertEquals(false, it.request().url().query()?.contains("%5B"))
@@ -93,8 +94,8 @@ class HttpGetDslKtTest {
         }
         response.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedHeader, parsedResponse["headers"])
-            assertResponses(expectedParams, parsedResponse["args"])
+            assertContainsAtLeast(expectedHeader, parsedResponse["headers"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
             assertEquals(200, it.code())
         }
     }
@@ -140,16 +141,20 @@ class HttpGetDslKtTest {
         }
         response.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedHeader, parsedResponse["headers"])
-            assertResponses(expectedParams, parsedResponse["args"])
+            assertContainsAtLeast(expectedHeader, parsedResponse["headers"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
             assertEquals(200, it.code())
         }
     }
 
+
+    /**
+     * @since 0.10.0
+     */
     @Test
-    fun `omitting query params in url`() {
+    fun `no omitting of query params in url`() {
         val response = httpGet {
-            url("http://postman-echo.com/get?a=missing&b=missing")
+            url("http://postman-echo.com/get?a=exists&b=exists")
 
             param {
                 "c" to "exists"
@@ -157,12 +162,38 @@ class HttpGetDslKtTest {
         }
 
         val expectedParams = mapOf(
+            "a" to "exists",
+            "b" to "exists",
             "c" to "exists"
         )
 
         response.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedParams, parsedResponse["args"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
+        }
+    }
+
+    /**
+     * @since 0.10.0
+     */
+    @Test
+    fun `multiple param declaration with same key`() {
+        val response = httpGet {
+            url("http://postman-echo.com/get?a=1&a=")
+
+            param {
+                "a" to "3"
+            }
+        }
+
+        val expectedParams = mapOf(
+            "a" to listOf("1", "", "3")
+        )
+
+
+        response.use {
+            val parsedResponse = it.asJson()
+            assertContainsExactly(expectedParams, parsedResponse["args"])
         }
     }
 
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDslKtTest.kt
index f3772346..6dfc7b9c 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDslKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPatchDslKtTest.kt
@@ -1,7 +1,9 @@
 package io.github.rybalkinsd.kohttp.dsl
 
-import io.github.rybalkinsd.kohttp.assertResponses
+import io.github.rybalkinsd.kohttp.assertContainsAtLeast
+import io.github.rybalkinsd.kohttp.assertContainsExactly
 import io.github.rybalkinsd.kohttp.ext.asJson
+
 import org.junit.Test
 import kotlin.test.assertEquals
 
@@ -50,9 +52,9 @@ class HttpPatchDslKtTest {
             }
         }.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedHeader, parsedResponse["headers"])
-            assertResponses(expectedParams, parsedResponse["args"])
-            assertResponses(expectedForm, parsedResponse["form"])
+            assertContainsAtLeast(expectedHeader, parsedResponse["headers"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
+            assertContainsExactly(expectedForm, parsedResponse["form"])
             assertEquals(200, it.code())
         }
     }
@@ -99,9 +101,9 @@ class HttpPatchDslKtTest {
 
         response.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedHeader, parsedResponse["headers"])
-            assertResponses(expectedParams, parsedResponse["args"])
-            assertResponses(expectedJson, parsedResponse["json"])
+            assertContainsAtLeast(expectedHeader, parsedResponse["headers"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
+            assertContainsExactly(expectedJson, parsedResponse["json"])
             assertEquals(200, it.code())
         }
     }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt
index e071307f..f8d28743 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPostDslKtTest.kt
@@ -1,6 +1,7 @@
 package io.github.rybalkinsd.kohttp.dsl
 
-import io.github.rybalkinsd.kohttp.assertResponses
+import io.github.rybalkinsd.kohttp.assertContainsAtLeast
+import io.github.rybalkinsd.kohttp.assertContainsExactly
 import io.github.rybalkinsd.kohttp.ext.asJson
 import org.junit.Test
 import java.io.File
@@ -52,9 +53,9 @@ class HttpPostDslKtTest {
             }
         }.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedHeader, parsedResponse["headers"])
-            assertResponses(expectedParams, parsedResponse["args"])
-            assertResponses(expectedForm, parsedResponse["form"])
+            assertContainsAtLeast(expectedHeader, parsedResponse["headers"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
+            assertContainsExactly(expectedForm, parsedResponse["form"])
             assertEquals(200, it.code())
         }
     }
@@ -78,8 +79,7 @@ class HttpPostDslKtTest {
             }
         }.use {
             val parsedResponse = it.asJson()
-            println(parsedResponse)
-            assertResponses(expectedForm, parsedResponse["form"])
+            assertContainsExactly(expectedForm, parsedResponse["form"])
             assertEquals(200, it.code())
         }
     }
@@ -139,9 +139,9 @@ class HttpPostDslKtTest {
 
         response.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedHeader, parsedResponse["headers"])
-            assertResponses(expectedParams, parsedResponse["args"])
-            assertResponses(expectedJson, parsedResponse["json"])
+            assertContainsAtLeast(expectedHeader, parsedResponse["headers"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
+            assertContainsExactly(expectedJson, parsedResponse["json"])
             assertEquals(200, it.code())
         }
     }
@@ -164,7 +164,7 @@ class HttpPostDslKtTest {
 
         response.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedJson, parsedResponse["json"])
+            assertContainsExactly(expectedJson, parsedResponse["json"])
             assertEquals(200, it.code())
         }
     }
@@ -187,7 +187,7 @@ class HttpPostDslKtTest {
 
         response.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedJson, parsedResponse["json"])
+            assertContainsExactly(expectedJson, parsedResponse["json"])
             assertEquals(200, it.code())
         }
     }
@@ -259,8 +259,8 @@ class HttpPostDslKtTest {
         }.use {
             val parsedResponse = it.asJson()
             val headers = parsedResponse["headers"]
-            assertResponses(expectedHeader, headers)
-            assertResponses(expectedParams, parsedResponse["args"])
+            assertContainsAtLeast(expectedHeader, headers)
+            assertContainsExactly(expectedParams, parsedResponse["args"])
             assertEquals(0, headers["content-length"].asInt())
             assertEquals(200, it.code())
         }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDslKtTest.kt
index 78ac6f62..a0c11b9d 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDslKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpPutDslKtTest.kt
@@ -1,6 +1,7 @@
 package io.github.rybalkinsd.kohttp.dsl
 
-import io.github.rybalkinsd.kohttp.assertResponses
+import io.github.rybalkinsd.kohttp.assertContainsAtLeast
+import io.github.rybalkinsd.kohttp.assertContainsExactly
 import io.github.rybalkinsd.kohttp.ext.asJson
 import org.junit.Test
 import kotlin.test.assertEquals
@@ -50,9 +51,9 @@ class HttpPutDslKtTest {
             }
         }.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedHeader, parsedResponse["headers"])
-            assertResponses(expectedParams, parsedResponse["args"])
-            assertResponses(expectedForm, parsedResponse["form"])
+            assertContainsAtLeast(expectedHeader, parsedResponse["headers"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
+            assertContainsExactly(expectedForm, parsedResponse["form"])
             assertEquals(200, it.code())
         }
     }
@@ -99,9 +100,9 @@ class HttpPutDslKtTest {
 
         response.use {
             val parsedResponse = it.asJson()
-            assertResponses(expectedHeader, parsedResponse["headers"])
-            assertResponses(expectedParams, parsedResponse["args"])
-            assertResponses(expectedJson, parsedResponse["json"])
+            assertContainsAtLeast(expectedHeader, parsedResponse["headers"])
+            assertContainsExactly(expectedParams, parsedResponse["args"])
+            assertContainsExactly(expectedJson, parsedResponse["json"])
             assertEquals(200, it.code())
         }
     }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt
index 24e620bc..31638e3d 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt
@@ -1,6 +1,6 @@
 package io.github.rybalkinsd.kohttp.dsl
 
-import io.github.rybalkinsd.kohttp.assertResponses
+import io.github.rybalkinsd.kohttp.assertContainsExactly
 import io.github.rybalkinsd.kohttp.ext.asJson
 import org.junit.Test
 import kotlin.test.assertEquals
@@ -79,7 +79,7 @@ class UploadDslKtTest {
 
         assertEquals(expectedArgs["query"], parsedResponse["args"]["query"].asText())
         assertEquals(expectedArgs["listOfParams"], parsedResponse["args"]["listOfParams"].toString())
-        assertResponses(expectedHeaders, parsedResponse["headers"])
+        assertContainsExactly(expectedHeaders, parsedResponse["headers"])
         assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt())
         assertTrue { parsedResponse["headers"]["content-type"].asText().startsWith("multipart/mixed; boundary=") }
     }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
index afe3e68a..23fbab75 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
@@ -1,11 +1,13 @@
 package io.github.rybalkinsd.kohttp.ext
 
+import io.github.rybalkinsd.kohttp.dsl.context.HttpContext
 import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext
 import org.junit.Test
 import java.net.MalformedURLException
 import java.net.URL
 import kotlin.test.assertEquals
 import kotlin.test.assertNull
+import kotlin.test.assertTrue
 
 class HttpContextExtKtTest {
 
@@ -58,4 +60,55 @@ class HttpContextExtKtTest {
         HttpGetContext().apply { url(URL("https", null, 0, "cat.gif")) }
     }
 
+
+//    path/?a=b"
+//  "path?a=b"
+//  "path/?a=b&c=&d=123"
+//  "path?a=b#tag"
+//  "path?a=xxx&a=&a=yyy
+    @Test
+    fun `url with single param`() {
+        val context = HttpGetContext().apply {
+            url("http://www.example.org/path?a=b")
+        }
+
+        val params = context.params
+        assertEquals(1, params.size)
+        assertTrue(params.containsKey("a"))
+        assertEquals("b", params["a"])
+    }
+
+    @Test
+    fun `url with multiple params`() {
+        val context = HttpGetContext().apply {
+            url("http://www.example.org/path?a=b&c=&d=123#label")
+        }
+
+        val params = context.params
+        assertEquals(3, params.size)
+        assertEquals("b", params["a"])
+        assertEquals("", params["c"])
+        assertEquals("123", params["d"])
+    }
+
+    @Test
+    fun `url with multiple occurrences of parameter`() {
+        val context = HttpGetContext().apply {
+            url("http://www.example.org/path?a=&a=123&a=xxx#label")
+        }
+
+        val params = context.params
+        assertEquals(1, params.size)
+        assertEquals(listOf("", "123", "xxx"), params["a"])
+    }
+
+    private val HttpContext.params: Map
+        get() {
+            val collector = mutableMapOf()
+            param {
+                forEach { k, v -> collector[k] = v }
+            }
+
+            return collector
+        }
 }

From 6eea7b0b4c1c523bc00f6546aae73b30da72d8cd Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Thu, 27 Jun 2019 16:47:58 +0300
Subject: [PATCH 26/38] fixed assertions in tests

---
 .../io/github/rybalkinsd/kohttp/Utils.kt      | 24 ++++++++++++++++---
 .../rybalkinsd/kohttp/dsl/UploadDslKtTest.kt  |  4 ++--
 2 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt
index 51b495e6..d1c6f2e8 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/Utils.kt
@@ -1,10 +1,12 @@
 package io.github.rybalkinsd.kohttp
 
 import com.fasterxml.jackson.databind.JsonNode
+import com.fasterxml.jackson.databind.node.ArrayNode
 import kotlin.test.assertEquals
 import kotlin.test.assertTrue
 
 /**
+ * todo: Probably it would be better to provide extension function to test Json
  * @author sergey
  */
 fun assertContainsExactly(expected: Map, actual: JsonNode) {
@@ -12,7 +14,10 @@ fun assertContainsExactly(expected: Map, actual: JsonNode) {
 
     actual.fieldNames().forEach {
         assertTrue(expected.containsKey(it), message = "Expected does not contain $it")
-        assertEquals(expected[it].toString(), actual[it].asText())
+        val ex = expected[it]
+        if (ex !is List<*>) {
+            assertEquals(ex.toString(), actual[it].asText())
+        }
     }
 }
 
@@ -20,7 +25,20 @@ fun assertContainsExactly(expected: Map, actual: JsonNode) {
  * @author gokul
  */
 fun assertContainsAtLeast(expected: Map, actual: JsonNode) {
-    expected.forEach { (t, u) ->
-        assertEquals(u.toString(), actual[t]?.asText())
+    expected.forEach { (k, v) ->
+        when (v) {
+            is List<*> -> {
+                val arrayNode = actual[k] as? ArrayNode ?: throw Exception("$k type is not Array, as expected")
+
+                assertEquals(v.size, arrayNode.size())
+
+                arrayNode.forEachIndexed { i, element ->
+                    assertEquals(v[i].toString(), element.asText())
+                }
+
+            }
+            else -> assertEquals(v.toString(), actual[k]?.asText())
+        }
+
     }
 }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt
index 31638e3d..a0a0d1dc 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/UploadDslKtTest.kt
@@ -1,6 +1,6 @@
 package io.github.rybalkinsd.kohttp.dsl
 
-import io.github.rybalkinsd.kohttp.assertContainsExactly
+import io.github.rybalkinsd.kohttp.assertContainsAtLeast
 import io.github.rybalkinsd.kohttp.ext.asJson
 import org.junit.Test
 import kotlin.test.assertEquals
@@ -79,7 +79,7 @@ class UploadDslKtTest {
 
         assertEquals(expectedArgs["query"], parsedResponse["args"]["query"].asText())
         assertEquals(expectedArgs["listOfParams"], parsedResponse["args"]["listOfParams"].toString())
-        assertContainsExactly(expectedHeaders, parsedResponse["headers"])
+        assertContainsAtLeast(expectedHeaders, parsedResponse["headers"])
         assertEquals(1046214, parsedResponse["headers"]["content-length"].asInt())
         assertTrue { parsedResponse["headers"]["content-type"].asText().startsWith("multipart/mixed; boundary=") }
     }

From 455464ca35d4a75050bf67989eb74fd9ec58c7ba Mon Sep 17 00:00:00 2001
From: Dmitry Tropanets 
Date: Thu, 27 Jun 2019 16:12:00 +0200
Subject: [PATCH 27/38] Feature/url function params support (#116)

* add support for query in url()

* change query container to list to support query lists.

fixes an issue with query lists, e.g. "?id[]=1&id[]=2", where key would be duplicated and replaced in a map

* add tests for query
---
 .../kohttp/dsl/context/ParamContext.kt        |  4 +-
 .../rybalkinsd/kohttp/ext/HttpContextExt.kt   | 13 +++++
 .../kohttp/ext/HttpContextExtKtTest.kt        | 54 +++++++++++++++++--
 3 files changed, 64 insertions(+), 7 deletions(-)

diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
index 29dba1e2..67f31369 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
@@ -5,9 +5,9 @@ class ParamContext {
     private val params: MutableList> = mutableListOf()
 
     infix fun String.to(v: Any) {
-        params += Pair(this, v)
+        list += Pair(this, v)
     }
 
     internal fun forEach(action: (k: String, v: Any) -> Unit) =
-        params.forEach { (k, v) -> action(k, v) }
+        list.forEach { (k, v) -> action(k, v) }
 }
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
index df858d52..bb424f2d 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
@@ -30,6 +30,19 @@ fun HttpContext.url(url: URL) {
     }
 
     path = url.path
+
+    if (url.query?.isNotBlank() == true) {
+        param {
+            url.query.split("&")
+                .onEach {
+                    val queryComponents = it.split("=", limit = 2)
+                    if (queryComponents.size == 1) {
+                        throw IllegalArgumentException("unexpected query: $it")
+                    }
+                    queryComponents[0] to queryComponents[1]
+                }
+        }
+    }
 }
 
 /**
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
index 23fbab75..ee4c64d3 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
@@ -2,6 +2,7 @@ package io.github.rybalkinsd.kohttp.ext
 
 import io.github.rybalkinsd.kohttp.dsl.context.HttpContext
 import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext
+import okhttp3.HttpUrl
 import org.junit.Test
 import java.net.MalformedURLException
 import java.net.URL
@@ -13,11 +14,12 @@ class HttpContextExtKtTest {
 
     @Test
     fun `full url test`() {
-        val context = HttpGetContext().apply { url("https://www.example.org:8080/path") }
-        assertEquals("https", context.scheme)
-        assertEquals("www.example.org", context.host)
-        assertEquals("/path", context.path)
-        assertEquals(8080, context.port)
+        val url = httpUrlFor { url("https://www.example.org:8080/path?foo=bar") }
+        assertEquals("https", url.scheme())
+        assertEquals("www.example.org", url.host())
+        assertEquals("/path", url.encodedPath())
+        assertEquals(8080, url.port())
+        assertEquals("foo=bar", url.query())
     }
 
     @Test
@@ -60,6 +62,48 @@ class HttpContextExtKtTest {
         HttpGetContext().apply { url(URL("https", null, 0, "cat.gif")) }
     }
 
+    @Test
+    fun `empty query`() {
+        val url = httpUrlFor { url("https://www.example.org/?") }
+        assertEquals(null, url.query())
+    }
+
+    @Test
+    fun `query with malformed path`() {
+        val url = httpUrlFor { url("https://www.example.org/?a=b") }
+        assertEquals("a=b", url.query())
+    }
+
+    @Test
+    fun `query added from 2 sources`() {
+        val url = httpUrlFor {
+            url("https://www.example.org/path?a=xxx&a=")
+            param {
+                "a" to "yyy"
+            }
+        }
+        assertEquals("a=xxx&a=&a=yyy", url.query())
+    }
+
+    @Test
+    fun `duplicate query keys`() {
+        val url = httpUrlFor { url("https://www.example.org/path?id[]=1&id[]=2") }
+        assertEquals("id[]=1&id[]=2", url.query())
+    }
+
+    @Test(expected = IllegalArgumentException::class)
+    fun `query without value`() {
+        HttpGetContext().apply { url("https://www.example.org/path?foo") }
+    }
+
+    private fun httpUrlFor(init: HttpContext.() -> Unit): HttpUrl =
+        HttpGetContext()
+            .run {
+                init()
+                makeUrl().build()
+            }
+    }
+
 
 //    path/?a=b"
 //  "path?a=b"

From d405a02b2e1a27324ef74d06718134b77114e196 Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Thu, 27 Jun 2019 18:32:22 +0300
Subject: [PATCH 28/38] not fixed: list + params

---
 .../kohttp/dsl/context/ParamContext.kt        |   4 +-
 .../rybalkinsd/kohttp/ext/HttpContextExt.kt   |  24 +---
 .../kohttp/ext/HttpContextExtKtTest.kt        | 103 ++++++++----------
 3 files changed, 55 insertions(+), 76 deletions(-)

diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
index 67f31369..29dba1e2 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
@@ -5,9 +5,9 @@ class ParamContext {
     private val params: MutableList> = mutableListOf()
 
     infix fun String.to(v: Any) {
-        list += Pair(this, v)
+        params += Pair(this, v)
     }
 
     internal fun forEach(action: (k: String, v: Any) -> Unit) =
-        list.forEach { (k, v) -> action(k, v) }
+        params.forEach { (k, v) -> action(k, v) }
 }
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
index bb424f2d..9b46fd46 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
@@ -14,32 +14,18 @@ fun HttpContext.url(url: URL) {
 
     host = url.host ?: throw IllegalArgumentException("unexpected host: $host")
 
-    if (url.port != -1) {
+    if (url.port != -1)
         port = url.port
-    }
-
-    url.query?.let { query ->
-        param {
-            query.split("&")
-                .map { it.split("=") }
-                .groupBy({ it[0] }, { it.getOrElse(1) { "" } })
-                .forEach { (k, v) ->
-                    k to (if (v.size == 1) v.first() else v)
-                }
-        }
-    }
 
     path = url.path
 
     if (url.query?.isNotBlank() == true) {
         param {
             url.query.split("&")
-                .onEach {
-                    val queryComponents = it.split("=", limit = 2)
-                    if (queryComponents.size == 1) {
-                        throw IllegalArgumentException("unexpected query: $it")
-                    }
-                    queryComponents[0] to queryComponents[1]
+                .map { it.split("=", limit = 2) }
+                .groupBy({ it[0] }, { it.getOrElse(1) { "" } })
+                .forEach { (k, v) ->
+                    k to (if (v.size == 1) v.first() else v)
                 }
         }
     }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
index ee4c64d3..fb275478 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
@@ -8,18 +8,16 @@ import java.net.MalformedURLException
 import java.net.URL
 import kotlin.test.assertEquals
 import kotlin.test.assertNull
-import kotlin.test.assertTrue
 
 class HttpContextExtKtTest {
 
     @Test
     fun `full url test`() {
-        val url = httpUrlFor { url("https://www.example.org:8080/path?foo=bar") }
-        assertEquals("https", url.scheme())
-        assertEquals("www.example.org", url.host())
-        assertEquals("/path", url.encodedPath())
-        assertEquals(8080, url.port())
-        assertEquals("foo=bar", url.query())
+        val context = HttpGetContext().apply { url("https://www.example.org:8080/path?foo=bar") }
+        assertEquals("https", context.scheme)
+        assertEquals("www.example.org", context.host)
+        assertEquals("/path", context.path)
+        assertEquals("bar", context.params["foo"])
     }
 
     @Test
@@ -51,7 +49,6 @@ class HttpContextExtKtTest {
         HttpGetContext().apply { url(URL(null, null, 0, "cat.gif")) }
     }
 
-
     @Test(expected = IllegalArgumentException::class)
     fun `not http or https protocol url`() {
         HttpGetContext().apply { url(URL("file", null, 0, "cat.gif")) }
@@ -63,62 +60,19 @@ class HttpContextExtKtTest {
     }
 
     @Test
-    fun `empty query`() {
+    fun `url with empty query`() {
         val url = httpUrlFor { url("https://www.example.org/?") }
         assertEquals(null, url.query())
     }
 
-    @Test
-    fun `query with malformed path`() {
-        val url = httpUrlFor { url("https://www.example.org/?a=b") }
-        assertEquals("a=b", url.query())
-    }
-
-    @Test
-    fun `query added from 2 sources`() {
-        val url = httpUrlFor {
-            url("https://www.example.org/path?a=xxx&a=")
-            param {
-                "a" to "yyy"
-            }
-        }
-        assertEquals("a=xxx&a=&a=yyy", url.query())
-    }
-
-    @Test
-    fun `duplicate query keys`() {
-        val url = httpUrlFor { url("https://www.example.org/path?id[]=1&id[]=2") }
-        assertEquals("id[]=1&id[]=2", url.query())
-    }
-
-    @Test(expected = IllegalArgumentException::class)
-    fun `query without value`() {
-        HttpGetContext().apply { url("https://www.example.org/path?foo") }
-    }
-
-    private fun httpUrlFor(init: HttpContext.() -> Unit): HttpUrl =
-        HttpGetContext()
-            .run {
-                init()
-                makeUrl().build()
-            }
-    }
-
-
-//    path/?a=b"
-//  "path?a=b"
-//  "path/?a=b&c=&d=123"
-//  "path?a=b#tag"
-//  "path?a=xxx&a=&a=yyy
     @Test
     fun `url with single param`() {
         val context = HttpGetContext().apply {
-            url("http://www.example.org/path?a=b")
+            url("http://www.example.org/path/?a=b")
         }
 
         val params = context.params
         assertEquals(1, params.size)
-        assertTrue(params.containsKey("a"))
         assertEquals("b", params["a"])
     }
 
@@ -138,14 +92,51 @@ class HttpContextExtKtTest {
     @Test
     fun `url with multiple occurrences of parameter`() {
         val context = HttpGetContext().apply {
-            url("http://www.example.org/path?a=&a=123&a=xxx#label")
+            url("http://www.example.org/path?a&a=&a=123&a=xxx#label")
         }
 
         val params = context.params
         assertEquals(1, params.size)
-        assertEquals(listOf("", "123", "xxx"), params["a"])
+        assertEquals(listOf("", "", "123", "xxx"), params["a"])
     }
 
+    @Test
+    fun `query added from 2 sources`() {
+        val context = HttpGetContext().apply {
+            url("https://www.example.org/path?a=xxx&a=")
+            param {
+                "a" to "yyy"
+            }
+        }
+
+        val params = context.params
+        assertEquals(1, params.size)
+        assertEquals(listOf("xxx", "", "yyy"), params["a"])
+    }
+
+    @Test
+    fun `duplicate query keys`() {
+        val url = httpUrlFor { url("https://www.example.org/path?id[]=1&id[]=2") }
+        assertEquals("id[]=1&id[]=2", url.query())
+    }
+
+
+    @Test
+    fun `query param without value`() {
+        val context = HttpGetContext().apply { url("https://www.example.org/path?foo&bar=baz") }
+        with(context.params) {
+            assertEquals(2, size)
+            assertEquals("", get("foo"))
+            assertEquals("baz", get("bar"))
+        }
+    }
+
+    private fun httpUrlFor(init: HttpContext.() -> Unit): HttpUrl =
+        HttpGetContext().run {
+            init()
+            makeUrl().build()
+        }
+
     private val HttpContext.params: Map
         get() {
             val collector = mutableMapOf()
@@ -156,3 +147,5 @@ class HttpContextExtKtTest {
             return collector
         }
 }
+
+

From 01cb678537f4d8b3958bcec58d5c0933a6533ef3 Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Fri, 28 Jun 2019 19:07:07 +0300
Subject: [PATCH 29/38] 1. Param.to accept Any? 2. params internal storage
 update

---
 .../kohttp/dsl/context/HttpContext.kt         |  1 +
 .../kohttp/dsl/context/ParamContext.kt        | 19 ++++++++---
 .../rybalkinsd/kohttp/ext/HttpContextExt.kt   |  2 +-
 .../rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt | 28 +++++++++++++++-
 .../kohttp/ext/HttpContextExtKtTest.kt        | 32 +++++++++++--------
 .../kohttp/ext/ResponseExtKtTest.kt           | 27 +++++++++++-----
 6 files changed, 81 insertions(+), 28 deletions(-)

diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/HttpContext.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/HttpContext.kt
index 8f9dc589..269eb4d5 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/HttpContext.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/HttpContext.kt
@@ -59,6 +59,7 @@ sealed class HttpContext(private val method: Method = GET) : IHttpContext {
         path?.let { encodedPath(it) }
         paramContext.forEach { k, v ->
             when (v) {
+                null -> addQueryParameter(k, null)
                 is List<*> -> v.forEach { addQueryParameter(k, it.toString()) }
                 else -> addQueryParameter(k, v.toString())
             }
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
index 29dba1e2..92af7fad 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
@@ -2,12 +2,21 @@ package io.github.rybalkinsd.kohttp.dsl.context
 
 @HttpDslMarker
 class ParamContext {
-    private val params: MutableList> = mutableListOf()
+    private val params: MutableMap> = mutableMapOf()
 
-    infix fun String.to(v: Any) {
-        params += Pair(this, v)
+    infix fun String.to(v: Any?) {
+        params.computeIfAbsent(this) { mutableListOf() }.apply {
+            when (v) {
+                null -> add(null)
+                is List<*> -> addAll(v)
+                else -> add(v)
+            }
+        }
     }
 
-    internal fun forEach(action: (k: String, v: Any) -> Unit) =
-        params.forEach { (k, v) -> action(k, v) }
+    internal fun forEach(action: (k: String, v: Any?) -> Unit) =
+        params.forEach { (k, v) ->
+            action(k, if (v.size == 1) v.first() else v)
+        }
+
 }
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
index 9b46fd46..12424866 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
@@ -23,7 +23,7 @@ fun HttpContext.url(url: URL) {
         param {
             url.query.split("&")
                 .map { it.split("=", limit = 2) }
-                .groupBy({ it[0] }, { it.getOrElse(1) { "" } })
+                .groupBy({ it[0] }, { it.getOrNull(1) })
                 .forEach { (k, v) ->
                     k to (if (v.size == 1) v.first() else v)
                 }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt
index ee76271b..a6905519 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpGetDslKtTest.kt
@@ -2,8 +2,8 @@ package io.github.rybalkinsd.kohttp.dsl
 
 import io.github.rybalkinsd.kohttp.assertContainsAtLeast
 import io.github.rybalkinsd.kohttp.assertContainsExactly
-import io.github.rybalkinsd.kohttp.ext.url
 import io.github.rybalkinsd.kohttp.ext.asJson
+import io.github.rybalkinsd.kohttp.ext.url
 import io.github.rybalkinsd.kohttp.util.json
 import org.junit.Test
 import kotlin.test.assertEquals
@@ -191,6 +191,32 @@ class HttpGetDslKtTest {
         )
 
 
+        response.use {
+            val parsedResponse = it.asJson()
+            assertContainsExactly(expectedParams, parsedResponse["args"])
+        }
+    }
+
+    /**
+     * @since 0.10.0
+     */
+    @Test
+    fun `multiple param declaration in param block`() {
+        val response = httpGet {
+            url("http://postman-echo.com/get?a=1&a=&a")
+
+            param {
+                "a" to null
+                "a" to "3"
+                "a" to listOf(1, 2, null)
+            }
+        }
+
+        val expectedParams = mapOf(
+            "a" to listOf("1", "", null, null, "3", 1, 2, null)
+        )
+
+
         response.use {
             val parsedResponse = it.asJson()
             assertContainsExactly(expectedParams, parsedResponse["args"])
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
index fb275478..67693684 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
@@ -2,7 +2,6 @@ package io.github.rybalkinsd.kohttp.ext
 
 import io.github.rybalkinsd.kohttp.dsl.context.HttpContext
 import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext
-import okhttp3.HttpUrl
 import org.junit.Test
 import java.net.MalformedURLException
 import java.net.URL
@@ -61,8 +60,10 @@ class HttpContextExtKtTest {
 
     @Test
     fun `url with empty query`() {
-        val url = httpUrlFor { url("https://www.example.org/?") }
-        assertEquals(null, url.query())
+        val context = HttpGetContext().apply {
+            url("https://www.example.org/?")
+        }
+        assertEquals(0, context.params.size)
     }
 
     @Test
@@ -97,7 +98,7 @@ class HttpContextExtKtTest {
 
         val params = context.params
         assertEquals(1, params.size)
-        assertEquals(listOf("", "", "123", "xxx"), params["a"])
+        assertEquals(listOf(null, "", "123", "xxx"), params["a"])
     }
 
     @Test
@@ -116,8 +117,10 @@ class HttpContextExtKtTest {
 
     @Test
     fun `duplicate query keys`() {
-        val url = httpUrlFor { url("https://www.example.org/path?id[]=1&id[]=2") }
-        assertEquals("id[]=1&id[]=2", url.query())
+        val context = HttpGetContext().apply { url("https://www.example.org/path?id[]=1&id[]=2") }
+        val params = context.params
+        assertEquals(1, params.size)
+        assertEquals(listOf("1", "2"), params["id[]"])
     }
 
 
@@ -126,20 +129,23 @@ class HttpContextExtKtTest {
         val context = HttpGetContext().apply { url("https://www.example.org/path?foo&bar=baz") }
         with(context.params) {
             assertEquals(2, size)
-            assertEquals("", get("foo"))
+            assertEquals(null, get("foo"))
             assertEquals("baz", get("bar"))
         }
     }
 
-    private fun httpUrlFor(init: HttpContext.() -> Unit): HttpUrl =
-        HttpGetContext().run {
-            init()
-            makeUrl().build()
+    @Test
+    fun `query param with value containing = symbol`() {
+        val context = HttpGetContext().apply { url("https://www.example.org/path?bool=2*2==4") }
+        with(context.params) {
+            assertEquals(1, size)
+            assertEquals("2*2==4", get("bool"))
         }
+    }
 
-    private val HttpContext.params: Map
+    private val HttpContext.params: Map
         get() {
-            val collector = mutableMapOf()
+            val collector = mutableMapOf()
             param {
                 forEach { k, v -> collector[k] = v }
             }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt
index 1a5ec1f0..b0e24743 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt
@@ -56,12 +56,24 @@ class ResponseExtKtTest {
 
     @Test
     fun `gets response as string # ext`() {
-
         val response = getUrl.httpGet().asString()
-        val expected = """{"args":{},"headers":{"x-forwarded-proto":"https","host":"postman-echo.com","accept-encoding":"gzip","user-agent":"okhttp/3.14.2","x-forwarded-port":"443"},"url":"https://postman-echo.com/get"}"""
+        val expected = """{
+            |"args":{},
+            |"headers":{
+            |   "x-forwarded-proto":"https",
+            |   "host":"postman-echo.com",
+            |   "accept-encoding":"gzip",
+            |   "user-agent":"okhttp/3.12.0",
+            |   "x-forwarded-port":"443"
+            |   },
+            |"url":"https://postman-echo.com/get"
+            |}"""
+            .trimMargin("|")
+            .replace(Regex("\\s"), "")
         assertEquals(expected, response)
     }
 
+    // todo escape '/'  write ext function
     @Test
     fun `gets response as json # ext`() {
         val response = getUrl.httpGet().asJson().toString()
@@ -71,12 +83,13 @@ class ResponseExtKtTest {
                 "x-forwarded-proto" to "https"
                 "host" to "postman-echo.com"
                 "accept-encoding" to "gzip"
-                "user-agent" to "okhttp/3.14.2"
+                "user-agent" to ""
                 "x-forwarded-port" to "443"
             }
             "url" to getUrl
-        }
-        assertEquals(response, expected)
+        }.toRegex()
+
+        assertTrue { response.matches(expected) }
     }
 
     @Test
@@ -110,7 +123,5 @@ class ResponseExtKtTest {
         |  "url": "https://postman-echo.com/stream/2"
         |}""".trimMargin("|")
         assertEquals(actual, expected)
-
-
     }
-}
\ No newline at end of file
+}

From aad2de175fbe95106431062d7ee38efb63ba6434 Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Sat, 29 Jun 2019 22:39:40 +0300
Subject: [PATCH 30/38] 1. Test for param with null value 2. add assertj lib
 for regexp matching in tests

---
 build.gradle.kts                              |  3 +-
 .../kohttp/ext/HttpContextExtKtTest.kt        | 11 +++++
 .../kohttp/ext/ResponseExtKtTest.kt           | 46 +++++++++++--------
 3 files changed, 41 insertions(+), 19 deletions(-)

diff --git a/build.gradle.kts b/build.gradle.kts
index 46b075aa..ba25eeb3 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -31,7 +31,8 @@ dependencies {
     testImplementation(kotlin("test-junit"))
     testImplementation("io.mockk:mockk:1.9.3")
     testImplementation("org.mock-server:mockserver-netty:5.5.4")
-    testImplementation( "org.mock-server:mockserver-client-java:5.5.4")
+    testImplementation("org.mock-server:mockserver-client-java:5.5.4")
+    testImplementation("org.assertj:assertj-core:3.12.2")
 }
 
 configure {
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
index 67693684..c0ea30fd 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExtKtTest.kt
@@ -2,6 +2,7 @@ package io.github.rybalkinsd.kohttp.ext
 
 import io.github.rybalkinsd.kohttp.dsl.context.HttpContext
 import io.github.rybalkinsd.kohttp.dsl.context.HttpGetContext
+import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import java.net.MalformedURLException
 import java.net.URL
@@ -143,6 +144,16 @@ class HttpContextExtKtTest {
         }
     }
 
+    @Test
+    fun `query with single null param`() {
+        val context = HttpGetContext().apply {
+            url("https://www.example.org/path?a")
+        }
+
+        assertThat(context.params.size).isEqualTo(1)
+        assertThat(context.params["a"]).isNull()
+    }
+
     private val HttpContext.params: Map
         get() {
             val collector = mutableMapOf()
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt
index b0e24743..8050dee6 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/ResponseExtKtTest.kt
@@ -2,6 +2,7 @@ package io.github.rybalkinsd.kohttp.ext
 
 import io.github.rybalkinsd.kohttp.dsl.httpGet
 import io.github.rybalkinsd.kohttp.util.json
+import org.assertj.core.api.Assertions.assertThat
 import org.junit.Test
 import kotlin.test.assertEquals
 import kotlin.test.assertNotNull
@@ -56,48 +57,50 @@ class ResponseExtKtTest {
 
     @Test
     fun `gets response as string # ext`() {
-        val response = getUrl.httpGet().asString()
-        val expected = """{
+        val response = getUrl.httpGet().asString()!!
+        val expectedRegex = """{
             |"args":{},
             |"headers":{
             |   "x-forwarded-proto":"https",
             |   "host":"postman-echo.com",
             |   "accept-encoding":"gzip",
-            |   "user-agent":"okhttp/3.12.0",
+            |   "user-agent":"okhttp/[0-9]*.[0-9]*.[0-9]*",
             |   "x-forwarded-port":"443"
             |   },
             |"url":"https://postman-echo.com/get"
             |}"""
-            .trimMargin("|")
-            .replace(Regex("\\s"), "")
-        assertEquals(expected, response)
+                .trimMargin("|")
+                .replace(Regex("\\s"),"")
+                .escape()
+
+
+        assertThat(response).matches(expectedRegex)
     }
 
-    // todo escape '/'  write ext function
     @Test
     fun `gets response as json # ext`() {
         val response = getUrl.httpGet().asJson().toString()
-        val expected = json {
+        val expectedRegex = json {
             "args" to json { }
             "headers" to json {
                 "x-forwarded-proto" to "https"
                 "host" to "postman-echo.com"
                 "accept-encoding" to "gzip"
-                "user-agent" to ""
+                "user-agent" to "okhttp/[0-9]*.[0-9]*.[0-9]*"
                 "x-forwarded-port" to "443"
             }
             "url" to getUrl
-        }.toRegex()
+        }.escape()
 
-        assertTrue { response.matches(expected) }
+        assertThat(response).matches(expectedRegex)
     }
 
     @Test
     fun `streams response # ext`() {
         val response = "https://postman-echo.com/stream/2".httpGet().asStream()
-        val arr = response?.readBytes()
-        val actual = arr?.let { String(it) }
-        val expected = """{
+                ?.readBytes()?.let { String(it) }
+
+        val expectedRegex = """{
         |  "args": {
         |    "n": "2"
         |  },
@@ -105,7 +108,7 @@ class ResponseExtKtTest {
         |    "x-forwarded-proto": "https",
         |    "host": "postman-echo.com",
         |    "accept-encoding": "gzip",
-        |    "user-agent": "okhttp/3.14.2",
+        |    "user-agent": "okhttp/[0-9]*.[0-9]*.[0-9]*",
         |    "x-forwarded-port": "443"
         |  },
         |  "url": "https://postman-echo.com/stream/2"
@@ -117,11 +120,18 @@ class ResponseExtKtTest {
         |    "x-forwarded-proto": "https",
         |    "host": "postman-echo.com",
         |    "accept-encoding": "gzip",
-        |    "user-agent": "okhttp/3.14.2",
+        |    "user-agent": "okhttp/[0-9]*.[0-9]*.[0-9*]",
         |    "x-forwarded-port": "443"
         |  },
         |  "url": "https://postman-echo.com/stream/2"
-        |}""".trimMargin("|")
-        assertEquals(actual, expected)
+        |}"""
+                .trimMargin("|")
+                .escape()
+
+        assertThat(response).matches(expectedRegex)
     }
 }
+
+private fun String.escape(): String = replace("/", "\\/")
+        .replace("{", "\\{")
+        .replace("}", "\\}")

From eec6fef7877d04a6f4998461fd84635de72e9153 Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Sat, 29 Jun 2019 23:00:54 +0300
Subject: [PATCH 31/38] Simplified param building in HttpContextExt

---
 .../io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt      | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
index 12424866..59fe2ae3 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/ext/HttpContextExt.kt
@@ -22,11 +22,8 @@ fun HttpContext.url(url: URL) {
     if (url.query?.isNotBlank() == true) {
         param {
             url.query.split("&")
-                .map { it.split("=", limit = 2) }
-                .groupBy({ it[0] }, { it.getOrNull(1) })
-                .forEach { (k, v) ->
-                    k to (if (v.size == 1) v.first() else v)
-                }
+                    .map { it.split("=", limit = 2) }
+                    .forEach { it[0] to it.getOrNull(1) }
         }
     }
 }

From 845bb3356a19a12edc9d6f42dd2c569fe3021891 Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Sat, 29 Jun 2019 23:05:26 +0300
Subject: [PATCH 32/38] Simplified ParamContext.`to` infix function for null
 case

---
 .../io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt      | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
index 92af7fad..0f20360f 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/dsl/context/ParamContext.kt
@@ -7,7 +7,6 @@ class ParamContext {
     infix fun String.to(v: Any?) {
         params.computeIfAbsent(this) { mutableListOf() }.apply {
             when (v) {
-                null -> add(null)
                 is List<*> -> addAll(v)
                 else -> add(v)
             }

From f70470493764ec48d53e98e386ddd0599aa3d66b Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Sun, 30 Jun 2019 00:11:28 +0300
Subject: [PATCH 33/38] Improved coverage for RetryInterceptor and Header
 sequence

---
 .../kohttp/dsl/HttpHeadDslKtTest.kt           |  2 +-
 .../rybalkinsd/kohttp/ext/HeadersExtKtTest.kt | 20 ++++++++++++-------
 .../interceptors/RetryInterceptorTest.kt      |  4 ++--
 3 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpHeadDslKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpHeadDslKtTest.kt
index 33c93959..5b25e75d 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpHeadDslKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/dsl/HttpHeadDslKtTest.kt
@@ -21,7 +21,7 @@ class HttpHeadDslKtTest {
                 "text" to "iphone"
                 "lr" to 213
             }
-        }.also { println(it) }
+        }
 
         response.use {
             assertEquals(200, it.code())
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt
index 002f5fcd..de91eea9 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/ext/HeadersExtKtTest.kt
@@ -6,6 +6,13 @@ import kotlin.test.assertEquals
 
 class HeadersExtKtTest {
 
+    val headers: Headers = Headers.Builder()
+            .add("zero", "0")
+            .add("one", "1")
+            .add("two", "2")
+            .add("three", "3")
+            .build()
+
     @Test
     fun `iterating with asSequence`() {
         headers.asSequence().forEachIndexed { index, header ->
@@ -20,12 +27,11 @@ class HeadersExtKtTest {
         assertEquals(3, sum)
     }
 
-    companion object {
-        val headers = Headers.Builder()
-            .add("zero", "0")
-            .add("one", "1")
-            .add("two", "2")
-            .add("three", "3")
-            .build()
+    @Test(expected = NoSuchElementException::class)
+    fun `headers does not have 5 elements`() {
+        val iterator = headers.asSequence().iterator()
+        repeat(5) {
+            iterator.next()
+        }
     }
 }
diff --git a/src/test/kotlin/io/github/rybalkinsd/kohttp/interceptors/RetryInterceptorTest.kt b/src/test/kotlin/io/github/rybalkinsd/kohttp/interceptors/RetryInterceptorTest.kt
index b564076a..3820c01e 100644
--- a/src/test/kotlin/io/github/rybalkinsd/kohttp/interceptors/RetryInterceptorTest.kt
+++ b/src/test/kotlin/io/github/rybalkinsd/kohttp/interceptors/RetryInterceptorTest.kt
@@ -49,7 +49,7 @@ class RetryInterceptorTest {
     fun `retry 5 times if socket timeout exception`() {
         val numOfRetry = 5
         createExpectationForGetWithResponseCode(numOfRetry + 1, 200, 10)
-        val retryInterceptor = spyk(RetryInterceptor(failureThreshold = numOfRetry))
+        val retryInterceptor = spyk(RetryInterceptor(failureThreshold = numOfRetry, invocationTimeout = 100))
         try {
             getCall(getHttpClientWithConnectTimeoutAndInterceptors(retryInterceptor, 1))
         } catch (ignored: SocketTimeoutException) {
@@ -93,7 +93,7 @@ class RetryInterceptorTest {
     @Test
     fun `delay increase with step`() {
         val retryInterceptor = RetryInterceptor(ratio = 2)
-        var invocationTimeout: Long = 1000
+        val invocationTimeout: Long = 1000
         assert(retryInterceptor.performAndReturnDelay(invocationTimeout) == invocationTimeout * 2)
         assert(retryInterceptor.performAndReturnDelay(invocationTimeout * 2) == invocationTimeout * 4)
         assert(retryInterceptor.performAndReturnDelay(invocationTimeout * 4) == invocationTimeout * 8)

From 506bdc861e807bce5a8ff1604c8b1238511c7137 Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Sun, 30 Jun 2019 00:21:14 +0300
Subject: [PATCH 34/38] - readme prepared for 0.10.0 - immutable error statuses
 in RetryInterceptor

---
 README.md                                                   | 6 +++---
 .../rybalkinsd/kohttp/interceptors/RetryInterceptor.kt      | 2 +-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 4e9bd526..3da03c31 100644
--- a/README.md
+++ b/README.md
@@ -12,12 +12,12 @@ Kotlin DSL http client
 
 gradle kotlin DSL:
 ```kotlin
-implementation(group = "io.github.rybalkinsd", name = "kohttp", version = "0.9.0")
+implementation(group = "io.github.rybalkinsd", name = "kohttp", version = "0.10.0")
 ```
 
 gradle groovy DSL:
 ```groovy
-implementation 'io.github.rybalkinsd:kohttp:0.9.0'
+implementation 'io.github.rybalkinsd:kohttp:0.10.0'
 ```
 
 maven:
@@ -25,7 +25,7 @@ maven:
 
   io.github.rybalkinsd
   kohttp
-  0.9.0
+  0.10.0
 
 ```
 
diff --git a/src/main/kotlin/io/github/rybalkinsd/kohttp/interceptors/RetryInterceptor.kt b/src/main/kotlin/io/github/rybalkinsd/kohttp/interceptors/RetryInterceptor.kt
index fbba782b..ce8d533f 100644
--- a/src/main/kotlin/io/github/rybalkinsd/kohttp/interceptors/RetryInterceptor.kt
+++ b/src/main/kotlin/io/github/rybalkinsd/kohttp/interceptors/RetryInterceptor.kt
@@ -21,7 +21,7 @@ class RetryInterceptor(
     private val failureThreshold: Int = 3,
     private val invocationTimeout: Long = 0,
     private val ratio: Int = 1,
-    private var errorStatuses: List = listOf(503, 504)
+    private val errorStatuses: List = listOf(503, 504)
 ) : Interceptor {
 
     override fun intercept(chain: Interceptor.Chain): Response {

From 40ac0dde1bc183883b1447836a8941d1d7d7c27f Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Sun, 30 Jun 2019 00:40:14 +0300
Subject: [PATCH 35/38] coverage target

---
 src/.codecov.yaml | 4 ++++
 1 file changed, 4 insertions(+)
 create mode 100644 src/.codecov.yaml

diff --git a/src/.codecov.yaml b/src/.codecov.yaml
new file mode 100644
index 00000000..111734bb
--- /dev/null
+++ b/src/.codecov.yaml
@@ -0,0 +1,4 @@
+status:
+  project:
+    default:
+      target: 90
\ No newline at end of file

From 8194afc2cf7ca03bf843fd070b7a9edc02845ece Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Sun, 30 Jun 2019 14:22:18 +0300
Subject: [PATCH 36/38] moved codecov to project root

---
 src/.codecov.yaml => .codecov.yaml | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename src/.codecov.yaml => .codecov.yaml (100%)

diff --git a/src/.codecov.yaml b/.codecov.yaml
similarity index 100%
rename from src/.codecov.yaml
rename to .codecov.yaml

From 032c054a635503536f688fa99b62f998240bb981 Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Mon, 1 Jul 2019 11:33:04 +0300
Subject: [PATCH 37/38] + changelog

---
 CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100644 CHANGELOG.md

diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 00000000..0cdbec0f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,40 @@
+# Changelog
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [0.10.0] - 2019-06-20
+### Added
+- Async Post DSL by [@Evgeny](https://github.com/DeviantBadge) [issue #86](https://github.com/rybalkinsd/kohttp/issues/86).
+- Async Head DSL by [@Evgeny](https://github.com/DeviantBadge).
+- Async Put DSL by [@Evgeny](https://github.com/DeviantBadge).
+- Async Patch DSL by [@Evgeny](https://github.com/DeviantBadge).
+- Async Delete DSL by [@Evgeny](https://github.com/DeviantBadge).
+- Async Upload DSL by [@Evgeny](https://github.com/DeviantBadge) [issue #87](https://github.com/rybalkinsd/kohttp/issues/87).
+- Async Upload File extensions by [@Evgeny](https://github.com/DeviantBadge).
+- Async Upload Url extensions by [@Evgeny](https://github.com/DeviantBadge).
+- Default `Dispatcher` configuration in `okhttp.yaml`
+- CHANGELOG.md
+
+### Changed
+- Migrated to `kotlin` 1.3.40
+- Migrated to `okhttp` 3.12.2 [issue #81](https://github.com/rybalkinsd/kohttp/issues/81)
+- Migrated to `kotlinx-coroutines-core` 1.2.1
+- `Boolean` and Nullable types support in [Json builder](https://github.com/rybalkinsd/kohttp/blob/master/src/main/kotlin/io/github/rybalkinsd/kohttp/util/json.kt) 
+[issue #113](https://github.com/rybalkinsd/kohttp/issues/113)
+- `url()` and `param { }` joint usage by [@dtropanets](https://github.com/dtropanets) [issue #94](https://github.com/rybalkinsd/kohttp/issues/94)
+- Allowed nullable types in request `param { }` builder [issue #118](https://github.com/rybalkinsd/kohttp/issues/118),
+[PR](https://github.com/rybalkinsd/kohttp/pull/117)
+- Changed `maxRequests` and `maxRequestsPerHost` for default http client
+- Async methods naming (Deprecated `asyncHttpGet`)
+- Relaxed coverage [issue #56](https://github.com/rybalkinsd/kohttp/issues/56)
+ 
+
+### Removed
+ - pass
+ 
+### Big thanks 
+ @deviantBadge, @dtropanets, @gokulchandra for your contribution   

From 9252e96cff69b11f1859c4306ba6c0d2639215fd Mon Sep 17 00:00:00 2001
From: rybalkinsd 
Date: Mon, 1 Jul 2019 11:45:05 +0300
Subject: [PATCH 38/38] version 0.10.0

---
 build.gradle.kts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build.gradle.kts b/build.gradle.kts
index ba25eeb3..d59b5202 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -11,7 +11,7 @@ plugins {
 }
 
 group = "io.github.rybalkinsd"
-version = "0.10.0-SNAPSHOT"
+version = "0.10.0"
 
 repositories {
     mavenCentral()