Kotlin DSL http client
gradle kotlin DSL:
implementation(group = "io.github.rybalkinsd", name = "kohttp", version = "0.10.0")gradle groovy DSL:
implementation 'io.github.rybalkinsd:kohttp:0.10.0'maven:
<dependency>
<groupId>io.github.rybalkinsd</groupId>
<artifactId>kohttp</artifactId>
<version>0.10.0</version>
</dependency>val response: Response = "https://google.com/search?q=iphone".httpGet()val response: Response = httpGet {
host = "google.com"
path = "/search"
param {
"q" to "iphone"
"safe" to "off"
}
}or
val response: Response = httpGet {
url("https://google.com/search")
param {
"q" to "iphone"
"safe" to "off"
}
}
val response: Response = httpGet {
host = "github.com"
path = "/search"
header {
"username" to "rybalkinsd"
"security-policy" to json {
"base-uri" to "none"
"expect-ct" to json {
"max-age" to 2592000
"report-uri" to "foo.com/bar"
}
"script-src" to listOf("github.com", "github.io")
}
cookie {
"user_session" to "toFycNV"
"expires" to "Fri, 21 Dec 2018 09:29:55 -0000"
}
}
param { ... }
}form body has a application/x-www-form-urlencoded content type
val response: Response = httpPost {
host = "postman-echo.com"
path = "/post"
param { ... }
header { ... }
body {
form { // Resulting form will not contain ' ', '\t', '\n'
"login" to "user" // login=user&
"email" to "john.doe@gmail.com" // email=john.doe@gmail.com
}
}
// or
body {
form("login=user&email=john.doe@gmail.com")
}
}json body has a application/json content type
val response: Response = httpPost {
host = "postman-echo.com"
path = "/post"
param { ... }
header { ... }
body { // Resulting json will not contain ' ', '\t', '\n'
json { // {
"login" to "user" // "login": "user",
"email" to "john.doe@gmail.com" // "email": "john.doe@gmail.com"
} // }
}
// or
body {
json("""{"login":"user","email":"john.doe@gmail.com"}""")
}
}In addition to form or json body content types it is possible to declare a custom content type.
body DSL support three data sources: file(), bytes() and string()
httpPost {
body("application/json") {
string("""{"login":"user","email":"john.doe@gmail.com"}""")
}
}val imageFile = File(getResource("/cat.gif").toURI())
httpPost {
body(type = "image/gif") {
file(imageFile)
}
}httpPost {
body { // content type is optional, null by default
bytes("string of bytes".toByteArray())
}
}Content type is set according to the following priority levels (higher is prioritized)
- Form or Json in body :
kotlin ... body() { json { ... } } ... - Custom body type :
kotlin ... body(myContentType) { ... } ... - Header :
kotlin ... header { "Content-type" to myContentType } ...
val response = httpPost {
url("http://postman-echo.com/post")
multipartBody {
+form("cat", File(this.javaClass.getResource("/cat.gif").toURI()))
+form("dog", File("/mydog.img"))
}
}You can use same syntax as in GET
val response = httpHead { }You can use same syntax as in POST
val response = httpPut { }You can use same syntax as in POST
val response = httpPatch { }You can use same syntax as in POST
val response = httpDelete { }You can upload file by URI or File . Upload DSL can include headers and params.
val fileUri = this.javaClass.getResource("/cat.gif").toURI()
val response = upload {
url("http://postman-echo.com/post")
file(fileUri)
headers {
...
cookies {...}
}
params {...}
}val file = File(this.javaClass.getResource("/cat.gif").toURI())
val response = file.upload( string or url )val fileUri = this.javaClass.getResource("/cat.gif").toURI()
val response = fileUri.upload( string or url )This function starts a new coroutine with Unconfined dispatcher.
val response: Deferred<Response> = "https://google.com/search?q=iphone".httpGetAsync()You can use same syntax as in GET
val response: Deferred<Response> = httpGetAsync { }You can use same syntax as in POST
val response: Deferred<Response> = httpPostAsync { }You can use same syntax as in GET
val response: Deferred<Response> = httpHeadAsync { }You can use same syntax as in POST
val response: Deferred<Response> = httpPutAsync { }You can use same syntax as in POST
val response: Deferred<Response> = httpPatchAsync { }You can use same syntax as in POST
val response: Deferred<Response> = httpDeleteAsync { }Kohttp methods return okhttp3.Response which is AutoClosable
It's strictly recommended to access it with use to prevent resource leakage.
val response = httpGet { ... }
reponse.use {
...
}Response body can be retrieved as a JSON, String or InputStream using provided extension functions on Response.
val response = httpGet { ... }
val dataAsJson: JsonNode = response.asJson()
val dataAsString: String? = response.asString()
val dataAsStream: InputStream? = response.asStream()
Kohttp provides a DSL to add interceptors. Custom Interceptors can be defined by implementing the okhttp3.Interceptors. Interceptors are added by forking the defaultHttpClient.
val forkedClient = defaultHttpClient.fork {
interceptors {
+interceptor1
+interceptor2
}
}-
Logging Interceptor: A Request Logging Interceptor.
Parameters:
log: (String) -> Unit = ::println: function as a parameter to consume the log message. It defaults toprintln. Logs Request body when present.
Usage:
val client = defaultHttpClient.fork { interceptors { +LoggingInterceptor() } }
Sample Output:
[2019-01-28T04:17:42.885Z] GET 200 - 1743ms https://postman-echo.com/get -
Retry Interceptor: Provides a configurable method to retry on specific errors.
Parameters:
failureThreshold: Int: Number of attempts to get response with. Defaults to3.invocationTimeout: Long: timeout (millisecond) before retry. Defaults to0ratio: Int: ratio for exponential increase of invocation timeout. Defaults to1errorStatuses: List<Int>: HTTP status codes to be retried on. Defaults to listOf(503, 504)
Usage:
```kotlin val client = defaultHttpClient.fork { interceptors { ... +RetryInterceptor() ... } } ``` -
Signing Interceptor: Enables signing of query parameters. Allowing creation of presigned URLs.
Parameters:
parameterName: String: The name of the parameter with signed keysigner: HttpUrl.() -> String: Function withokhttp3.HttpUrlas a receiver to sign the request parameter
Usage:
val client = defaultHttpClient.fork { interceptors { +SigningInterceptor("key") { val query = (query() ?: "").toByteArray() urlEncoder.encodeToString(md5.digest(query)) } } }
Kohttp provides a defaultClientPool to have a single endpoint for your http request.
It is possible to customize defaultClientPool by setting kohttp.yaml in resource directory of your project.
You can check default values in io.github.rybalkinsd.kohttp.configuration.Config.kt
All time values are in Milliseconds
client:
connectTimeout: 5000
readTimeout: 10000
writeTimeout: 10000
followRedirects: true
followSslRedirects: true
connectionPool:
maxIdleConnections: 42
keepAliveDuration: 10000Forked client uses exactly the same connection pool and dispatcher. However, it will custom parameters like custom timeouts, additional interceptors or others.
In this example below patientClient will share ConnectionPool with defaultHttpClient,
however patientClient requests will have custom read timeout.
val patientClient = defaultHttpClient.fork {
readTimeout = 100_000
}If defaultClientPool or forked client does not suit you for some reason, it is possible to create your own one.
// a new client with custom dispatcher, connection pool and ping interval
val customClient = client {
dispatcher = ...
connectionPool = ConnectionPool( ... )
pingInterval = 1_000
}Instead of .use { ... it.body?.string() ... } it is now possible to read response body as string.
And also to map Headers to listOf<Header> to operate them easily.
val response: EagerResponse = "https://google.com/search?q=iphone".httpGet().eager()
// iterating over headers
response.headers.forEach { ... }
// manipulating body
response.body?.let { ... }
val response: EagerResponse = httpGet { }.eager()
// iterating over headers
response.headers.forEach { ... }
// manipulating body
response.body?.let { ... }