A Ktor server plugin that provides HTTP response compression and request decompression capabilities with support for gzip, deflate, and identity encoding algorithms.
—
The Compression plugin automatically decompresses incoming request bodies that have been compressed with supported algorithms. It handles Content-Encoding headers and tracks which decoders were applied.
When the plugin receives a request with a Content-Encoding header:
Content-Encoding headerval ApplicationRequest.appliedDecoders: List<String>Access the list of decoder names that were used to decompress the request body:
routing {
post("/upload") {
val decoders = call.request.appliedDecoders
if (decoders.isNotEmpty()) {
println("Request was decompressed using: ${decoders.joinToString(", ")}")
}
val body = call.receiveText()
// Process decompressed body...
call.respond("Received")
}
}The plugin uses the same ContentEncoder implementations for both compression and decompression:
When all encodings can be decompressed, the plugin:
Content-Encoding header entirelyContent-Length headerTransfer-Encoding: chunked if neededRequest headers before decompression:
Content-Encoding: gzip
Content-Length: 1024
Request headers after decompression:
Transfer-Encoding: chunked
(Content-Encoding and Content-Length removed)When only some encodings can be decompressed, the plugin:
Content-Encoding header with remaining encodingsRequest headers before decompression:
Content-Encoding: gzip, custom-encoding
Request headers after decompression:
Content-Encoding: custom-encoding
(gzip removed, custom-encoding remains)Control decompression behavior via the plugin's operation mode:
install(Compression) {
mode = CompressionConfig.Mode.DecompressRequest // Only decompress requests
mode = CompressionConfig.Mode.CompressResponse // No decompression
mode = CompressionConfig.Mode.All // Both compress and decompress (default)
}Disable decompression for specific requests:
fun ApplicationCall.suppressDecompression()
val ApplicationCall.isDecompressionSuppressed: Booleanrouting {
post("/raw-upload") {
// Keep request body compressed
call.suppressDecompression()
// Receive raw compressed bytes
val compressedBody = call.receive<ByteArray>()
// Handle compressed data directly...
}
post("/check-suppressed") {
if (call.isDecompressionSuppressed) {
// Handle suppressed case
} else {
// Normal decompression will occur
val decompressedText = call.receiveText()
}
}
}The plugin handles various decompression scenarios gracefully:
Requests without Content-Encoding headers are processed normally without decompression attempts.
When encountering unknown encodings:
Content-Encoding headerDecompression errors are propagated as exceptions that can be handled by error handling mechanisms.
install(Compression) // Enables decompression by default
routing {
post("/api/data") {
// Automatically decompressed if client sent compressed request
val data = call.receive<MyDataClass>()
call.respond("Processed ${data}")
}
}routing {
post("/upload") {
val wasCompressed = call.request.appliedDecoders.isNotEmpty()
val contentLength = call.request.headers[HttpHeaders.ContentLength]?.toLongOrNull()
when {
wasCompressed -> {
logger.info("Received compressed upload (${call.request.appliedDecoders})")
}
contentLength != null && contentLength > 1024 * 1024 -> {
logger.warn("Large uncompressed upload: $contentLength bytes")
}
}
val content = call.receiveText()
// Process content...
}
}install(Compression) {
// Add custom decoder alongside built-in ones
encoder(MyCustomEncoder) {
// Custom encoder handles both compression and decompression
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-server-compression-jvm