or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application.mdconfig.mdengine.mdindex.mdrequest-response.mdrouting.mdutilities.md

utilities.mddocs/

0

# HTTP Utilities and Extensions

1

2

HTTP-specific utilities, link headers, server push support, logging capabilities, and various helper functions for enhanced server functionality.

3

4

## Capabilities

5

6

### HTTP Link Headers

7

8

Support for HTTP Link headers as defined in RFC 8288.

9

10

```kotlin { .api }

11

/**

12

* Represents HTTP Link header information

13

*/

14

data class Link(

15

/** URI reference for the link */

16

val uri: String,

17

18

/** Relation types for this link */

19

val rel: List<String>,

20

21

/** Content type of the linked resource */

22

val type: ContentType? = null,

23

24

/** Human-readable title for the link */

25

val title: String? = null

26

)

27

28

/**

29

* Add Link header to response

30

*/

31

fun ApplicationResponse.linkHeader(link: Link)

32

33

/**

34

* Add Link header to response with simplified parameters

35

*/

36

fun ApplicationResponse.linkHeader(

37

uri: String,

38

rel: String,

39

type: ContentType? = null,

40

title: String? = null

41

)

42

```

43

44

### HTTP/2 Server Push

45

46

Support for HTTP/2 server push functionality.

47

48

```kotlin { .api }

49

/**

50

* Initiate HTTP/2 server push

51

*/

52

suspend fun ApplicationCall.push(block: ResponsePushBuilder.() -> Unit)

53

54

/**

55

* Builder for HTTP/2 push promises

56

*/

57

class ResponsePushBuilder {

58

/** URL to push */

59

var url: Url

60

61

/** HTTP method for pushed request */

62

var method: HttpMethod

63

64

/** Headers for pushed request */

65

val headers: HeadersBuilder

66

}

67

```

68

69

### Content Transformation

70

71

Default content transformations for request and response processing.

72

73

```kotlin { .api }

74

/**

75

* Configure default content transformations

76

*/

77

fun ApplicationSendPipeline.defaultTransformations()

78

79

/**

80

* Configure default content transformations for receive pipeline

81

*/

82

fun ApplicationReceivePipeline.defaultTransformations()

83

```

84

85

### HTTP Parsing Utilities

86

87

Utilities for parsing HTTP headers and cookies.

88

89

```kotlin { .api }

90

/**

91

* Parse Cookie header into individual cookies

92

*/

93

fun parseClientCookiesHeader(cookiesHeader: String): Map<String, String>

94

95

/**

96

* Parse Set-Cookie header

97

*/

98

fun parseServerSetCookieHeader(setCookieHeader: String): Cookie

99

```

100

101

### Logging and MDC Support

102

103

Mapped Diagnostic Context (MDC) support for structured logging.

104

105

```kotlin { .api }

106

/**

107

* Provides MDC (Mapped Diagnostic Context) functionality

108

*/

109

interface MDCProvider {

110

/** Get MDC value by key */

111

fun get(key: String): String?

112

113

/** Set MDC value */

114

fun put(key: String, value: String?)

115

116

/** Remove MDC value */

117

fun remove(key: String)

118

119

/** Clear all MDC values */

120

fun clear()

121

122

/** Get copy of current MDC context */

123

fun getCopyOfContextMap(): Map<String, String>?

124

125

/** Set entire MDC context */

126

fun setContextMap(contextMap: Map<String, String>)

127

}

128

129

/**

130

* Call logging plugin for HTTP request/response logging

131

*/

132

object CallLogging : ApplicationPlugin<CallLoggingConfig>

133

```

134

135

### Origin Connection Point

136

137

Data class for representing connection point information, useful for proxy scenarios.

138

139

```kotlin { .api }

140

/**

141

* Represents connection point information for forwarded requests

142

*/

143

data class OriginConnectionPoint(

144

/** URI scheme (http, https) */

145

val scheme: String,

146

147

/** Host name */

148

val host: String,

149

150

/** Port number */

151

val port: Int,

152

153

/** HTTP version */

154

val version: String

155

)

156

```

157

158

### Parameter Utilities

159

160

Enhanced parameter handling and URL building utilities.

161

162

```kotlin { .api }

163

/**

164

* Type alias for parameter collections (string-based key-value pairs)

165

*/

166

typealias Parameters = StringValues

167

168

/**

169

* Get parameter or throw exception if missing

170

*/

171

fun Parameters.getOrFail(name: String): String

172

173

/**

174

* Encode parameters to URL format

175

*/

176

fun encodeParameters(parameters: Parameters): String

177

178

/**

179

* Parse URL-encoded parameters

180

*/

181

fun parseParameters(encoded: String): Parameters

182

```

183

184

### URL Building

185

186

Utilities for building URLs relative to current request context.

187

188

```kotlin { .api }

189

/**

190

* Build URLs relative to current request

191

*/

192

fun ApplicationCall.url(block: URLBuilder.() -> Unit = {}): String

193

194

/**

195

* Build paths relative to current request

196

*/

197

fun ApplicationCall.path(vararg segments: String): String

198

199

/**

200

* Build URL with specific path segments

201

*/

202

fun ApplicationCall.href(path: String, block: URLBuilder.() -> Unit = {}): String

203

```

204

205

### Path Utilities

206

207

Utilities for path manipulation and normalization.

208

209

```kotlin { .api }

210

/**

211

* Combine path segments safely

212

*/

213

fun combinePath(vararg segments: String): String

214

215

/**

216

* Resolve relative paths

217

*/

218

fun resolvePath(base: String, relative: String): String

219

220

/**

221

* Normalize and relativize paths

222

*/

223

fun normalizeAndRelativize(path: String): String

224

```

225

226

### Thread-Safe Collections

227

228

Copy-on-write hash map implementation for thread-safe operations.

229

230

```kotlin { .api }

231

/**

232

* Thread-safe copy-on-write hash map implementation

233

*/

234

class CopyOnWriteHashMap<K, V> : MutableMap<K, V> {

235

/** Get value by key */

236

override fun get(key: K): V?

237

238

/** Put key-value pair, returns previous value */

239

override fun put(key: K, value: V): V?

240

241

/** Remove key, returns previous value */

242

override fun remove(key: K): V?

243

244

/** Clear all entries */

245

override fun clear()

246

247

/** Get current size */

248

override val size: Int

249

250

/** Check if empty */

251

override fun isEmpty(): Boolean

252

253

/** Check if contains key */

254

override fun containsKey(key: K): Boolean

255

256

/** Check if contains value */

257

override fun containsValue(value: V): Boolean

258

259

/** Get all keys */

260

override val keys: MutableSet<K>

261

262

/** Get all values */

263

override val values: MutableCollection<V>

264

265

/** Get all entries */

266

override val entries: MutableSet<MutableMap.MutableEntry<K, V>>

267

}

268

```

269

270

### Data Conversion Plugin

271

272

Plugin for automatic data type conversion.

273

274

```kotlin { .api }

275

/**

276

* Plugin for data conversion between types

277

*/

278

object DataConversion : ApplicationPlugin<DataConversionConfig>

279

280

/**

281

* Configuration for data conversion

282

*/

283

class DataConversionConfig {

284

/** Add conversion from one type to another */

285

fun <T, R> convert(from: KClass<T>, to: KClass<R>, converter: (T) -> R)

286

287

/** Add conversion with conditions */

288

fun <T, R> convert(

289

from: KClass<T>,

290

to: KClass<R>,

291

predicate: (T) -> Boolean,

292

converter: (T) -> R

293

)

294

}

295

```

296

297

### Error Handling Utilities

298

299

Exception classes for common HTTP error scenarios.

300

301

```kotlin { .api }

302

/**

303

* Exception for bad requests (400)

304

*/

305

class BadRequestException(message: String = "Bad Request", cause: Throwable? = null) : Exception(message, cause)

306

307

/**

308

* Exception for not found resources (404)

309

*/

310

class NotFoundException(message: String = "Not Found", cause: Throwable? = null) : Exception(message, cause)

311

312

/**

313

* Exception when content cannot be transformed to requested type

314

*/

315

class CannotTransformContentToTypeException(

316

message: String,

317

cause: Throwable? = null

318

) : Exception(message, cause)

319

320

/**

321

* Exception for unsupported media types (415)

322

*/

323

class UnsupportedMediaTypeException(

324

contentType: ContentType,

325

cause: Throwable? = null

326

) : Exception("Unsupported media type: $contentType", cause)

327

```

328

329

## Usage Examples

330

331

### HTTP Link Headers

332

333

```kotlin

334

import io.ktor.server.application.*

335

import io.ktor.server.http.*

336

import io.ktor.server.response.*

337

import io.ktor.server.routing.*

338

339

fun Application.linkHeaders() {

340

routing {

341

get("/article/{id}") {

342

val articleId = call.parameters["id"]

343

344

// Add link headers for related resources

345

call.response.linkHeader(

346

uri = "/api/articles/$articleId",

347

rel = "canonical"

348

)

349

350

call.response.linkHeader(Link(

351

uri = "/articles/$articleId/comments",

352

rel = listOf("related", "comments"),

353

type = ContentType.Application.Json,

354

title = "Article Comments"

355

))

356

357

call.response.linkHeader(

358

uri = "/css/article.css",

359

rel = "stylesheet",

360

type = ContentType.Text.CSS

361

)

362

363

call.respondText("Article $articleId")

364

}

365

366

get("/api/users") {

367

val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1

368

val limit = call.request.queryParameters["limit"]?.toIntOrNull() ?: 20

369

370

// Pagination links

371

if (page > 1) {

372

call.response.linkHeader(

373

uri = "/api/users?page=${page - 1}&limit=$limit",

374

rel = "prev"

375

)

376

}

377

378

call.response.linkHeader(

379

uri = "/api/users?page=${page + 1}&limit=$limit",

380

rel = "next"

381

)

382

383

call.response.linkHeader(

384

uri = "/api/users?page=1&limit=$limit",

385

rel = "first"

386

)

387

388

call.respond(emptyList<String>()) // Mock response

389

}

390

}

391

}

392

```

393

394

### HTTP/2 Server Push

395

396

```kotlin

397

import io.ktor.server.application.*

398

import io.ktor.server.http.*

399

import io.ktor.server.response.*

400

import io.ktor.server.routing.*

401

402

fun Application.serverPush() {

403

routing {

404

get("/") {

405

// Push related resources before sending main response

406

call.push {

407

url = Url("/css/main.css")

408

method = HttpMethod.Get

409

headers.append(HttpHeaders.Accept, "text/css")

410

}

411

412

call.push {

413

url = Url("/js/main.js")

414

method = HttpMethod.Get

415

headers.append(HttpHeaders.Accept, "application/javascript")

416

}

417

418

call.push {

419

url = Url("/images/logo.png")

420

method = HttpMethod.Get

421

headers.append(HttpHeaders.Accept, "image/*")

422

}

423

424

// Send main HTML response

425

call.respondText("""

426

<!DOCTYPE html>

427

<html>

428

<head>

429

<link rel="stylesheet" href="/css/main.css">

430

<script src="/js/main.js" defer></script>

431

</head>

432

<body>

433

<img src="/images/logo.png" alt="Logo">

434

<h1>Welcome</h1>

435

</body>

436

</html>

437

""".trimIndent(), ContentType.Text.Html)

438

}

439

}

440

}

441

```

442

443

### URL Building and Path Utilities

444

445

```kotlin

446

import io.ktor.server.application.*

447

import io.ktor.server.response.*

448

import io.ktor.server.routing.*

449

import io.ktor.server.util.*

450

451

fun Application.urlBuilding() {

452

routing {

453

get("/user/{id}") {

454

val userId = call.parameters["id"]

455

456

// Build URLs relative to current request

457

val profileUrl = call.url {

458

path("profile")

459

}

460

461

val apiUrl = call.url {

462

path("api", "users", userId ?: "")

463

parameters.append("include", "profile")

464

}

465

466

val avatarPath = call.path("avatar.jpg")

467

468

val editHref = call.href("/edit") {

469

parameters.append("id", userId ?: "")

470

}

471

472

call.respond(mapOf(

473

"userId" to userId,

474

"profileUrl" to profileUrl,

475

"apiUrl" to apiUrl,

476

"avatarPath" to avatarPath,

477

"editHref" to editHref

478

))

479

}

480

481

get("/docs/{...}") {

482

val pathSegments = call.parameters.getAll("...")

483

val docPath = pathSegments?.joinToString("/") ?: ""

484

485

// Path utilities

486

val normalizedPath = normalizeAndRelativize(docPath)

487

val combinedPath = combinePath("docs", "v1", normalizedPath)

488

val resolvedPath = resolvePath("/documentation", combinedPath)

489

490

call.respond(mapOf(

491

"originalPath" to docPath,

492

"normalizedPath" to normalizedPath,

493

"combinedPath" to combinedPath,

494

"resolvedPath" to resolvedPath

495

))

496

}

497

}

498

}

499

```

500

501

### Parameter Handling

502

503

```kotlin

504

import io.ktor.server.application.*

505

import io.ktor.server.response.*

506

import io.ktor.server.routing.*

507

import io.ktor.server.util.*

508

509

fun Application.parameterHandling() {

510

routing {

511

get("/required") {

512

try {

513

val userId = call.request.queryParameters.getOrFail("userId")

514

val action = call.request.queryParameters.getOrFail("action")

515

516

call.respond(mapOf(

517

"userId" to userId,

518

"action" to action

519

))

520

521

} catch (e: IllegalArgumentException) {

522

call.respond(HttpStatusCode.BadRequest, "Missing required parameters")

523

}

524

}

525

526

post("/form") {

527

val formParams = call.receiveParameters()

528

529

// Encode parameters back to URL format

530

val encoded = encodeParameters(formParams)

531

532

call.respond(mapOf(

533

"received" to formParams.toMap(),

534

"encoded" to encoded

535

))

536

}

537

}

538

}

539

```

540

541

### Thread-Safe Data Structures

542

543

```kotlin

544

import io.ktor.server.application.*

545

import io.ktor.server.response.*

546

import io.ktor.server.routing.*

547

import io.ktor.server.util.*

548

549

class SessionManager {

550

private val sessions = CopyOnWriteHashMap<String, SessionData>()

551

552

fun createSession(sessionId: String, data: SessionData) {

553

sessions[sessionId] = data

554

}

555

556

fun getSession(sessionId: String): SessionData? {

557

return sessions[sessionId]

558

}

559

560

fun removeSession(sessionId: String): SessionData? {

561

return sessions.remove(sessionId)

562

}

563

564

fun getAllSessions(): Map<String, SessionData> {

565

return sessions.toMap()

566

}

567

568

fun getActiveSessionCount(): Int {

569

return sessions.size

570

}

571

}

572

573

data class SessionData(

574

val userId: String,

575

val createdAt: Long,

576

val lastAccessedAt: Long

577

)

578

579

fun Application.sessionHandling() {

580

val sessionManager = SessionManager()

581

582

routing {

583

post("/login") {

584

val sessionId = generateSessionId()

585

val sessionData = SessionData(

586

userId = "user123",

587

createdAt = System.currentTimeMillis(),

588

lastAccessedAt = System.currentTimeMillis()

589

)

590

591

sessionManager.createSession(sessionId, sessionData)

592

593

call.respond(mapOf(

594

"sessionId" to sessionId,

595

"activeSessions" to sessionManager.getActiveSessionCount()

596

))

597

}

598

599

get("/session/{id}") {

600

val sessionId = call.parameters["id"]

601

val session = sessionManager.getSession(sessionId ?: "")

602

603

if (session != null) {

604

call.respond(session)

605

} else {

606

call.respond(HttpStatusCode.NotFound, "Session not found")

607

}

608

}

609

}

610

}

611

612

private fun generateSessionId(): String {

613

return java.util.UUID.randomUUID().toString()

614

}

615

```

616

617

### Error Handling

618

619

```kotlin

620

import io.ktor.server.application.*

621

import io.ktor.server.plugins.*

622

import io.ktor.server.response.*

623

import io.ktor.server.routing.*

624

625

fun Application.errorHandling() {

626

routing {

627

get("/validate/{value}") {

628

val value = call.parameters["value"]

629

630

if (value.isNullOrBlank()) {

631

throw BadRequestException("Value parameter is required")

632

}

633

634

val intValue = value.toIntOrNull()

635

?: throw BadRequestException("Value must be a valid integer")

636

637

if (intValue < 0) {

638

throw BadRequestException("Value must be positive")

639

}

640

641

call.respond(mapOf("validatedValue" to intValue))

642

}

643

644

get("/resource/{id}") {

645

val resourceId = call.parameters["id"]

646

val resource = findResourceById(resourceId)

647

648

if (resource == null) {

649

throw NotFoundException("Resource with ID $resourceId not found")

650

}

651

652

call.respond(resource)

653

}

654

655

post("/convert") {

656

val contentType = call.request.contentType

657

658

when {

659

contentType.match(ContentType.Application.Json) -> {

660

try {

661

val data = call.receive<Map<String, Any>>()

662

call.respond(data)

663

} catch (e: Exception) {

664

throw CannotTransformContentToTypeException(

665

"Failed to parse JSON content",

666

e

667

)

668

}

669

}

670

671

contentType.match(ContentType.Application.Xml) -> {

672

throw UnsupportedMediaTypeException(contentType)

673

}

674

675

else -> {

676

throw UnsupportedMediaTypeException(contentType)

677

}

678

}

679

}

680

}

681

}

682

683

private fun findResourceById(id: String?): Map<String, Any>? {

684

// Mock implementation

685

return if (id == "123") {

686

mapOf("id" to id, "name" to "Sample Resource")

687

} else {

688

null

689

}

690

}

691

```

692

693

### Data Conversion

694

695

```kotlin

696

import io.ktor.server.application.*

697

import io.ktor.server.plugins.*

698

import io.ktor.server.response.*

699

import io.ktor.server.routing.*

700

701

data class User(val id: Int, val name: String, val email: String)

702

data class UserDto(val id: String, val displayName: String, val contactEmail: String)

703

704

fun Application.dataConversion() {

705

install(DataConversion) {

706

// Convert User to UserDto

707

convert<User, UserDto> { user ->

708

UserDto(

709

id = user.id.toString(),

710

displayName = user.name,

711

contactEmail = user.email

712

)

713

}

714

715

// Convert String to Int with validation

716

convert<String, Int>(

717

predicate = { it.matches(Regex("\\d+")) }

718

) { it.toInt() }

719

720

// Convert String to Boolean

721

convert<String, Boolean> { str ->

722

when (str.lowercase()) {

723

"true", "yes", "1" -> true

724

"false", "no", "0" -> false

725

else -> throw IllegalArgumentException("Invalid boolean value: $str")

726

}

727

}

728

}

729

730

routing {

731

get("/user/{id}") {

732

val userId = call.parameters["id"]?.toIntOrNull()

733

?: return@get call.respond(HttpStatusCode.BadRequest)

734

735

val user = User(userId, "John Doe", "john@example.com")

736

737

// DataConversion will automatically convert User to UserDto

738

call.respond(user)

739

}

740

}

741

}

742

```

743

744

### Logging and MDC

745

746

```kotlin

747

import io.ktor.server.application.*

748

import io.ktor.server.logging.*

749

import io.ktor.server.response.*

750

import io.ktor.server.routing.*

751

752

fun Application.loggingAndMdc() {

753

install(CallLogging) {

754

level = Level.INFO

755

756

mdc("requestId") {

757

java.util.UUID.randomUUID().toString()

758

}

759

760

mdc("userId") { call ->

761

call.request.headers["X-User-ID"]

762

}

763

764

filter { call ->

765

!call.request.path().startsWith("/health")

766

}

767

}

768

769

routing {

770

get("/api/data") {

771

val mdcProvider = application.environment.monitor.mdcProvider

772

773

// Add custom MDC values

774

mdcProvider?.put("operation", "fetchData")

775

mdcProvider?.put("startTime", System.currentTimeMillis().toString())

776

777

try {

778

// Simulate data fetching

779

val data = fetchData()

780

781

mdcProvider?.put("recordCount", data.size.toString())

782

application.environment.log.info("Data fetch completed successfully")

783

784

call.respond(data)

785

786

} catch (e: Exception) {

787

mdcProvider?.put("error", e.message ?: "Unknown error")

788

application.environment.log.error("Data fetch failed", e)

789

790

call.respond(HttpStatusCode.InternalServerError, "Data fetch failed")

791

} finally {

792

mdcProvider?.remove("operation")

793

mdcProvider?.remove("startTime")

794

mdcProvider?.remove("recordCount")

795

mdcProvider?.remove("error")

796

}

797

}

798

}

799

}

800

801

private fun fetchData(): List<String> {

802

// Mock data fetching

803

return listOf("item1", "item2", "item3")

804

}

805

```