
org.http4k.client.Java8HttpClient.kt Maven / Gradle / Ivy
package org.http4k.client
import org.http4k.core.Body
import org.http4k.core.HttpHandler
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status
import org.http4k.core.Status.Companion.CLIENT_TIMEOUT
import org.http4k.core.Status.Companion.CONNECTION_REFUSED
import org.http4k.core.Status.Companion.UNKNOWN_HOST
import java.io.ByteArrayInputStream
import java.net.ConnectException
import java.net.HttpURLConnection
import java.net.SocketTimeoutException
import java.net.URL
import java.net.UnknownHostException
import java.nio.ByteBuffer
import java.time.Duration
import java.time.Duration.ZERO
/**
* Use this legacy Java client when you're not yet on Java 11.
*/
object Java8HttpClient {
@JvmStatic
@JvmName("create")
operator fun invoke(): HttpHandler = invoke(ZERO)
@JvmStatic
@JvmName("createWithTimeouts")
operator fun invoke(
readTimeout: Duration = ZERO,
connectionTimeout: Duration = ZERO
): HttpHandler = { request: Request ->
try {
val connection = (URL(request.uri.toString()).openConnection() as HttpURLConnection).apply {
this.readTimeout = readTimeout.toMillis().toInt()
this.connectTimeout = connectionTimeout.toMillis().toInt()
instanceFollowRedirects = false
requestMethod = request.method.name
doOutput = true
doInput = true
request.headers.forEach {
addRequestProperty(it.first, it.second)
}
request.body.apply {
if (this != Body.EMPTY) {
val content = if (stream.available() == 0) payload.array().inputStream() else stream
content.copyTo(outputStream)
}
}
}
val status = Status(connection.responseCode, connection.responseMessage.orEmpty())
val baseResponse = Response(status).body(connection.body(status))
connection.headerFields
.filterKeys { it != null } // because response status line comes as a header with null key (*facepalm*)
.map { header -> header.value.map { header.key to it } }
.flatten()
.fold(baseResponse) { acc, next -> acc.header(next.first, next.second) }
} catch (e: UnknownHostException) {
Response(UNKNOWN_HOST.toClientStatus(e))
} catch (e: ConnectException) {
Response(CONNECTION_REFUSED.toClientStatus(e))
} catch (e: SocketTimeoutException) {
Response(CLIENT_TIMEOUT.toClientStatus(e))
}
}
// Because HttpURLConnection closes the stream if a new request is made, we are forced to consume it straight away
private fun HttpURLConnection.body(status: Status) =
Body(resolveStream(status).readBytes().let { ByteBuffer.wrap(it) })
private fun HttpURLConnection.resolveStream(status: Status) =
when {
status.serverError || status.clientError -> errorStream
else -> inputStream
} ?: EMPTY_STREAM
private val EMPTY_STREAM = ByteArrayInputStream(ByteArray(0))
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy