CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/kotlin-io-ktor--ktor-http-iosarm64

Ktor HTTP library for iOS ARM64 providing HTTP utilities, content types, headers manipulation, URL building, and HTTP message handling for iOS ARM64 applications built with Kotlin Multiplatform.

Pending
Overview
Eval results
Files

header-parsing.mddocs/

Header Value Parsing

Comprehensive HTTP header value parsing utilities for handling complex header formats with parameters, quality values, content type negotiation, and multi-value headers according to HTTP specifications.

Capabilities

Header Value Representation

Core data classes for representing parsed header values with parameters and quality scoring.

/**
 * Represents a single header value with optional parameters
 * @param value the main header value
 * @param params list of parameters associated with this value
 */
data class HeaderValue(
    val value: String,
    val params: List<HeaderValueParam> = emptyList()
) {
    /**
     * Quality value from 'q' parameter, defaults to 1.0 if missing or invalid
     * Used for content negotiation and priority ordering
     */
    val quality: Double
}

/**
 * Represents a header parameter
 * @param name parameter name (case-insensitive)
 * @param value parameter value
 * @param escapeValue whether the value should be escaped when serialized
 */
data class HeaderValueParam(
    val name: String, 
    val value: String, 
    val escapeValue: Boolean = false
)

Usage Examples:

import io.ktor.http.*

// Create header values manually
val jsonHeader = HeaderValue(
    value = "application/json",
    params = listOf(
        HeaderValueParam("charset", "utf-8"),
        HeaderValueParam("boundary", "abc123", escapeValue = true)
    )
)
println(jsonHeader.quality) // 1.0 (default)

val xmlHeader = HeaderValue(
    value = "application/xml",
    params = listOf(HeaderValueParam("q", "0.8"))
)
println(xmlHeader.quality) // 0.8

// Parameter comparison (case-insensitive)
val param1 = HeaderValueParam("Content-Type", "application/json")
val param2 = HeaderValueParam("content-type", "APPLICATION/JSON")
println(param1 == param2) // true

Basic Header Parsing

Parse header values supporting multiple values separated by commas and parameters.

/**
 * Parse header value string into list of HeaderValue objects
 * @param text header value string to parse
 * @return list of parsed header values
 */
fun parseHeaderValue(text: String?): List<HeaderValue>

/**
 * Parse header value with control over value parsing
 * @param text header value string to parse
 * @param parametersOnly if true, parse only parameters without main value
 * @return list of parsed header values
 */
fun parseHeaderValue(text: String?, parametersOnly: Boolean): List<HeaderValue>

Usage Examples:

import io.ktor.http.*

// Parse simple header
val simpleHeader = parseHeaderValue("application/json")
println(simpleHeader[0].value) // "application/json"
println(simpleHeader[0].params.size) // 0

// Parse header with parameters
val withParams = parseHeaderValue("text/html; charset=utf-8; boundary=abc123")
val headerValue = withParams[0]
println(headerValue.value) // "text/html"
println(headerValue.params.size) // 2
println(headerValue.params[0].name) // "charset"
println(headerValue.params[0].value) // "utf-8"

// Parse multiple values
val multiValue = parseHeaderValue("gzip, deflate, br")
println(multiValue.size) // 3
println(multiValue[0].value) // "gzip"
println(multiValue[1].value) // "deflate"
println(multiValue[2].value) // "br"

// Parse complex Accept header
val acceptHeader = parseHeaderValue("text/html; q=0.9, application/json; q=0.8, */*; q=0.1")
println(acceptHeader.size) // 3
println(acceptHeader[0].value) // "text/html"
println(acceptHeader[0].quality) // 0.9
println(acceptHeader[1].value) // "application/json"
println(acceptHeader[1].quality) // 0.8

// Parse quoted parameter values
val quotedParams = parseHeaderValue("attachment; filename=\"my file.txt\"; size=1024")
val attachment = quotedParams[0]
println(attachment.params.find { it.name == "filename" }?.value) // "my file.txt"
println(attachment.params.find { it.name == "size" }?.value) // "1024"

// Handle escaped quotes
val escapedQuotes = parseHeaderValue("text; value=\"He said \\\"Hello\\\"\"")
val textValue = escapedQuotes[0]
println(textValue.params[0].value) // "He said \"Hello\""

Quality-Based Sorting

Parse and sort header values by quality for content negotiation.

/**
 * Parse header value and sort by quality (q parameter) in descending order
 * Higher quality values appear first in the result
 * @param header header value string to parse
 * @return list of HeaderValue objects sorted by quality
 */
fun parseAndSortHeader(header: String?): List<HeaderValue>

Usage Examples:

import io.ktor.http.*

// Parse and sort Accept header
val acceptHeader = "text/html; q=0.5, application/json, text/plain; q=0.8, */*; q=0.1"
val sortedValues = parseAndSortHeader(acceptHeader)

println("Sorted by preference:")
sortedValues.forEach { headerValue ->
    println("${headerValue.value} (q=${headerValue.quality})")
}
// Output:
// application/json (q=1.0)
// text/plain (q=0.8)
// text/html (q=0.5)
// */* (q=0.1)

// Parse Accept-Language with quality
val languageHeader = "en-US; q=0.9, en; q=0.8, fr; q=0.7, *; q=0.1"
val sortedLanguages = parseAndSortHeader(languageHeader)
println("Preferred languages:")
sortedLanguages.forEach { lang ->
    println("${lang.value} (preference: ${lang.quality})")
}

// Parse Accept-Encoding
val encodingHeader = "gzip; q=1.0, deflate; q=0.8, identity; q=0.5"
val sortedEncodings = parseAndSortHeader(encodingHeader)
val preferredEncoding = sortedEncodings.firstOrNull()?.value
println("Preferred encoding: $preferredEncoding") // "gzip"

Content Type Negotiation

Special parsing for Content-Type headers with sophisticated sorting by quality, specificity, and parameter count.

/**
 * Parse Content-Type header values and sort by preference
 * Sorting criteria (in order):
 * 1. Quality value (q parameter) - higher first
 * 2. Specificity - fewer wildcards first  
 * 3. Parameter count - more parameters first
 * @param header Content-Type header value
 * @return list of HeaderValue objects sorted by negotiation preference
 */
fun parseAndSortContentTypeHeader(header: String?): List<HeaderValue>

Usage Examples:

import io.ktor.http.*

// Parse complex Accept header for content negotiation
val acceptHeader = "text/html; q=0.9, application/*; q=0.8, text/*; q=0.7, */*; q=0.1, application/json"
val negotiated = parseAndSortContentTypeHeader(acceptHeader)

println("Content type negotiation order:")
negotiated.forEach { ct ->
    println("${ct.value} (q=${ct.quality})")
}
// Output prioritizes:
// 1. application/json (q=1.0, most specific)
// 2. text/html (q=0.9, specific)
// 3. application/* (q=0.8, wildcard subtype)
// 4. text/* (q=0.7, wildcard subtype)  
// 5. */* (q=0.1, wildcard type)

// Content negotiation with parameters
val complexAccept = "application/json; charset=utf-8; q=0.9, application/xml; q=0.8, text/plain"
val sorted = parseAndSortContentTypeHeader(complexAccept)

// Find best match for available content types
val availableTypes = listOf("application/json", "text/plain", "application/xml")
val bestMatch = sorted.firstOrNull { headerValue ->
    availableTypes.any { available ->
        ContentType.parse(headerValue.value).match(ContentType.parse(available))
    }
}
println("Best match: ${bestMatch?.value}") // "text/plain" (q=1.0)

// Handle wildcard matching
fun findBestContentType(acceptHeader: String, availableTypes: List<String>): String? {
    val parsed = parseAndSortContentTypeHeader(acceptHeader)
    
    for (accepted in parsed) {
        val acceptedType = ContentType.parse(accepted.value)
        
        for (available in availableTypes) {
            val availableType = ContentType.parse(available)
            if (availableType.match(acceptedType)) {
                return available
            }
        }
    }
    
    return null
}

val available = listOf("application/json", "text/html", "image/png")
val clientAccepts = "text/*; q=0.8, application/json; q=0.9, */*; q=0.1"
val chosen = findBestContentType(clientAccepts, available)
println("Selected: $chosen") // "application/json"

Parameter Utilities

Helper functions for working with header parameters.

/**
 * Convert pairs to HeaderValueParam list
 * @return list of HeaderValueParam objects
 */
fun Iterable<Pair<String, String>>.toHeaderParamsList(): List<HeaderValueParam>

Usage Examples:

import io.ktor.http.*

// Create parameters from pairs
val paramPairs = listOf(
    "charset" to "utf-8",
    "boundary" to "----WebKitFormBoundary",
    "q" to "0.8"
)
val params = paramPairs.toHeaderParamsList()

// Use in HeaderValue
val contentType = HeaderValue("multipart/form-data", params)
println(contentType.quality) // 0.8
println(contentType.params.find { it.name == "boundary" }?.value) // "----WebKitFormBoundary"

// Combine with manual parameter creation
val mixedParams = listOf(
    HeaderValueParam("type", "image"),
    HeaderValueParam("quality", "high", escapeValue = true)
) + listOf("format" to "png", "compression" to "lossless").toHeaderParamsList()

val imageHeader = HeaderValue("image/png", mixedParams)
println(imageHeader.params.size) // 4

Advanced Header Parsing Examples

import io.ktor.http.*

// Complete content negotiation system
class ContentNegotiator {
    fun negotiate(acceptHeader: String?, availableTypes: List<String>): String? {
        if (acceptHeader == null) return availableTypes.firstOrNull()
        
        val parsed = parseAndSortContentTypeHeader(acceptHeader)
        
        for (accepted in parsed) {
            val acceptedType = try {
                ContentType.parse(accepted.value)
            } catch (e: Exception) {
                continue
            }
            
            for (available in availableTypes) {
                val availableType = try {
                    ContentType.parse(available)
                } catch (e: Exception) {
                    continue
                }
                
                if (matches(acceptedType, availableType)) {
                    return available
                }
            }
        }
        
        return null
    }
    
    private fun matches(accepted: ContentType, available: ContentType): Boolean {
        return when {
            accepted.contentType == "*" -> true
            accepted.contentType != available.contentType -> false
            accepted.contentSubtype == "*" -> true
            else -> accepted.contentSubtype == available.contentSubtype
        }
    }
}

// Language preference parsing
fun parseLanguagePreferences(acceptLanguage: String?): List<String> {
    if (acceptLanguage == null) return emptyList()
    
    return parseAndSortHeader(acceptLanguage)
        .map { it.value.lowercase() }
        .filter { it != "*" }
}

// Cache control directive parsing
fun parseCacheControl(cacheControl: String?): Map<String, String?> {
    if (cacheControl == null) return emptyMap()
    
    return parseHeaderValue(cacheControl, parametersOnly = true)
        .flatMap { it.params }
        .associate { param ->
            param.name.lowercase() to param.value.takeIf { it.isNotEmpty() }
        }
}

// Usage examples
val negotiator = ContentNegotiator()
val result = negotiator.negotiate(
    "application/json; q=0.9, text/html; q=0.8, */*; q=0.1",
    listOf("text/html", "application/xml", "text/plain")
)
println("Negotiated: $result") // "text/html"

val languages = parseLanguagePreferences("en-US; q=0.9, en; q=0.8, fr; q=0.5")
println("Languages: $languages") // ["en-us", "en", "fr"]

val cacheDirectives = parseCacheControl("public, max-age=3600, no-transform")
println("Max age: ${cacheDirectives["max-age"]}") // "3600"
println("Is public: ${cacheDirectives.containsKey("public")}") // true

// Complex multipart boundary extraction
fun extractBoundary(contentType: String): String? {
    val parsed = parseHeaderValue(contentType).firstOrNull() ?: return null
    
    return if (parsed.value.startsWith("multipart/")) {
        parsed.params.find { it.name.equals("boundary", ignoreCase = true) }?.value
    } else null
}

val multipartHeader = "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"
val boundary = extractBoundary(multipartHeader)
println("Boundary: $boundary") // "----WebKitFormBoundary7MA4YWxkTrZu0gW"

Types

/**
 * Header value with parameters and automatic quality calculation
 */
data class HeaderValue(
    val value: String,
    val params: List<HeaderValueParam> = emptyList()
) {
    val quality: Double // Automatically calculated from 'q' parameter
}

/**
 * Header parameter with case-insensitive name comparison
 */
data class HeaderValueParam(
    val name: String,
    val value: String,
    val escapeValue: Boolean = false
)

Install with Tessl CLI

npx tessl i tessl/kotlin-io-ktor--ktor-http-iosarm64

docs

authentication.md

content-processing.md

content-types.md

cookies.md

date-utilities.md

header-parsing.md

headers.md

http-message-extensions.md

http-methods-status.md

index.md

multipart.md

parameters.md

url-encoding.md

url-handling.md

tile.json