io.tekniq.rest.TqRestClient.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tekniq-rest Show documentation
Show all versions of tekniq-rest Show documentation
A framework designed around Kotlin
package io.tekniq.rest
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.*
import javax.net.ssl.*
import kotlin.reflect.KClass
import kotlin.system.measureTimeMillis
open class TqRestClient(val logHandler: RestLogHandler = NoOpRestLogHandler,
val mapper: ObjectMapper = ObjectMapper().registerModule(KotlinModule())
.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL)
.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES),
val allowSelfSigned: Boolean = false,
val ignoreHostnameVerifier: Boolean = false) {
private val ctx = SSLContext.getInstance("SSL").apply {
init(null, arrayOf(SelfSignedTrustManager), SecureRandom())
}
open fun delete(url: String, headers: Map = emptyMap()): TqResponse = request("DELETE", url, headers = headers)
open fun get(url: String, headers: Map = emptyMap()): TqResponse = request("GET", url, headers = headers)
open fun put(url: String, json: Any?, headers: Map = emptyMap()): TqResponse = request("PUT", url, json, headers)
open fun post(url: String, json: Any?, headers: Map = emptyMap()): TqResponse = request("POST", url, json, headers)
open fun delete(url: String, headers: Map = emptyMap(), action: TqResponse.() -> T): T? {
val response = delete(url, headers)
return action(response)
}
open fun get(url: String, headers: Map = emptyMap(), action: TqResponse.() -> T): T? {
val response = get(url, headers)
return action(response)
}
open fun put(url: String, json: Any?, headers: Map = emptyMap(), action: TqResponse.() -> T): T? {
val response = put(url, json, headers)
return action(response)
}
open fun post(url: String, json: Any?, headers: Map = emptyMap(), action: TqResponse.() -> T): T? {
val response = post(url, json, headers)
return action(response)
}
open fun transform(json: Any?) = when (json) {
is String, is Number, is Boolean -> json.toString()
else -> mapper.writeValueAsString(json)
}
protected fun request(method: String, url: String, json: Any? = null, headers: Map = emptyMap()): TqResponse {
val payload: String = transform(json)
var response: TqResponse? = null
val duration = measureTimeMillis {
val conn = (URL(url).openConnection() as HttpURLConnection).apply {
if (this is HttpsURLConnection) {
if (allowSelfSigned) {
sslSocketFactory = ctx.socketFactory
}
if (ignoreHostnameVerifier) {
hostnameVerifier = HostnameVerifier { _, _ -> true }
}
}
requestMethod = method
setRequestProperty("Content-Type", "application/json")
headers.forEach {
setRequestProperty(it.key, it.value.toString())
}
if (json != null) {
setRequestProperty("Content-Length", payload.length.toString())
doOutput = true
outputStream.write(payload.toByteArray())
}
}
response = try {
val responseCode = conn.responseCode
val stream = conn.errorStream ?: conn.inputStream
TqResponse(responseCode, stream.bufferedReader().use { it.readText() }, conn.headerFields, mapper)
} catch (e: IOException) {
TqResponse(-1, e.message ?: "", conn.headerFields, mapper)
}
}
logHandler.onRestLog(RestLog(method, url, duration = duration, request = payload, status = response!!.status, response = response!!.body))
return response ?: TqResponse(-1, "", emptyMap(), mapper)
}
private object SelfSignedTrustManager : X509TrustManager {
override fun checkServerTrusted(p0: Array?, p1: String?) = Unit
override fun getAcceptedIssuers(): Array = emptyArray()
override fun checkClientTrusted(p0: Array?, p1: String?) = Unit
}
}
data class TqResponse(val status: Int, val body: String, private val headers: Map, private val mapper: ObjectMapper) {
fun header(key: String): Any? {
val value = headers[key] ?: return null
if (value is Collection<*> && value.size == 1) {
return value.first()
}
return value
}
fun headers(): Map {
return headers.mapValues {
if (it is Array<*> && it.size == 1) {
return@mapValues it[0]!!
}
it
}
}
inline fun jsonAs(): T = jsonAsNullable(T::class)!!
inline fun jsonAsNullable(): T? = jsonAsNullable(T::class)
fun jsonAsNullable(type: KClass): T? = mapper.readValue(body, type.java)
}
data class RestLog(val method: String, val url: String, val ts: Date = Date(), val duration: Long = 0, val request: String? = null, val status: Int = 0, val response: String? = null)
interface RestLogHandler {
fun onRestLog(log: RestLog)
}
private object NoOpRestLogHandler : RestLogHandler {
override fun onRestLog(log: RestLog) {
}
}