A multiplatform asynchronous framework for creating microservices, web applications, and HTTP clients written in Kotlin from the ground up
—
Response processing, body consumption, and content type handling for working with HTTP responses and consuming response data.
Abstract base class representing an HTTP response with status, headers, and content.
/**
* Abstract HTTP response representing the server's response to a request
*/
abstract class HttpResponse : HttpMessage, CoroutineScope {
/** The call that generated this response */
abstract val call: HttpClientCall
/** HTTP status code and description */
abstract val status: HttpStatusCode
/** HTTP protocol version */
abstract val version: HttpProtocolVersion
/** Timestamp when the request was sent */
abstract val requestTime: GMTDate
/** Timestamp when the response was received */
abstract val responseTime: GMTDate
/** Raw response content as a byte channel */
abstract val rawContent: ByteReadChannel
}Extension functions for consuming response body content in various formats.
/**
* Get response body as text with optional charset fallback
* @param fallbackCharset Charset to use if not specified in response
* @return Response body as string
*/
suspend fun HttpResponse.bodyAsText(
fallbackCharset: Charset = Charsets.UTF_8
): String
/**
* Get response body as raw byte channel
* @return ByteReadChannel for streaming response content
*/
suspend fun HttpResponse.bodyAsChannel(): ByteReadChannel
/**
* Get response body as byte array
* @return Response body as ByteArray
*/
suspend fun HttpResponse.bodyAsBytes(): ByteArray
/**
* Get response body as typed object using content negotiation
* @return Deserialized response body of type T
*/
suspend inline fun <reified T> HttpResponse.body(): T
/**
* Get response body as typed object with explicit type information
* @param typeInfo Type information for deserialization
* @return Deserialized response body
*/
suspend fun <T> HttpResponse.body(typeInfo: TypeInfo): TUsage Examples:
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
val client = HttpClient()
// Get response as text
val response = client.get("https://api.example.com/users")
val textContent = response.bodyAsText()
// Get response as bytes
val imageResponse = client.get("https://example.com/image.png")
val imageBytes = imageResponse.bodyAsBytes()
// Get response as typed object (requires serialization plugin)
val usersResponse = client.get("https://api.example.com/users")
val users: List<User> = usersResponse.body()
// Stream response content
val streamResponse = client.get("https://example.com/large-file")
val channel = streamResponse.bodyAsChannel()
while (!channel.isClosedForRead) {
val data = channel.readBuffer(8192)
// Process data chunk
}Deferred HTTP request execution that allows for custom response handling.
/**
* Represents a prepared HTTP request that can be executed multiple times
*/
class HttpStatement(
private val builder: HttpRequestBuilder,
internal val client: HttpClient
) {
/**
* Execute the request with custom response handling
* @param block Response handling block
* @return Result of the response handling block
*/
suspend fun <T> execute(
block: suspend (response: HttpResponse) -> T
): T
/**
* Execute the request and return the response
* @return HttpResponse from the server
*/
suspend fun execute(): HttpResponse
/**
* Execute the request and get typed response body
* @return Deserialized response body of type T
*/
suspend inline fun <reified T> body(): T
/**
* Execute the request and get typed response body with custom handling
* @param block Custom handling block for the typed response
* @return Result of the handling block
*/
suspend inline fun <reified T, R> body(
crossinline block: suspend (response: T) -> R
): R
}Usage Examples:
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
val client = HttpClient()
// Prepare statement for later execution
val statement = client.prepareGet("https://api.example.com/users")
// Execute with custom response handling
val result = statement.execute { response ->
when {
response.status.isSuccess() -> response.bodyAsText()
response.status.value == 404 -> "Not found"
else -> "Error: ${response.status}"
}
}
// Execute and get typed body
val users: List<User> = statement.body()
// Execute with typed body and custom handling
val userCount = statement.body<List<User>> { users ->
users.size
}Represents a complete HTTP request-response cycle with access to both request and response.
/**
* Represents a complete HTTP call including request and response
*/
open class HttpClientCall(
val client: HttpClient
) : CoroutineScope {
/** Attributes for storing call metadata */
val attributes: Attributes
/** The request that was sent */
lateinit var request: HttpRequest
/** The response that was received */
lateinit var response: HttpResponse
/**
* Get response body with nullable handling
* @param info Type information for deserialization
* @return Deserialized body or null
*/
suspend fun bodyNullable(info: TypeInfo): Any?
/**
* Get response body with type information
* @param info Type information for deserialization
* @return Deserialized body
*/
suspend fun body(info: TypeInfo): Any
}Extension functions for getting typed response bodies from HttpClientCall instances.
/**
* Get typed response body from call
* @return Deserialized response body of type T
*/
suspend inline fun <reified T> HttpClientCall.body(): T
/**
* Get typed response body from response
* @return Deserialized response body of type T
*/
suspend inline fun <reified T> HttpResponse.body(): TUsage Examples:
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
val client = HttpClient()
val response = client.get("https://api.example.com/user/123")
// Access the call
val call = response.call
// Get typed body from call
val user: User = call.body()
// Access request information
println("Request URL: ${call.request.url}")
println("Response Status: ${call.response.status}")Utility functions for working with response content types and character sets.
/**
* Get the charset from response Content-Type header
* @return Charset if specified, null otherwise
*/
fun HttpResponse.charset(): Charset?
/**
* Get the content type from response headers
* @return ContentType if specified, null otherwise
*/
fun HttpMessage.contentType(): ContentType?Usage Examples:
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
val response = client.get("https://api.example.com/data")
// Check content type
val contentType = response.contentType()
when {
contentType?.match(ContentType.Application.Json) == true -> {
val jsonData = response.bodyAsText()
// Process JSON
}
contentType?.match(ContentType.Text.Plain) == true -> {
val textData = response.bodyAsText()
// Process plain text
}
}
// Use specific charset if available
val charset = response.charset() ?: Charsets.UTF_8
val text = response.bodyAsText(charset)Response-related exceptions that may be thrown during response processing:
/**
* Thrown when attempting to receive response body multiple times
*/
class DoubleReceiveException(call: HttpClientCall) : IllegalStateException()
/**
* Thrown when no type transformer is found for response deserialization
*/
class NoTransformationFoundException(
response: HttpResponse,
from: KClass<*>,
to: KClass<*>
) : UnsupportedOperationException()
/**
* Exception in response receive pipeline
*/
class ReceivePipelineException(
request: HttpClientCall,
info: TypeInfo,
override val cause: Throwable
) : IllegalStateException()
/**
* Base class for HTTP response exceptions
*/
open class ResponseException(
response: HttpResponse,
cachedResponseText: String
) : IllegalStateException()
/**
* Exception for HTTP redirect responses
*/
class RedirectResponseException(
response: HttpResponse,
cachedResponseText: String
) : ResponseException(response, cachedResponseText)
/**
* Exception for HTTP client error responses (4xx)
*/
class ClientRequestException(
response: HttpResponse,
cachedResponseText: String
) : ResponseException(response, cachedResponseText)
/**
* Exception for HTTP server error responses (5xx)
*/
class ServerResponseException(
response: HttpResponse,
cachedResponseText: String
) : ResponseException(response, cachedResponseText)Usage Examples:
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.client.plugins.*
val client = HttpClient {
// Configure to not throw on non-2xx responses
expectSuccess = false
}
try {
val response = client.get("https://api.example.com/might-fail")
val content = response.bodyAsText()
// Try to read body again - throws DoubleReceiveException
val duplicateContent = response.bodyAsText() // ERROR!
} catch (e: ClientRequestException) {
// Handle 4xx responses
println("Client error: ${e.response.status}")
} catch (e: ServerResponseException) {
// Handle 5xx responses
println("Server error: ${e.response.status}")
} catch (e: DoubleReceiveException) {
// Handle duplicate receive attempts
println("Response body already consumed")
}Response handling related types:
/**
* HTTP status code with value and description
*/
data class HttpStatusCode(val value: Int, val description: String) {
companion object {
val Continue = HttpStatusCode(100, "Continue")
val OK = HttpStatusCode(200, "OK")
val Created = HttpStatusCode(201, "Created")
val NoContent = HttpStatusCode(204, "No Content")
val NotModified = HttpStatusCode(304, "Not Modified")
val BadRequest = HttpStatusCode(400, "Bad Request")
val Unauthorized = HttpStatusCode(401, "Unauthorized")
val Forbidden = HttpStatusCode(403, "Forbidden")
val NotFound = HttpStatusCode(404, "Not Found")
val InternalServerError = HttpStatusCode(500, "Internal Server Error")
val BadGateway = HttpStatusCode(502, "Bad Gateway")
}
fun isSuccess(): Boolean = value in 200..299
fun isInformational(): Boolean = value in 100..199
fun isRedirect(): Boolean = value in 300..399
fun isClientError(): Boolean = value in 400..499
fun isServerError(): Boolean = value in 500..599
}
/**
* HTTP protocol version
*/
data class HttpProtocolVersion(
val name: String,
val major: Int,
val minor: Int
) {
companion object {
val HTTP_1_0 = HttpProtocolVersion("HTTP", 1, 0)
val HTTP_1_1 = HttpProtocolVersion("HTTP", 1, 1)
val HTTP_2_0 = HttpProtocolVersion("HTTP", 2, 0)
val SPDY_3 = HttpProtocolVersion("SPDY", 3, 0)
val QUIC = HttpProtocolVersion("QUIC", 1, 0)
}
}
/**
* Type information for response deserialization
*/
interface TypeInfo {
val type: KClass<*>
val reifiedType: KType
val kotlinType: KType?
}
/**
* GMT date representation
*/
data class GMTDate(val timestamp: Long) {
companion object {
fun now(): GMTDate
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor