0
# Compression Control
1
2
The Compression plugin provides runtime control mechanisms to selectively suppress compression or decompression on a per-request basis, giving you fine-grained control over when these operations occur.
3
4
## Suppression Functions
5
6
### Suppress Response Compression
7
8
```kotlin { .api }
9
fun ApplicationCall.suppressCompression()
10
```
11
12
Prevents the current response from being compressed, regardless of plugin configuration:
13
14
```kotlin
15
routing {
16
get("/no-compression") {
17
call.suppressCompression()
18
call.respondText("This response will never be compressed")
19
}
20
21
get("/conditional") {
22
if (call.request.queryParameters["raw"] == "true") {
23
call.suppressCompression()
24
}
25
call.respond("Conditionally compressed response")
26
}
27
}
28
```
29
30
### Suppress Request Decompression
31
32
```kotlin { .api }
33
fun ApplicationCall.suppressDecompression()
34
```
35
36
Prevents the current request body from being decompressed, allowing access to raw compressed data:
37
38
```kotlin
39
routing {
40
post("/raw-upload") {
41
call.suppressDecompression()
42
43
// Receive compressed bytes directly
44
val compressedData = call.receive<ByteArray>()
45
46
// Process compressed data or decompress manually
47
val originalSize = call.request.headers[HttpHeaders.ContentLength]?.toInt() ?: 0
48
logger.info("Received $originalSize bytes of compressed data")
49
50
call.respond("Processed raw compressed upload")
51
}
52
}
53
```
54
55
## Suppression Status Checking
56
57
### Check Compression Suppression
58
59
```kotlin { .api }
60
val ApplicationCall.isCompressionSuppressed: Boolean
61
```
62
63
Determine if response compression has been suppressed for the current call:
64
65
```kotlin
66
routing {
67
get("/debug-compression") {
68
if (call.isCompressionSuppressed) {
69
call.respond("Compression is suppressed")
70
} else {
71
call.respond("Compression is active")
72
}
73
}
74
}
75
```
76
77
### Check Decompression Suppression
78
79
```kotlin { .api }
80
val ApplicationCall.isDecompressionSuppressed: Boolean
81
```
82
83
Determine if request decompression has been suppressed for the current call:
84
85
```kotlin
86
routing {
87
post("/upload") {
88
val message = if (call.isDecompressionSuppressed) {
89
"Processing raw compressed data"
90
} else {
91
"Processing decompressed data"
92
}
93
94
logger.info(message)
95
val data = call.receiveText()
96
call.respond("Processed: ${data.length} characters")
97
}
98
}
99
```
100
101
## Use Cases
102
103
### Pre-compressed Content
104
105
Serve pre-compressed static files without double compression:
106
107
```kotlin
108
routing {
109
static("/assets") {
110
files("static")
111
// Ktor automatically calls suppressCompression() for pre-compressed files
112
// like .gz, .br variants
113
}
114
115
get("/precompressed/{file}") {
116
val filename = call.parameters["file"]
117
val gzFile = File("compressed/$filename.gz")
118
119
if (gzFile.exists()) {
120
call.suppressCompression()
121
call.response.header(HttpHeaders.ContentEncoding, "gzip")
122
call.respondFile(gzFile)
123
} else {
124
call.respondText("File not found", status = HttpStatusCode.NotFound)
125
}
126
}
127
}
128
```
129
130
### Binary Data Handling
131
132
Handle binary uploads that shouldn't be decompressed:
133
134
```kotlin
135
routing {
136
post("/binary-upload") {
137
val contentType = call.request.contentType()
138
139
if (contentType?.match(ContentType.Application.OctetStream) == true) {
140
// Keep binary data compressed for efficient storage
141
call.suppressDecompression()
142
143
val compressedBytes = call.receive<ByteArray>()
144
// Store compressed data directly
145
database.storeBinary(compressedBytes)
146
} else {
147
// Normal decompression for text/JSON data
148
val content = call.receiveText()
149
processTextContent(content)
150
}
151
152
call.respond("Upload processed")
153
}
154
}
155
```
156
157
### Performance Optimization
158
159
Skip compression for small responses or specific content types:
160
161
```kotlin
162
routing {
163
get("/small-response") {
164
val data = generateSmallResponse()
165
166
if (data.length < 100) {
167
// Skip compression overhead for very small responses
168
call.suppressCompression()
169
}
170
171
call.respondText(data)
172
}
173
174
get("/image-proxy/{id}") {
175
val imageData = imageService.getImage(call.parameters["id"])
176
177
// Images are already compressed
178
call.suppressCompression()
179
call.respondBytes(imageData, ContentType.Image.JPEG)
180
}
181
}
182
```
183
184
### Debug and Development
185
186
Disable compression for easier debugging:
187
188
```kotlin
189
routing {
190
get("/api/{...}") {
191
if (call.request.headers["X-Debug-Mode"] == "true") {
192
call.suppressCompression()
193
}
194
195
val response = apiService.processRequest(call)
196
call.respond(response)
197
}
198
}
199
```
200
201
### Conditional Suppression with Interceptors
202
203
Apply suppression logic globally using interceptors:
204
205
```kotlin
206
install(createApplicationPlugin("CompressionControl") {
207
onCall { call ->
208
// Suppress compression for API documentation endpoints
209
if (call.request.uri.startsWith("/docs/")) {
210
call.suppressCompression()
211
}
212
213
// Suppress decompression for webhook endpoints that need raw data
214
if (call.request.uri.startsWith("/webhooks/")) {
215
call.suppressDecompression()
216
}
217
}
218
})
219
```
220
221
## Implementation Notes
222
223
### Suppression Attributes
224
225
The suppression system uses internal attribute keys:
226
227
```kotlin
228
internal val SuppressionAttribute: AttributeKey<Boolean>
229
internal val DecompressionSuppressionAttribute: AttributeKey<Boolean>
230
```
231
232
These attributes are automatically checked by the compression plugin during request/response processing.
233
234
### Timing Requirements
235
236
Suppression calls must be made before the compression/decompression logic executes:
237
238
- **Compression suppression**: Call before response content is processed
239
- **Decompression suppression**: Call before request body is consumed
240
241
```kotlin
242
routing {
243
post("/example") {
244
// ✅ Correct: suppress before processing
245
call.suppressDecompression()
246
val body = call.receiveText()
247
248
// ❌ Incorrect: too late to suppress compression
249
call.suppressCompression() // This won't work
250
call.respond(body)
251
}
252
}
253
```
254
255
### Interaction with Conditions
256
257
Suppression overrides all configured conditions. Even if conditions would normally allow compression/decompression, suppression takes precedence:
258
259
```kotlin
260
install(Compression) {
261
matchContentType(ContentType.Text.Plain) // Condition allows text compression
262
}
263
264
routing {
265
get("/text") {
266
call.suppressCompression() // Overrides condition - no compression occurs
267
call.respondText("This text won't be compressed", ContentType.Text.Plain)
268
}
269
}
270
```