Type-safe HTTP header and parameter handling with case-insensitive access, validation, and comprehensive builder patterns for managing HTTP headers and URL query parameters.
Base interface for both headers and parameters providing common functionality for key-value collections.
interface StringValues {
operator fun get(name: String): String?
fun getAll(name: String): List<String>?
fun names(): Set<String>
fun isEmpty(): Boolean
fun entries(): Set<Map.Entry<String, List<String>>>
fun forEach(body: (String, List<String>) -> Unit)
companion object {
val Empty: StringValues
}
}interface Headers : StringValues {
operator fun get(name: String): String?
fun getAll(name: String): List<String>?
fun contains(name: String): Boolean
fun contains(name: String, value: String): Boolean
companion object {
val Empty: Headers
}
}// Factory functions
fun headersOf(): Headers
fun headersOf(vararg pairs: Pair<String, String>): Headers
fun headersOf(vararg pairs: Pair<String, List<String>>): Headers
fun headersOf(map: Map<String, String>): Headers
fun headersOf(map: Map<String, List<String>>): Headers
// Builder DSL
fun headers(builder: HeadersBuilder.() -> Unit): Headersclass HeadersBuilder : StringValuesBuilder {
fun append(name: String, value: String)
fun append(name: String, values: List<String>)
fun appendAll(stringValues: StringValues)
fun appendAll(other: Headers)
fun appendMissing(stringValues: StringValues)
fun appendMissing(other: Headers)
fun set(name: String, value: String)
fun set(name: String, values: List<String>)
fun remove(name: String): List<String>
fun removeKeysWithNoEntries()
fun clear()
fun build(): Headers
}object HttpHeaders {
// Request Headers
const val Accept: String = "Accept"
const val AcceptCharset: String = "Accept-Charset"
const val AcceptEncoding: String = "Accept-Encoding"
const val AcceptLanguage: String = "Accept-Language"
const val AcceptRanges: String = "Accept-Ranges"
const val Authorization: String = "Authorization"
const val CacheControl: String = "Cache-Control"
const val Connection: String = "Connection"
const val ContentEncoding: String = "Content-Encoding"
const val ContentLength: String = "Content-Length"
const val ContentType: String = "Content-Type"
const val Cookie: String = "Cookie"
const val Host: String = "Host"
const val IfMatch: String = "If-Match"
const val IfModifiedSince: String = "If-Modified-Since"
const val IfNoneMatch: String = "If-None-Match"
const val IfRange: String = "If-Range"
const val IfUnmodifiedSince: String = "If-Unmodified-Since"
const val Origin: String = "Origin"
const val Range: String = "Range"
const val Referer: String = "Referer"
const val UserAgent: String = "User-Agent"
// Response Headers
const val AcceptPatch: String = "Accept-Patch"
const val AccessControlAllowCredentials: String = "Access-Control-Allow-Credentials"
const val AccessControlAllowHeaders: String = "Access-Control-Allow-Headers"
const val AccessControlAllowMethods: String = "Access-Control-Allow-Methods"
const val AccessControlAllowOrigin: String = "Access-Control-Allow-Origin"
const val AccessControlExposeHeaders: String = "Access-Control-Expose-Headers"
const val AccessControlMaxAge: String = "Access-Control-Max-Age"
const val Age: String = "Age"
const val Allow: String = "Allow"
const val ContentDisposition: String = "Content-Disposition"
const val ContentRange: String = "Content-Range"
const val ETag: String = "ETag"
const val Expires: String = "Expires"
const val LastModified: String = "Last-Modified"
const val Link: String = "Link"
const val Location: String = "Location"
const val Server: String = "Server"
const val SetCookie: String = "Set-Cookie"
const val Vary: String = "Vary"
const val WWWAuthenticate: String = "WWW-Authenticate"
// Header validation
fun isUnsafe(header: String): Boolean
fun checkHeaderName(name: String)
fun checkHeaderValue(value: String)
val UnsafeHeadersList: List<String>
}interface Parameters : StringValues {
operator fun get(name: String): String?
fun getAll(name: String): List<String>?
companion object {
val Empty: Parameters
}
}// Factory functions
fun parametersOf(): Parameters
fun parametersOf(vararg pairs: Pair<String, String>): Parameters
fun parametersOf(vararg pairs: Pair<String, List<String>>): Parameters
fun parametersOf(map: Map<String, String>): Parameters
fun parametersOf(map: Map<String, List<String>>): Parameters
// Builder DSL
fun parameters(builder: ParametersBuilder.() -> Unit): Parametersinterface ParametersBuilder : StringValuesBuilder {
fun append(name: String, value: String)
fun append(name: String, values: List<String>)
fun appendAll(stringValues: StringValues)
fun appendAll(other: Parameters)
fun appendMissing(stringValues: StringValues)
fun appendMissing(other: Parameters)
fun set(name: String, value: String)
fun set(name: String, values: List<String>)
fun remove(name: String): List<String>
fun removeKeysWithNoEntries()
fun clear()
fun build(): Parameters
}class UrlDecodedParametersBuilder : ParametersBuilder {
constructor(capacity: Int = 8)
// Specialized for URL-encoded form data
fun appendAll(encodedQueryString: String)
fun appendAll(encodedQueryString: String, decode: Boolean)
}
fun parseQueryString(query: String, decode: Boolean = true): Parameters// Create headers using factory functions
val simpleHeaders = headersOf(
"Content-Type" to "application/json",
"User-Agent" to "MyApp/1.0"
)
// Create headers with multiple values
val complexHeaders = headersOf(
"Accept" to listOf("application/json", "application/xml"),
"Cache-Control" to listOf("no-cache", "no-store")
)
// Using headers builder
val builtHeaders = headers {
append(HttpHeaders.ContentType, "text/html")
append(HttpHeaders.ContentLength, "1234")
append(HttpHeaders.CacheControl, "max-age=3600")
append(HttpHeaders.CacheControl, "public") // Multiple values
}
// Accessing header values
val contentType = headers[HttpHeaders.ContentType]
val allCacheControls = headers.getAll(HttpHeaders.CacheControl)
val hasContentLength = headers.contains(HttpHeaders.ContentLength)val builder = HeadersBuilder()
// Adding headers
builder.append("X-Custom-Header", "value1")
builder.append("X-Custom-Header", "value2") // Multiple values
builder.set("Content-Type", "application/json") // Replace existing
// Adding from other sources
val existingHeaders = headersOf("Authorization" to "Bearer token123")
builder.appendAll(existingHeaders)
// Conditional adding
builder.appendMissing(headersOf("User-Agent" to "DefaultAgent"))
// Removing headers
builder.remove("X-Debug-Header")
builder.removeKeysWithNoEntries()
val finalHeaders = builder.build()// Create parameters
val queryParams = parametersOf(
"page" to "1",
"limit" to "10",
"sort" to listOf("name", "date")
)
// Using parameters builder
val searchParams = parameters {
append("q", "kotlin")
append("type", "library")
append("category", "web")
append("category", "mobile") // Multiple categories
}
// Accessing parameter values
val page = queryParams["page"]
val categories = queryParams.getAll("category")
val isEmpty = queryParams.isEmpty()
// URL-encoded parameters from query string
val parsedParams = parseQueryString("q=kotlin%20http&page=1&sort=name&sort=date")
println(parsedParams["q"]) // "kotlin http"
println(parsedParams.getAll("sort")) // ["name", "date"]val urlBuilder = UrlDecodedParametersBuilder()
// Parse from query string
urlBuilder.appendAll("name=John%20Doe&age=25&skills=kotlin&skills=android")
// Add additional parameters
urlBuilder.append("location", "Remote")
urlBuilder.set("verified", "true")
val parameters = urlBuilder.build()
println(parameters["name"]) // "John Doe"
println(parameters.getAll("skills")) // ["kotlin", "android"]// Iterate over headers
headers.forEach { name, values ->
println("$name: ${values.joinToString(", ")}")
}
// Get all header names
val headerNames = headers.names()
println("Headers present: $headerNames")
// Get all entries
val headerEntries = headers.entries()
headerEntries.forEach { (name, values) ->
values.forEach { value ->
println("$name: $value")
}
}
// Same operations work for parameters
parameters.forEach { name, values ->
println("Parameter $name: ${values.joinToString(", ")}")
}// Check for unsafe headers
val headerName = "Host"
if (HttpHeaders.isUnsafe(headerName)) {
println("$headerName is considered unsafe")
}
// Validate header names and values
try {
HttpHeaders.checkHeaderName("Invalid Header Name!") // Throws exception
} catch (e: IllegalHeaderNameException) {
println("Invalid header name: ${e.message}")
}
try {
HttpHeaders.checkHeaderValue("Header value with\r\n injection") // Throws exception
} catch (e: IllegalHeaderValueException) {
println("Invalid header value: ${e.message}")
}
// Get list of unsafe headers
val unsafeHeaders = HttpHeaders.UnsafeHeadersList
println("Unsafe headers: $unsafeHeaders")// Merging headers from multiple sources
fun mergeHeaders(vararg headerSets: Headers): Headers {
return headers {
headerSets.forEach { headerSet ->
appendAll(headerSet)
}
}
}
// Filtering headers
fun filterHeaders(headers: Headers, predicate: (String) -> Boolean): Headers {
return headers {
headers.entries().forEach { (name, values) ->
if (predicate(name)) {
values.forEach { value ->
append(name, value)
}
}
}
}
}
// Converting parameters to query string
fun parametersToQueryString(parameters: Parameters): String {
return parameters.entries().joinToString("&") { (name, values) ->
values.joinToString("&") { value ->
"${name.encodeURLQueryComponent()}=${value.encodeURLQueryComponent()}"
}
}
}
// Usage examples
val corsHeaders = headersOf(
HttpHeaders.AccessControlAllowOrigin to "*",
HttpHeaders.AccessControlAllowMethods to "GET, POST, PUT, DELETE"
)
val authHeaders = headersOf(
HttpHeaders.Authorization to "Bearer token123",
HttpHeaders.UserAgent to "MyApp/2.0"
)
val combinedHeaders = mergeHeaders(corsHeaders, authHeaders)
val publicHeaders = filterHeaders(combinedHeaders) { name ->
!name.equals(HttpHeaders.Authorization, ignoreCase = true)
}