or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdcontent-types.mdcontent.mdcookies.mdheaders.mdhttp-core.mdindex.mdurls.md

content.mddocs/

0

# Content Framework

1

2

Outgoing content types for representing different data sources and formats in HTTP responses, with support for multipart data, versioning, and caching.

3

4

## Capabilities

5

6

### OutgoingContent Base Class

7

8

Abstract base class for all outgoing HTTP content with common properties and extensibility.

9

10

```kotlin { .api }

11

/**

12

* Base class for all outgoing HTTP content

13

*/

14

abstract class OutgoingContent {

15

open val contentType: ContentType?

16

open val contentLength: Long?

17

open val status: HttpStatusCode?

18

open val headers: Headers?

19

20

/**

21

* Get content property by key

22

* @param key Property key

23

* @return Property value or null

24

*/

25

fun <T : Any> getProperty(key: AttributeKey<T>): T?

26

27

/**

28

* Set content property

29

* @param key Property key

30

* @param value Property value

31

*/

32

fun <T : Any> setProperty(key: AttributeKey<T>, value: T?)

33

34

/**

35

* Get trailer headers (sent after content)

36

* @return Trailer headers or null

37

*/

38

open fun trailers(): Headers?

39

}

40

41

/**

42

* Check if content is empty

43

* @return true if content has no body

44

*/

45

fun OutgoingContent.isEmpty(): Boolean

46

```

47

48

### ByteArrayContent

49

50

Content type for byte array data.

51

52

```kotlin { .api }

53

/**

54

* Content from byte array

55

*/

56

abstract class OutgoingContent.ByteArrayContent : OutgoingContent() {

57

/**

58

* Get content as byte array

59

* @return Content bytes

60

*/

61

abstract fun bytes(): ByteArray

62

}

63

64

/**

65

* Concrete implementation for byte array content

66

*/

67

class ByteArrayContent(

68

private val bytes: ByteArray,

69

override val contentType: ContentType?,

70

override val status: HttpStatusCode? = null

71

) : OutgoingContent.ByteArrayContent() {

72

override fun bytes(): ByteArray = bytes

73

override val contentLength: Long? = bytes.size.toLong()

74

}

75

```

76

77

### TextContent

78

79

Content type for text data with automatic encoding.

80

81

```kotlin { .api }

82

/**

83

* Content from text string

84

*/

85

class TextContent(

86

val text: String,

87

override val contentType: ContentType,

88

override val status: HttpStatusCode? = null

89

) : OutgoingContent.ByteArrayContent() {

90

91

override fun bytes(): ByteArray

92

override val contentLength: Long?

93

}

94

```

95

96

### ReadChannelContent

97

98

Content type for streaming data from ByteReadChannel.

99

100

```kotlin { .api }

101

/**

102

* Content from ByteReadChannel

103

*/

104

abstract class OutgoingContent.ReadChannelContent : OutgoingContent() {

105

/**

106

* Get read channel for content

107

* @return ByteReadChannel with content

108

*/

109

abstract fun readFrom(): ByteReadChannel

110

111

/**

112

* Get read channel for content range

113

* @param range Byte range to read

114

* @return ByteReadChannel with range content

115

*/

116

open fun readFrom(range: LongRange): ByteReadChannel = readFrom()

117

}

118

```

119

120

### WriteChannelContent

121

122

Content type for data written to ByteWriteChannel.

123

124

```kotlin { .api }

125

/**

126

* Content written to ByteWriteChannel

127

*/

128

abstract class OutgoingContent.WriteChannelContent : OutgoingContent() {

129

/**

130

* Write content to channel

131

* @param channel Channel to write to

132

*/

133

abstract suspend fun writeTo(channel: ByteWriteChannel)

134

}

135

136

/**

137

* Content written via lambda function

138

*/

139

class ChannelWriterContent(

140

private val body: suspend ByteWriteChannel.() -> Unit,

141

override val contentType: ContentType?,

142

override val status: HttpStatusCode? = null,

143

override val contentLength: Long? = null

144

) : OutgoingContent.WriteChannelContent() {

145

146

override suspend fun writeTo(channel: ByteWriteChannel) {

147

channel.body()

148

}

149

}

150

```

151

152

### Additional Content Types

153

154

Specialized content types for different output methods.

155

156

```kotlin { .api }

157

/**

158

* Content written to OutputStream

159

*/

160

class OutputStreamContent(

161

private val body: suspend OutputStream.() -> Unit,

162

override val contentType: ContentType?,

163

override val status: HttpStatusCode? = null,

164

override val contentLength: Long? = null

165

) : OutgoingContent.WriteChannelContent()

166

167

/**

168

* Content written to Writer

169

*/

170

class WriterContent(

171

private val body: suspend Writer.() -> Unit,

172

override val contentType: ContentType?,

173

override val status: HttpStatusCode? = null,

174

override val contentLength: Long? = null

175

) : OutgoingContent.WriteChannelContent()

176

177

/**

178

* Content from URI/URL

179

*/

180

class URIFileContent(

181

val uri: URI,

182

override val contentType: ContentType?,

183

override val contentLength: Long? = null

184

) : OutgoingContent.ReadChannelContent() {

185

186

constructor(url: URL, contentType: ContentType?)

187

188

override fun readFrom(): ByteReadChannel

189

}

190

```

191

192

### NoContent and Special Types

193

194

Special content types for empty responses and protocol upgrades.

195

196

```kotlin { .api }

197

/**

198

* Empty content (no body)

199

*/

200

abstract class OutgoingContent.NoContent : OutgoingContent()

201

202

/**

203

* Protocol upgrade content (e.g., WebSocket)

204

*/

205

abstract class OutgoingContent.ProtocolUpgrade : OutgoingContent() {

206

override val status: HttpStatusCode? = HttpStatusCode.SwitchingProtocols

207

208

/**

209

* Handle protocol upgrade

210

* @param input Input channel

211

* @param output Output channel

212

* @param engineContext Engine coroutine context

213

* @param userContext User coroutine context

214

*/

215

abstract suspend fun upgrade(

216

input: ByteReadChannel,

217

output: ByteWriteChannel,

218

engineContext: CoroutineContext,

219

userContext: CoroutineContext

220

)

221

}

222

223

/**

224

* Null body marker

225

*/

226

object NullBody

227

```

228

229

### Content Wrapper

230

231

Base class for wrapping and modifying existing content.

232

233

```kotlin { .api }

234

/**

235

* Content wrapper for modifying existing content

236

*/

237

abstract class OutgoingContent.ContentWrapper(

238

private val delegate: OutgoingContent

239

) : OutgoingContent() {

240

241

/**

242

* Create copy with different delegate

243

* @param delegate New delegate content

244

* @return New ContentWrapper instance

245

*/

246

abstract fun copy(delegate: OutgoingContent): OutgoingContent.ContentWrapper

247

248

/**

249

* Get wrapped content

250

* @return Delegate content

251

*/

252

fun delegate(): OutgoingContent = delegate

253

254

// Default implementations delegate to wrapped content

255

override val contentType: ContentType? get() = delegate.contentType

256

override val contentLength: Long? get() = delegate.contentLength

257

override val status: HttpStatusCode? get() = delegate.status

258

override val headers: Headers? get() = delegate.headers

259

override fun <T : Any> getProperty(key: AttributeKey<T>): T? = delegate.getProperty(key)

260

override fun <T : Any> setProperty(key: AttributeKey<T>, value: T?) = delegate.setProperty(key, value)

261

}

262

```

263

264

### Multipart Data

265

266

Interface and types for handling multipart content.

267

268

```kotlin { .api }

269

/**

270

* Interface for multipart data reading

271

*/

272

interface MultiPartData {

273

/**

274

* Read next part from multipart data

275

* @return Next PartData or null if no more parts

276

*/

277

suspend fun readPart(): PartData?

278

279

companion object {

280

val Empty: MultiPartData

281

}

282

}

283

284

/**

285

* Base class for multipart parts

286

*/

287

abstract class PartData(

288

private val dispose: () -> Unit,

289

val headers: Headers

290

) {

291

val contentDisposition: ContentDisposition?

292

val contentType: ContentType?

293

val name: String?

294

295

/**

296

* Dispose part resources

297

*/

298

fun dispose() = dispose.invoke()

299

}

300

301

/**

302

* Form field part

303

*/

304

class PartData.FormItem(

305

val value: String,

306

dispose: () -> Unit,

307

headers: Headers

308

) : PartData(dispose, headers)

309

310

/**

311

* File upload part

312

*/

313

class PartData.FileItem(

314

val provider: () -> InputStream,

315

dispose: () -> Unit,

316

headers: Headers

317

) : PartData(dispose, headers) {

318

val originalFileName: String?

319

val streamProvider: () -> InputStream // JVM-specific

320

}

321

322

/**

323

* Binary data part

324

*/

325

class PartData.BinaryItem(

326

val provider: () -> ByteArray,

327

dispose: () -> Unit,

328

headers: Headers

329

) : PartData(dispose, headers)

330

331

/**

332

* Binary channel part

333

*/

334

class PartData.BinaryChannelItem(

335

val provider: () -> ByteReadChannel,

336

dispose: () -> Unit,

337

headers: Headers

338

) : PartData(dispose, headers)

339

```

340

341

### Multipart Utilities

342

343

Utility functions for working with multipart data.

344

345

```kotlin { .api }

346

/**

347

* Convert multipart data to Flow

348

* @return Flow of PartData

349

*/

350

fun MultiPartData.asFlow(): Flow<PartData>

351

352

/**

353

* Read all parts into list

354

* @return List of all PartData

355

*/

356

suspend fun MultiPartData.readAllParts(): List<PartData>

357

358

/**

359

* Process each part with action

360

* @param action Suspend function to process each part

361

*/

362

suspend fun MultiPartData.forEachPart(action: suspend (PartData) -> Unit)

363

```

364

365

### Content Versioning

366

367

System for content versioning with ETags and Last-Modified headers.

368

369

```kotlin { .api }

370

/**

371

* Base interface for content versioning

372

*/

373

interface Version {

374

/**

375

* Check version against request headers

376

* @param headers Request headers

377

* @return Version check result

378

*/

379

fun check(headers: Headers): VersionCheckResult

380

381

/**

382

* Append version headers to response

383

* @param builder Headers builder

384

*/

385

fun appendHeadersTo(builder: HeadersBuilder)

386

}

387

388

/**

389

* ETag-based versioning

390

*/

391

class EntityTagVersion(

392

val etag: String,

393

val weak: Boolean = false

394

) : Version {

395

396

/**

397

* Check if this ETag matches another

398

* @param other Other ETag version

399

* @return true if matches

400

*/

401

fun match(other: EntityTagVersion): Boolean

402

403

/**

404

* Check if this ETag matches any in list

405

* @param etags List of ETag versions

406

* @return Version check result

407

*/

408

fun match(etags: List<EntityTagVersion>): VersionCheckResult

409

410

/**

411

* Check if this ETag matches none in list

412

* @param etags List of ETag versions

413

* @return Version check result

414

*/

415

fun noneMatch(etags: List<EntityTagVersion>): VersionCheckResult

416

417

companion object {

418

val STAR: EntityTagVersion

419

420

/**

421

* Parse ETag header value

422

* @param value ETag header value

423

* @return List of EntityTagVersion instances

424

*/

425

fun parse(value: String): List<EntityTagVersion>

426

427

/**

428

* Parse single ETag value

429

* @param value Single ETag value

430

* @return EntityTagVersion instance

431

*/

432

fun parseSingle(value: String): EntityTagVersion

433

}

434

}

435

436

/**

437

* Last-Modified based versioning

438

*/

439

class LastModifiedVersion(

440

val lastModified: GMTDate

441

) : Version {

442

443

/**

444

* Check if modified since dates

445

* @param dates List of dates to check

446

* @return true if modified

447

*/

448

fun ifModifiedSince(dates: List<GMTDate>): Boolean

449

450

/**

451

* Check if unmodified since dates

452

* @param dates List of dates to check

453

* @return true if unmodified

454

*/

455

fun ifUnmodifiedSince(dates: List<GMTDate>): Boolean

456

}

457

458

/**

459

* Version check result

460

*/

461

enum class VersionCheckResult(val statusCode: HttpStatusCode) {

462

OK(HttpStatusCode.OK),

463

NOT_MODIFIED(HttpStatusCode.NotModified),

464

PRECONDITION_FAILED(HttpStatusCode.PreconditionFailed)

465

}

466

467

/**

468

* Create EntityTagVersion from string

469

* @param etag ETag value

470

* @return EntityTagVersion instance

471

*/

472

fun EntityTagVersion(etag: String): EntityTagVersion

473

474

/**

475

* Create LastModifiedVersion from Date

476

* @param date Last modified date

477

* @return LastModifiedVersion instance

478

*/

479

fun LastModifiedVersion(date: Date): LastModifiedVersion

480

```

481

482

### Caching Options

483

484

System for content caching configuration.

485

486

```kotlin { .api }

487

/**

488

* Content caching configuration

489

*/

490

data class CachingOptions(

491

val cacheControl: CacheControl? = null,

492

val expires: GMTDate? = null

493

)

494

495

/**

496

* Get caching options from content

497

* @return CachingOptions or null

498

*/

499

fun OutgoingContent.getCaching(): CachingOptions?

500

501

/**

502

* Set caching options on content

503

* @param options Caching options to set

504

*/

505

fun OutgoingContent.setCaching(options: CachingOptions)

506

507

/**

508

* Get versions list from content

509

* @return List of Version instances

510

*/

511

fun OutgoingContent.getVersions(): List<Version>

512

513

/**

514

* Set versions on content

515

* @param versions List of Version instances

516

*/

517

fun OutgoingContent.setVersions(versions: List<Version>)

518

```

519

520

### Content Compression

521

522

Utilities for content compression.

523

524

```kotlin { .api }

525

/**

526

* Create compressed version of content

527

* @param encoder Content encoder to use

528

* @param coroutineContext Coroutine context for compression

529

* @return Compressed OutgoingContent

530

*/

531

fun OutgoingContent.compressed(

532

encoder: ContentEncoder,

533

coroutineContext: CoroutineContext = EmptyCoroutineContext

534

): OutgoingContent

535

```

536

537

**Usage Examples:**

538

539

```kotlin

540

import io.ktor.http.content.*

541

import io.ktor.http.*

542

543

// Create different content types

544

val textContent = TextContent("Hello World", ContentType.Text.Plain)

545

val jsonContent = TextContent("""{"message": "hello"}""", ContentType.Application.Json)

546

val binaryContent = ByteArrayContent(byteArrayOf(1, 2, 3), ContentType.Application.OctetStream)

547

548

// Create streaming content

549

val channelContent = ChannelWriterContent(

550

body = { writeStringUtf8("Hello from channel") },

551

contentType = ContentType.Text.Plain

552

)

553

554

// Work with multipart data

555

suspend fun processMultipart(multipart: MultiPartData) {

556

multipart.forEachPart { part ->

557

when (part) {

558

is PartData.FormItem -> println("Form field: ${part.name} = ${part.value}")

559

is PartData.FileItem -> println("File: ${part.originalFileName}")

560

is PartData.BinaryItem -> println("Binary data: ${part.provider().size} bytes")

561

}

562

part.dispose()

563

}

564

}

565

566

// Content versioning

567

val etag = EntityTagVersion("abc123")

568

val lastModified = LastModifiedVersion(GMTDate())

569

570

textContent.setVersions(listOf(etag, lastModified))

571

572

// Caching

573

val caching = CachingOptions(

574

cacheControl = CacheControl.MaxAge(3600),

575

expires = GMTDate(System.currentTimeMillis() + 3600000)

576

)

577

textContent.setCaching(caching)

578

579

// Check content properties

580

val isEmpty = textContent.isEmpty() // false

581

val versions = textContent.getVersions()

582

val cachingOptions = textContent.getCaching()

583

```