0
# Content Handling
1
2
Outgoing content system with JVM-specific implementations for streams, writers, and file content. Provides base classes and concrete implementations for handling various types of HTTP response content.
3
4
## Capabilities
5
6
### OutgoingContent Base Class
7
8
Abstract base class for all outgoing HTTP content with property management and caching support.
9
10
```kotlin { .api }
11
/**
12
* Base class for outgoing HTTP content
13
*/
14
abstract class OutgoingContent {
15
/**
16
* Content type of the response
17
*/
18
open val contentType: ContentType?
19
20
/**
21
* Content length in bytes if known
22
*/
23
open val contentLength: Long?
24
25
/**
26
* HTTP status code for the response
27
*/
28
open val status: HttpStatusCode?
29
30
/**
31
* Additional HTTP headers
32
*/
33
open val headers: Headers?
34
35
/**
36
* Trailing headers (HTTP/1.1 chunked encoding)
37
*/
38
open fun trailers(): Headers?
39
40
/**
41
* Get property by key
42
*/
43
fun <T : Any> getProperty(key: AttributeKey<T>): T?
44
45
/**
46
* Set property value
47
*/
48
fun <T : Any> setProperty(key: AttributeKey<T>, value: T?)
49
}
50
51
/**
52
* Check if content is empty
53
*/
54
fun OutgoingContent.isEmpty(): Boolean
55
```
56
57
### ByteArrayContent
58
59
Content backed by a byte array for small, in-memory content.
60
61
```kotlin { .api }
62
/**
63
* Outgoing content backed by byte array
64
*/
65
abstract class OutgoingContent.ByteArrayContent : OutgoingContent() {
66
/**
67
* Get content as byte array
68
*/
69
abstract fun bytes(): ByteArray
70
}
71
72
/**
73
* Concrete byte array content implementation
74
*/
75
class ByteArrayContent(
76
private val bytes: ByteArray,
77
override val contentType: ContentType,
78
override val status: HttpStatusCode? = null
79
) : OutgoingContent.ByteArrayContent() {
80
81
override val contentLength: Long = bytes.size.toLong()
82
83
override fun bytes(): ByteArray = bytes
84
}
85
```
86
87
### TextContent
88
89
Text content with automatic charset handling and encoding.
90
91
```kotlin { .api }
92
/**
93
* Text content with charset support
94
*/
95
class TextContent(
96
private val text: String,
97
override val contentType: ContentType,
98
override val status: HttpStatusCode? = null
99
) : OutgoingContent.ByteArrayContent() {
100
101
override val contentLength: Long
102
103
/**
104
* Get original text
105
*/
106
val text: String
107
108
override fun bytes(): ByteArray
109
}
110
```
111
112
### WriteChannelContent
113
114
Content that writes data to a ByteWriteChannel for streaming.
115
116
```kotlin { .api }
117
/**
118
* Content that writes to a channel
119
*/
120
abstract class OutgoingContent.WriteChannelContent : OutgoingContent() {
121
/**
122
* Write content to the channel
123
* @param channel channel to write to
124
*/
125
abstract suspend fun writeTo(channel: ByteWriteChannel)
126
}
127
128
/**
129
* Channel writer content with suspend function body
130
*/
131
class ChannelWriterContent(
132
private val body: suspend ByteWriteChannel.() -> Unit,
133
override val contentType: ContentType,
134
override val status: HttpStatusCode? = null,
135
override val contentLength: Long? = null
136
) : OutgoingContent.WriteChannelContent() {
137
138
override suspend fun writeTo(channel: ByteWriteChannel) {
139
channel.body()
140
}
141
}
142
```
143
144
### ReadChannelContent
145
146
Content that provides data through a ByteReadChannel for streaming reads.
147
148
```kotlin { .api }
149
/**
150
* Content that provides a read channel
151
*/
152
abstract class OutgoingContent.ReadChannelContent : OutgoingContent() {
153
/**
154
* Create read channel for full content
155
*/
156
abstract fun readFrom(): ByteReadChannel
157
158
/**
159
* Create read channel for content range
160
* @param range byte range to read
161
*/
162
open fun readFrom(range: LongRange): ByteReadChannel
163
}
164
```
165
166
### JVM-specific Content Types
167
168
JVM-specific content implementations using Java I/O streams and writers.
169
170
#### OutputStreamContent
171
172
```kotlin { .api }
173
/**
174
* Content that writes to an OutputStream (JVM-specific)
175
*/
176
class OutputStreamContent(
177
private val body: suspend OutputStream.() -> Unit,
178
override val contentType: ContentType,
179
override val status: HttpStatusCode? = null,
180
override val contentLength: Long? = null
181
) : OutgoingContent.WriteChannelContent() {
182
183
override suspend fun writeTo(channel: ByteWriteChannel)
184
}
185
```
186
187
#### WriterContent
188
189
```kotlin { .api }
190
/**
191
* Content that writes to a Writer with charset handling (JVM-specific)
192
*/
193
class WriterContent(
194
private val body: suspend Writer.() -> Unit,
195
override val contentType: ContentType,
196
override val status: HttpStatusCode? = null,
197
override val contentLength: Long? = null
198
) : OutgoingContent.WriteChannelContent() {
199
200
override suspend fun writeTo(channel: ByteWriteChannel)
201
}
202
```
203
204
#### URIFileContent
205
206
```kotlin { .api }
207
/**
208
* Content served from URI/URL (JVM-specific)
209
*/
210
class URIFileContent(
211
val uri: URI,
212
override val contentType: ContentType = ContentType.defaultForFilePath(uri.path),
213
override val contentLength: Long? = null
214
) : OutgoingContent.ReadChannelContent() {
215
216
/**
217
* Constructor from URL
218
*/
219
constructor(
220
url: URL,
221
contentType: ContentType = ContentType.defaultForFilePath(url.path)
222
)
223
224
override fun readFrom(): ByteReadChannel
225
}
226
```
227
228
### NoContent
229
230
Content for responses without body (like HTTP 204).
231
232
```kotlin { .api }
233
/**
234
* Content representing no content
235
*/
236
abstract class OutgoingContent.NoContent : OutgoingContent()
237
```
238
239
### ProtocolUpgrade
240
241
Content for protocol upgrade responses (like WebSocket).
242
243
```kotlin { .api }
244
/**
245
* Content for protocol upgrade
246
*/
247
abstract class OutgoingContent.ProtocolUpgrade : OutgoingContent() {
248
override val status: HttpStatusCode = HttpStatusCode.SwitchingProtocols
249
250
/**
251
* Perform protocol upgrade
252
*/
253
abstract suspend fun upgrade(
254
input: ByteReadChannel,
255
output: ByteWriteChannel,
256
engineContext: CoroutineContext,
257
userContext: CoroutineContext
258
)
259
}
260
```
261
262
### ContentWrapper
263
264
Base class for content wrappers that delegate to another content.
265
266
```kotlin { .api }
267
/**
268
* Content wrapper that delegates to another content
269
*/
270
abstract class OutgoingContent.ContentWrapper(
271
private val delegate: OutgoingContent
272
) : OutgoingContent() {
273
274
/**
275
* Create copy with different delegate
276
*/
277
abstract fun copy(delegate: OutgoingContent): ContentWrapper
278
279
/**
280
* Get wrapped content
281
*/
282
fun delegate(): OutgoingContent
283
284
override val contentType: ContentType? get() = delegate.contentType
285
override val contentLength: Long? get() = delegate.contentLength
286
override val status: HttpStatusCode? get() = delegate.status
287
override val headers: Headers? get() = delegate.headers
288
}
289
```
290
291
### Content Compression
292
293
Utilities for compressing outgoing content.
294
295
```kotlin { .api }
296
/**
297
* Compress content using specified encoder
298
* @param encoder content encoder to use
299
* @param coroutineContext context for compression
300
* @return compressed content
301
*/
302
fun OutgoingContent.compressed(
303
encoder: ContentEncoder,
304
coroutineContext: CoroutineContext = EmptyCoroutineContext
305
): OutgoingContent
306
```
307
308
### Content Caching
309
310
Caching options and utilities for outgoing content.
311
312
```kotlin { .api }
313
/**
314
* Caching options for content
315
*/
316
data class CachingOptions(
317
val cacheControl: CacheControl? = null,
318
val expires: GMTDate? = null
319
)
320
321
/**
322
* Get caching options from content
323
*/
324
val OutgoingContent.caching: CachingOptions?
325
326
/**
327
* Set caching options for content
328
*/
329
fun OutgoingContent.setCaching(options: CachingOptions)
330
331
/**
332
* Property key for caching options
333
*/
334
val cachingProperty: AttributeKey<CachingOptions>
335
```
336
337
### Special Content Types
338
339
Special content types for specific use cases.
340
341
```kotlin { .api }
342
/**
343
* Represents null/empty body
344
*/
345
object NullBody
346
```
347
348
**Usage Examples:**
349
350
```kotlin
351
import io.ktor.http.*
352
import io.ktor.http.content.*
353
import java.io.*
354
import java.net.URI
355
356
// Simple text content
357
val textResponse = TextContent(
358
text = "Hello, World!",
359
contentType = ContentType.Text.Plain.withCharset(Charsets.UTF_8),
360
status = HttpStatusCode.OK
361
)
362
363
// Byte array content
364
val jsonBytes = """{"message": "Hello"}""".toByteArray()
365
val jsonResponse = ByteArrayContent(
366
bytes = jsonBytes,
367
contentType = ContentType.Application.Json,
368
status = HttpStatusCode.OK
369
)
370
371
// JVM-specific: OutputStream content
372
val streamContent = OutputStreamContent(
373
body = { outputStream ->
374
outputStream.write("Hello from OutputStream".toByteArray())
375
outputStream.flush()
376
},
377
contentType = ContentType.Text.Plain,
378
status = HttpStatusCode.OK
379
)
380
381
// JVM-specific: Writer content with charset handling
382
val writerContent = WriterContent(
383
body = { writer ->
384
writer.write("Hello from Writer")
385
writer.flush()
386
},
387
contentType = ContentType.Text.Html.withCharset(Charsets.UTF_8),
388
status = HttpStatusCode.OK
389
)
390
391
// JVM-specific: File content from URI
392
val fileUri = URI("file:///path/to/document.pdf")
393
val fileContent = URIFileContent(
394
uri = fileUri,
395
contentType = ContentType.Application.Pdf
396
)
397
398
// Channel writer for streaming
399
val streamingContent = ChannelWriterContent(
400
body = { channel ->
401
channel.writeStringUtf8("Chunk 1\n")
402
channel.flush()
403
delay(100)
404
channel.writeStringUtf8("Chunk 2\n")
405
channel.flush()
406
},
407
contentType = ContentType.Text.Plain,
408
contentLength = null // Unknown length for streaming
409
)
410
411
// Content with caching
412
val cachedContent = TextContent("Cached response", ContentType.Text.Plain)
413
cachedContent.setCaching(CachingOptions(
414
cacheControl = CacheControl.MaxAge(maxAgeSeconds = 3600),
415
expires = GMTDate() + 3600_000 // 1 hour from now
416
))
417
418
// Compressed content
419
val compressedContent = textResponse.compressed(
420
encoder = GzipEncoder,
421
coroutineContext = Dispatchers.IO
422
)
423
424
// Check content properties
425
println("Content type: ${textResponse.contentType}")
426
println("Content length: ${textResponse.contentLength}")
427
println("Status: ${textResponse.status}")
428
println("Is empty: ${textResponse.isEmpty()}")
429
430
// Access content data
431
when (val content = response.content) {
432
is TextContent -> println("Text: ${content.text}")
433
is ByteArrayContent -> println("Bytes: ${content.bytes().size}")
434
is URIFileContent -> println("URI: ${content.uri}")
435
}
436
```
437
438
## Types
439
440
All types are defined above in their respective capability sections.
441
442
## Content Properties
443
444
```kotlin { .api }
445
/**
446
* Property key for version list
447
*/
448
val versionListProperty: AttributeKey<List<Version>>
449
450
/**
451
* Get versions from content
452
*/
453
val OutgoingContent.versions: List<Version>
454
455
/**
456
* Set versions for content
457
*/
458
fun OutgoingContent.setVersions(versions: List<Version>)
459
```