-
Notifications
You must be signed in to change notification settings - Fork 9
Description
Violation
File: frameworks/ktor/src/main/kotlin/com/httparena/Application.kt
Endpoint: /compression
What it does
The gzipCompress() function uses GZIPOutputStream(bos) with the default constructor:
fun gzipCompress(data: ByteArray): ByteArray {
val bos = ByteArrayOutputStream(data.size / 4)
GZIPOutputStream(bos).use { it.write(data) }
return bos.toByteArray()
}Java's GZIPOutputStream default constructor uses Deflater.DEFAULT_COMPRESSION which maps to zlib level 6.
What the spec requires
MUST use gzip level 1 (fastest)
Impact
Level 6 produces smaller output but is significantly slower than level 1. This gives ktor worse throughput on the compression benchmark than it would have at level 1, but more importantly it means the benchmark isn't measuring the same thing across frameworks.
Suggested fix
Use the GZIPOutputStream constructor that accepts a buffer size and set the deflater level explicitly:
fun gzipCompress(data: ByteArray): ByteArray {
val bos = ByteArrayOutputStream(data.size / 4)
val deflater = java.util.zip.Deflater(java.util.zip.Deflater.BEST_SPEED, true)
GZIPOutputStream(bos, 8192).use { gos ->
// Access the internal deflater via reflection or use DeflaterOutputStream directly
it.write(data)
}
return bos.toByteArray()
}Or more cleanly, use DeflaterOutputStream with gzip wrapping:
fun gzipCompress(data: ByteArray): ByteArray {
val bos = ByteArrayOutputStream(data.size / 4)
val deflater = java.util.zip.Deflater(java.util.zip.Deflater.BEST_SPEED, true)
val header = byteArrayOf(0x1f.toByte(), 0x8b.toByte(), 8, 0, 0, 0, 0, 0, 0, 0)
bos.write(header)
java.util.zip.DeflaterOutputStream(bos, deflater).use { it.write(data) }
// Write CRC32 and size trailer for valid gzip
val crc = java.util.zip.CRC32()
crc.update(data)
// ... write trailer bytes
return bos.toByteArray()
}The simplest fix is probably to use GZIPOutputStream with the Deflater level set:
import java.util.zip.Deflater
fun gzipCompress(data: ByteArray): ByteArray {
val bos = ByteArrayOutputStream(data.size / 4)
object : GZIPOutputStream(bos) {
init { def.setLevel(Deflater.BEST_SPEED) }
}.use { it.write(data) }
return bos.toByteArray()
}Additional note
The /db endpoint uses a single shared Connection with synchronized(conn) rather than thread-local or per-worker connections as the spec requires. This serializes all concurrent DB queries through one connection, which doesn't give an unfair advantage (it hurts performance) but is technically non-compliant with the spec.