HttpClient.kt
- package com.hexagonkt.http.client
- import com.hexagonkt.http.handlers.HttpContext
- import com.hexagonkt.http.handlers.HttpHandler
- import com.hexagonkt.http.handlers.OnHandler
- import com.hexagonkt.http.handlers.path
- import com.hexagonkt.http.model.HttpRequest
- import com.hexagonkt.http.model.HttpResponsePort
- import com.hexagonkt.http.model.*
- import com.hexagonkt.http.model.HttpMethod.*
- import com.hexagonkt.http.model.ws.WsSession
- import java.io.Closeable
- import java.util.concurrent.Flow.Publisher
- /**
- * Client to use other REST services.
- */
- class HttpClient(
- private val adapter: HttpClientPort,
- val settings: HttpClientSettings = HttpClientSettings(),
- val handler: HttpHandler? = null,
- ) : Closeable {
- private val rootHandler: HttpHandler? =
- handler?.let {
- val sendHandler = OnHandler("*") { send(adapter.send(request)) }
- path("*", listOf(it, sendHandler))
- }
- private val noRequestSettings =
- settings.contentType == null
- && settings.authorization == null
- && settings.accept.isEmpty()
- && settings.headers.isEmpty()
- var cookies: List<Cookie> = emptyList()
- override fun close() {
- stop()
- }
- fun cookiesMap(): Map<String, Cookie> =
- cookies.associateBy { it.name }
- fun start() {
- check(!started()) { "HTTP client is already started" }
- adapter.startUp(this)
- }
- fun stop() {
- check(started()) { "HTTP client *MUST BE STARTED* before shut-down" }
- adapter.shutDown()
- }
- fun started(): Boolean =
- adapter.started()
- /**
- * Synchronous execution.
- */
- fun send(request: HttpRequest, attributes: Map<String, Any> = emptyMap()): HttpResponsePort =
- if (!started())
- error("HTTP client *MUST BE STARTED* before sending requests")
- else
- rootHandler
- ?.process(request.setUp(), attributes)
- ?.let {
- if (it.exception != null) throw it.exception as Exception
- else it.response
- }
- ?: adapter.send(request.setUp())
- private fun HttpRequest.setUp(): HttpRequest {
- return if (noRequestSettings)
- this
- else
- copy(
- contentType = contentType ?: settings.contentType,
- accept = accept.ifEmpty(settings::accept),
- headers = settings.headers + headers,
- authorization = authorization ?: settings.authorization,
- )
- }
- fun sse(request: HttpRequest): Publisher<ServerEvent> =
- if (!started()) error("HTTP client *MUST BE STARTED* before sending requests")
- else adapter.sse(request)
- fun sse(path: String): Publisher<ServerEvent> =
- sse(HttpRequest(path = path))
- fun ws(
- path: String,
- onConnect: WsSession.() -> Unit = {},
- onBinary: WsSession.(data: ByteArray) -> Unit = {},
- onText: WsSession.(text: String) -> Unit = {},
- onPing: WsSession.(data: ByteArray) -> Unit = {},
- onPong: WsSession.(data: ByteArray) -> Unit = {},
- onClose: WsSession.(status: Int, reason: String) -> Unit = { _, _ -> },
- ): WsSession =
- if (!started()) error("HTTP client *MUST BE STARTED* before connecting to WS")
- else adapter.ws(path, onConnect, onBinary, onText, onPing, onPong, onClose)
- fun request (block: HttpClient.() -> Unit) {
- if (!started())
- start()
- use(block)
- }
- fun get(
- path: String = "",
- headers: Headers = Headers(),
- body: Any? = null,
- contentType: ContentType? = settings.contentType,
- accept: List<ContentType> = settings.accept,
- ): HttpResponsePort =
- send(
- HttpRequest(
- method = GET,
- path = path,
- body = body ?: "",
- headers = headers,
- contentType = contentType,
- accept = accept,
- )
- )
- fun head(path: String = "", headers: Headers = Headers()): HttpResponsePort =
- send(HttpRequest(HEAD, path = path, body = ByteArray(0), headers = headers))
- fun post(
- path: String = "",
- body: Any? = null,
- contentType: ContentType? = settings.contentType,
- accept: List<ContentType> = settings.accept,
- ): HttpResponsePort =
- send(
- HttpRequest(
- method = POST,
- path = path,
- body = body ?: "",
- contentType = contentType,
- accept = accept,
- )
- )
- fun put(
- path: String = "",
- body: Any? = null,
- contentType: ContentType? = settings.contentType,
- accept: List<ContentType> = settings.accept,
- ): HttpResponsePort =
- send(
- HttpRequest(
- method = PUT,
- path = path,
- body = body ?: "",
- contentType = contentType,
- accept = accept,
- )
- )
- fun delete(
- path: String = "",
- body: Any? = null,
- contentType: ContentType? = settings.contentType,
- accept: List<ContentType> = settings.accept,
- ): HttpResponsePort =
- send(
- HttpRequest(
- method = DELETE,
- path = path,
- body = body ?: "",
- contentType = contentType,
- accept = accept,
- )
- )
- fun trace(
- path: String = "",
- body: Any? = null,
- contentType: ContentType? = settings.contentType,
- accept: List<ContentType> = settings.accept,
- ): HttpResponsePort =
- send(
- HttpRequest(
- method = TRACE,
- path = path,
- body = body ?: "",
- contentType = contentType,
- accept = accept,
- )
- )
- fun options(
- path: String = "",
- body: Any? = null,
- headers: Headers = Headers(),
- contentType: ContentType? = settings.contentType,
- accept: List<ContentType> = settings.accept,
- ): HttpResponsePort =
- send(
- HttpRequest(
- method = OPTIONS,
- path = path,
- body = body ?: "",
- headers = headers,
- contentType = contentType,
- accept = accept,
- )
- )
- fun patch(
- path: String = "",
- body: Any? = null,
- contentType: ContentType? = settings.contentType,
- accept: List<ContentType> = settings.accept,
- ): HttpResponsePort =
- send(
- HttpRequest(
- method = PATCH,
- path = path,
- body = body ?: "",
- contentType = contentType,
- accept = accept,
- )
- )
- private fun HttpHandler.process(
- request: HttpRequestPort, attributes: Map<String, Any>
- ): HttpContext =
- processHttp(
- HttpContext(HttpCall(request = request), handlerPredicate, attributes = attributes)
- )
- }