or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication.mdcontent-processing.mdcontent-types.mdcookies.mddate-utilities.mdheader-parsing.mdheaders.mdhttp-message-extensions.mdhttp-methods-status.mdindex.mdmultipart.mdparameters.mdurl-encoding.mdurl-handling.md
tile.json

http-message-extensions.mddocs/

HTTP Message Extensions

Extension functions for HTTP messages and message builders providing convenient access to common HTTP headers with automatic parsing and type-safe handling for content types, charset, caching, cookies, and other HTTP properties.

Capabilities

HTTP Message Builder Extensions

Extension functions for building HTTP messages with convenient header setters and getters.

/**
 * Set Content-Type header
 * @param type the content type to set
 */
fun HttpMessageBuilder.contentType(type: ContentType): Unit

/**
 * Get parsed Content-Type header
 * @return ContentType if header exists and is valid, null otherwise
 */
fun HttpMessageBuilder.contentType(): ContentType?

/**
 * Get charset from Content-Type header
 * @return Charset if specified in content type, null otherwise
 */
fun HttpMessageBuilder.charset(): Charset?

/**
 * Get ETag header value
 * @return ETag value if present, null otherwise
 */
fun HttpMessageBuilder.etag(): String?

/**
 * Get Vary header values
 * @return list of header names from Vary header, null if not present
 */
fun HttpMessageBuilder.vary(): List<String>?

/**
 * Get Content-Length header value
 * @return content length if valid, null otherwise
 */
fun HttpMessageBuilder.contentLength(): Long?

/**
 * Parse Set-Cookie headers into Cookie objects
 * @return list of parsed cookies
 */
fun HttpMessageBuilder.cookies(): List<Cookie>

Usage Examples:

import io.ktor.http.*

// Setting content type in message builder
fun buildHttpMessage(): HttpMessage {
    return HttpMessageBuilder().apply {
        contentType(ContentType.Application.Json)
        userAgent("MyApp/1.0")
        maxAge(3600)
        ifNoneMatch("\"etag-123\"")
        
        // Access parsed values
        val type = contentType() // ContentType.Application.Json
        val charset = charset()  // null (no charset specified)
        val length = contentLength() // null (not set)
    }.build()
}

// Working with cookies
fun setCookiesInBuilder(builder: HttpMessageBuilder) {
    builder.headers.append(HttpHeaders.SetCookie, "sessionId=abc123; Path=/; HttpOnly")
    builder.headers.append(HttpHeaders.SetCookie, "theme=dark; Max-Age=3600")
    
    val cookies = builder.cookies()
    println(cookies.size) // 2
    println(cookies[0].name) // "sessionId"
    println(cookies[0].value) // "abc123"
    println(cookies[0].httpOnly) // true
}

HTTP Message Reader Extensions

Extension functions for reading HTTP message properties with automatic parsing.

/**
 * Get parsed Content-Type header from HTTP message
 * @return ContentType if header exists and is valid, null otherwise
 */
fun HttpMessage.contentType(): ContentType?

/**
 * Get charset from Content-Type header in HTTP message
 * @return Charset if specified in content type, null otherwise
 */
fun HttpMessage.charset(): Charset?

/**
 * Get ETag header value from HTTP message
 * @return ETag value if present, null otherwise
 */
fun HttpMessage.etag(): String?

/**
 * Get Vary header values from HTTP message
 * @return list of header names from Vary header, null if not present
 */
fun HttpMessage.vary(): List<String>?

/**
 * Get Content-Length header value from HTTP message
 * @return content length if valid, null otherwise
 */
fun HttpMessage.contentLength(): Long?

/**
 * Parse Set-Cookie headers from HTTP message
 * @return list of parsed Cookie objects
 */
fun HttpMessage.setCookie(): List<Cookie>

/**
 * Parse Cache-Control header values
 * @return list of HeaderValue objects representing cache control directives
 */
fun HttpMessage.cacheControl(): List<HeaderValue>

Usage Examples:

import io.ktor.http.*

// Reading message properties
fun analyzeHttpMessage(message: HttpMessage) {
    // Content analysis
    val contentType = message.contentType()
    println("Content type: ${contentType?.contentType}/${contentType?.contentSubtype}")
    
    val charset = message.charset()
    println("Charset: ${charset?.name}")
    
    val contentLength = message.contentLength()
    println("Content length: $contentLength bytes")
    
    // ETag handling
    val etag = message.etag()
    if (etag != null) {
        println("ETag: $etag")
        val isWeak = etag.startsWith("W/")
        val tagValue = etag.removePrefix("W/").trim('"')
        println("Weak ETag: $isWeak, Value: $tagValue")
    }
    
    // Vary header analysis
    val varyHeaders = message.vary()
    if (!varyHeaders.isNullOrEmpty()) {
        println("Varies by headers: ${varyHeaders.joinToString(", ")}")
    }
    
    // Cookie handling
    val cookies = message.setCookie()
    cookies.forEach { cookie ->
        println("Cookie: ${cookie.name}=${cookie.value}")
        println("  Domain: ${cookie.domain}")
        println("  Path: ${cookie.path}")
        println("  Secure: ${cookie.secure}")
        println("  HttpOnly: ${cookie.httpOnly}")
        if (cookie.maxAge > 0) {
            println("  Max-Age: ${cookie.maxAge} seconds")
        }
    }
    
    // Cache control analysis
    val cacheDirectives = message.cacheControl()
    cacheDirectives.forEach { directive ->
        println("Cache directive: ${directive.value}")
        directive.params.forEach { param ->
            println("  ${param.name}: ${param.value}")
        }
    }
}

Message Builder Convenience Extensions

Extension functions for setting common HTTP headers with validation.

/**
 * Set Max-Age cache control directive
 * @param seconds maximum age in seconds
 */
fun HttpMessageBuilder.maxAge(seconds: Int): Unit

/**
 * Set If-None-Match header for conditional requests
 * @param value ETag value to match against
 */
fun HttpMessageBuilder.ifNoneMatch(value: String): Unit

/**
 * Set User-Agent header
 * @param content user agent string
 */
fun HttpMessageBuilder.userAgent(content: String): Unit

Usage Examples:

import io.ktor.http.*

// Building requests with convenience methods
fun buildApiRequest(): HttpMessage {
    return HttpMessageBuilder().apply {
        // Set common headers
        contentType(ContentType.Application.Json.withCharset(Charsets.UTF_8))
        userAgent("MyAPI-Client/2.1.0 (Android)")
        
        // Conditional request
        ifNoneMatch("\"v2.1-abc123\"")
        
        // Caching
        maxAge(300) // 5 minutes
        
        // Verify settings
        require(contentType()?.match(ContentType.Application.Json) == true)
        require(charset() == Charsets.UTF_8)
        require(etag() == null) // If-None-Match is for requests, ETag is for responses
    }.build()
}

// Building responses with cache control
fun buildCachedResponse(data: String, etag: String): HttpMessage {
    return HttpMessageBuilder().apply {
        contentType(ContentType.Text.Plain.withCharset(Charsets.UTF_8))
        headers.set(HttpHeaders.ETag, "\"$etag\"")
        maxAge(3600) // 1 hour
        headers.append(HttpHeaders.CacheControl, "public")
        headers.set(HttpHeaders.Vary, "Accept-Encoding, Accept-Language")
        
        // Add content
        headers.set(HttpHeaders.ContentLength, data.toByteArray().size.toString())
        
        // Verify response headers
        val parsedEtag = etag()
        val varyHeaders = vary()
        val cacheControl = cacheControl()
        
        println("ETag: $parsedEtag")
        println("Varies by: ${varyHeaders?.joinToString(", ")}")
        println("Cache directives: ${cacheControl.map { it.value }}")
    }.build()
}

Advanced Cookie Handling

import io.ktor.http.*

// Parse and analyze cookies from message
fun analyzeCookies(message: HttpMessage) {
    val cookies = message.setCookie()
    
    cookies.forEach { cookie ->
        println("Cookie Analysis:")
        println("  Name: ${cookie.name}")
        println("  Value: ${cookie.value}")
        println("  Encoding: ${cookie.encoding}")
        
        // Security attributes
        val securityLevel = when {
            cookie.secure && cookie.httpOnly -> "High (Secure + HttpOnly)"
            cookie.secure -> "Medium (Secure only)"
            cookie.httpOnly -> "Medium (HttpOnly only)"
            else -> "Low (No security flags)"
        }
        println("  Security: $securityLevel")
        
        // Expiration analysis
        when {
            cookie.maxAge > 0 -> {
                val hours = cookie.maxAge / 3600
                val days = hours / 24
                when {
                    days > 0 -> println("  Expires: in $days days")
                    hours > 0 -> println("  Expires: in $hours hours")
                    else -> println("  Expires: in ${cookie.maxAge} seconds")
                }
            }
            cookie.expires != null -> {
                println("  Expires: ${cookie.expires}")
            }
            else -> println("  Expires: Session cookie")
        }
        
        // Scope
        println("  Domain: ${cookie.domain ?: "current domain"}")
        println("  Path: ${cookie.path ?: "/"}")
        
        // Extensions
        if (cookie.extensions.isNotEmpty()) {
            println("  Extensions:")
            cookie.extensions.forEach { (key, value) ->
                println("    $key${if (value != null) "=$value" else ""}")
            }
        }
    }
}

// Content type handling with charset detection
fun handleContentType(message: HttpMessage): Pair<ContentType?, Charset?> {
    val contentType = message.contentType()
    val charset = message.charset()
    
    // Handle different content types
    when {
        contentType?.match(ContentType.Application.Json) == true -> {
            val effectiveCharset = charset ?: Charsets.UTF_8
            println("JSON content with charset: ${effectiveCharset.name}")
            return contentType to effectiveCharset
        }
        
        contentType?.match(ContentType.Text.Any) == true -> {
            val effectiveCharset = charset ?: Charsets.UTF_8
            println("Text content with charset: ${effectiveCharset.name}")
            return contentType to effectiveCharset
        }
        
        contentType?.match(ContentType.Application.OctetStream) == true -> {
            println("Binary content (no charset)")
            return contentType to null
        }
        
        contentType?.match(ContentType.MultiPart.Any) == true -> {
            println("Multipart content")
            // Extract boundary parameter
            val boundary = contentType.parameters.find { it.name == "boundary" }?.value
            println("Boundary: $boundary")
            return contentType to charset
        }
        
        else -> {
            println("Unknown or no content type")
            return null to null
        }
    }
}

Types

/**
 * HTTP message interface for reading headers and properties
 */
interface HttpMessage {
    val headers: Headers
}

/**
 * HTTP message builder interface for constructing messages
 */
interface HttpMessageBuilder {
    val headers: HeadersBuilder
}

/**
 * Header value with parameters (used by cache control parsing)
 */
data class HeaderValue(
    val value: String,
    val params: List<HeaderValueParam> = emptyList()
)

/**
 * Header value parameter
 */
data class HeaderValueParam(
    val name: String,
    val value: String
)