Kotlin dsl for http
Kotlin DSL http client
Download
gradle kotlin DSL:
implementation(group = "io.github.rybalkinsd", name = "kohttp", version = "0.8.0")
gradle groovy DSL:
implementation 'io.github.rybalkinsd:kohttp:0.8.0'
maven:
<dependency>
<groupId>io.github.rybalkinsd</groupId>
<artifactId>kohttp</artifactId>
<version>0.8.0</version>
</dependency>
Usage
Sync http calls
GET
String.httpGet()
extension
val response: Response = "https://google.com/search?q=iphone".httpGet()
GET with request parameters
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"
}
}
GET with header and cookies
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 { ... }
}
POST
form
body.
POST with 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 "[email protected]" // [email protected]
}
}
// or
body {
form("login=user&[email protected]")
}
}
json
body.
POST with 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 "[email protected]" // "email": "[email protected]"
} // }
}
// or
body {
json("""{"login":"user","email":"[email protected]"}""")
}
}
POST with various content type
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":"[email protected]"}""")
}
}
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())
}
}
POST with multipart body
val response = httpPost {
url("http://postman-echo.com/post")
multipartBody {
+form("cat", File(this.javaClass.getResource("/cat.gif").toURI()))
+form("dog", File("/mydog.img"))
}
}
HEAD
You can use same syntax as in GET
val response = httpHead { }
PUT
You can use same syntax as in POST
val response = httpPut { }
PATCH
You can use same syntax as in POST
val response = httpPatch { }
DELETE
You can use same syntax as in POST
val response = httpDelete { }
Upload files
Upload DSL
You can upload file by URI
or File
val fileUri = this.javaClass.getResource("/cat.gif").toURI()
val response = upload {
url("http://postman-echo.com/post")
file(fileUri)
}
File
extensions
Upload val file = File(this.javaClass.getResource("/cat.gif").toURI())
val response = file.upload( string or url )
URI
extensions
Upload val fileUri = this.javaClass.getResource("/cat.gif").toURI()
val response = fileUri.upload( string or url )
Async http calls
async GET
String.asyncHttpGet()
extension function
This function starts a new coroutine with Unconfined dispatcher.
val response: Deferred<Response> = "https://google.com/search?q=iphone".asyncHttpGet()
asyncHttpGet
call
val response: Deferred<Response> = asyncHttpGet {
host = "google.com"
path = "/search"
header { ... }
param { ... }
}
Response usage
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 {
...
}
Interceptors
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
}
}
Built-in Interceptors
-
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
-
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.HttpUrl
as a receiver to sign the request parameter
Usage:
val client = defaultHttpClient.fork { interceptors { +SigningInterceptor("key") { val query = (query() ?: "").toByteArray() urlEncoder.encodeToString(md5.digest(query)) } } }
Customization
defaultClientPool
customization
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: 10000
HttpClient
for specific tasks
Fork Forked client uses exactly the same connection pool and dispatcher. However, it will custom parameters like custom timeout
s, additional interceptor
s 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
}
Run HTTP methods on custom client
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
}
Experimental
Eager response
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 { ... }