Complete cookie support including parsing Set-Cookie and Cookie headers, cookie encoding strategies, cookie attribute handling, and comprehensive cookie management utilities.
data class Cookie(
val name: String,
val value: String,
val encoding: CookieEncoding = CookieEncoding.URI_ENCODING,
val maxAge: Int = 0,
val expires: GMTDate? = null,
val domain: String? = null,
val path: String? = null,
val secure: Boolean = false,
val httpOnly: Boolean = false,
val extensions: Map<String, String?> = emptyMap()
)Cookie properties:
enum class CookieEncoding {
RAW, // No encoding applied
DQUOTES, // Double-quote wrapping when needed
URI_ENCODING, // Percent-encoding (default)
BASE64_ENCODING // Base64 encoding
}fun parseServerSetCookieHeader(cookiesHeader: String): CookieParses a single Set-Cookie header value into a Cookie object.
fun parseClientCookiesHeader(cookiesHeader: String): List<Cookie>Parses a Cookie header value (multiple cookies) into a list of Cookie objects.
fun renderSetCookieHeader(cookie: Cookie): StringGenerates a Set-Cookie header value from a Cookie object.
fun renderCookieHeader(cookies: List<Cookie>): StringGenerates a Cookie header value from a list of Cookie objects.
fun encodeCookieValue(value: String, encoding: CookieEncoding): String
fun decodeCookieValue(value: String, encoding: CookieEncoding): StringManually encode and decode cookie values using specific encoding strategies.
// Simple session cookie
val sessionCookie = Cookie(
name = "JSESSIONID",
value = "ABC123DEF456"
)
// Persistent cookie with expiration
val rememberMeCookie = Cookie(
name = "remember_token",
value = "user123_token456",
maxAge = 30 * 24 * 60 * 60, // 30 days
secure = true,
httpOnly = true
)
// Cookie with domain and path restrictions
val trackingCookie = Cookie(
name = "tracking_id",
value = "track_12345",
domain = ".example.com",
path = "/app",
maxAge = 365 * 24 * 60 * 60 // 1 year
)// Different encoding strategies
val rawCookie = Cookie(
name = "raw_data",
value = "simple_value",
encoding = CookieEncoding.RAW
)
val quotedCookie = Cookie(
name = "quoted_data",
value = "value with spaces",
encoding = CookieEncoding.DQUOTES
)
val uriEncodedCookie = Cookie(
name = "uri_data",
value = "value/with/special&chars=here",
encoding = CookieEncoding.URI_ENCODING
)
val base64Cookie = Cookie(
name = "b64_data",
value = "complex data with unicode: 🍪",
encoding = CookieEncoding.BASE64_ENCODING
)
// Render as Set-Cookie headers
println(renderSetCookieHeader(rawCookie))
// raw_data=simple_value
println(renderSetCookieHeader(quotedCookie))
// quoted_data="value with spaces"
println(renderSetCookieHeader(uriEncodedCookie))
// uri_data=value%2Fwith%2Fspecial%26chars%3Dhere
println(renderSetCookieHeader(base64Cookie))
// b64_data=Y29tcGxleCBkYXRhIHdpdGggdW5pY29kZTogIPCfjYo=// Cookie with all security attributes
val secureCookie = Cookie(
name = "secure_session",
value = "encrypted_session_data",
maxAge = 3600, // 1 hour
domain = "secure.example.com",
path = "/api",
secure = true,
httpOnly = true
)
// Cookie with custom extensions
val customCookie = Cookie(
name = "custom_cookie",
value = "data",
extensions = mapOf(
"SameSite" to "Strict",
"Priority" to "High",
"CustomAttribute" to null // attribute without value
)
)
val customHeader = renderSetCookieHeader(customCookie)
println(customHeader)
// custom_cookie=data; SameSite=Strict; Priority=High; CustomAttribute// Parse various Set-Cookie header formats
val setCookieHeaders = listOf(
"session_id=ABC123; Max-Age=3600; Path=/; Secure; HttpOnly",
"preferences=theme:dark,lang:en; Domain=.example.com; Max-Age=2592000",
"tracking=\"visitor_12345\"; Expires=Wed, 21 Oct 2024 07:28:00 GMT; Path=/",
"minimal=value",
"complex=data; Max-Age=86400; Domain=.site.com; Path=/app; Secure; HttpOnly; SameSite=Strict"
)
setCookieHeaders.forEach { header ->
val cookie = parseServerSetCookieHeader(header)
println("Parsed: ${cookie.name}=${cookie.value}")
println(" Max-Age: ${cookie.maxAge}")
println(" Domain: ${cookie.domain}")
println(" Path: ${cookie.path}")
println(" Secure: ${cookie.secure}")
println(" HttpOnly: ${cookie.httpOnly}")
println(" Extensions: ${cookie.extensions}")
println()
}// Parse Cookie header from client requests
val cookieHeaderValues = listOf(
"session_id=ABC123; user_pref=theme:dark; tracking=visitor_12345",
"single_cookie=value",
"empty_value=; another=test",
"complex=\"quoted value\"; simple=unquoted"
)
cookieHeaderValues.forEach { header ->
val cookies = parseClientCookiesHeader(header)
println("Parsed ${cookies.size} cookies from: $header")
cookies.forEach { cookie ->
println(" ${cookie.name} = ${cookie.value}")
}
println()
}// Helper functions for cookie management
class CookieManager {
private val cookies = mutableMapOf<String, Cookie>()
fun setCookie(cookie: Cookie) {
cookies[cookie.name] = cookie
}
fun getCookie(name: String): Cookie? {
return cookies[name]
}
fun removeCookie(name: String) {
cookies.remove(name)
}
fun getAllCookies(): List<Cookie> {
return cookies.values.toList()
}
fun clearExpiredCookies() {
val now = System.currentTimeMillis()
cookies.entries.removeAll { (_, cookie) ->
cookie.expires?.let { expires ->
expires.timestamp < now
} ?: false
}
}
fun generateCookieHeader(): String {
return renderCookieHeader(getAllCookies())
}
fun generateSetCookieHeaders(): List<String> {
return getAllCookies().map { renderSetCookieHeader(it) }
}
}
// Usage
val manager = CookieManager()
manager.setCookie(Cookie("session", "ABC123", maxAge = 3600))
manager.setCookie(Cookie("theme", "dark", maxAge = 86400))
manager.setCookie(Cookie("lang", "en", maxAge = 86400))
val cookieHeader = manager.generateCookieHeader()
println("Cookie: $cookieHeader")
// Cookie: session=ABC123; theme=dark; lang=en
val setCookieHeaders = manager.generateSetCookieHeaders()
setCookieHeaders.forEach { header ->
println("Set-Cookie: $header")
}// Cookie factory functions
object CookieFactory {
fun createSessionCookie(name: String, value: String): Cookie {
return Cookie(
name = name,
value = value,
httpOnly = true,
secure = true,
encoding = CookieEncoding.URI_ENCODING
)
}
fun createPersistentCookie(
name: String,
value: String,
daysToExpire: Int
): Cookie {
return Cookie(
name = name,
value = value,
maxAge = daysToExpire * 24 * 60 * 60,
httpOnly = true,
secure = true,
encoding = CookieEncoding.URI_ENCODING
)
}
fun createTrackingCookie(
trackingId: String,
domain: String
): Cookie {
return Cookie(
name = "tracking_id",
value = trackingId,
domain = domain,
path = "/",
maxAge = 365 * 24 * 60 * 60, // 1 year
secure = true,
extensions = mapOf("SameSite" to "Lax")
)
}
fun createDeleteCookie(name: String, domain: String? = null, path: String? = null): Cookie {
return Cookie(
name = name,
value = "",
maxAge = 0,
expires = GMTDate(0), // Set to epoch
domain = domain,
path = path
)
}
}
// Usage examples
val sessionCookie = CookieFactory.createSessionCookie("PHPSESSID", "s3cr3t_s3ss10n")
val rememberCookie = CookieFactory.createPersistentCookie("remember_me", "user123", 30)
val trackingCookie = CookieFactory.createTrackingCookie("track_abc123", ".example.com")
val deleteCookie = CookieFactory.createDeleteCookie("old_session", ".example.com", "/")// Security-focused cookie creation
fun createSecureCookie(
name: String,
value: String,
maxAgeSeconds: Int = 3600,
domain: String? = null,
path: String = "/"
): Cookie {
return Cookie(
name = name,
value = value,
maxAge = maxAgeSeconds,
domain = domain,
path = path,
secure = true, // HTTPS only
httpOnly = true, // No JavaScript access
encoding = CookieEncoding.URI_ENCODING,
extensions = mapOf(
"SameSite" to "Strict" // CSRF protection
)
)
}
// JWT cookie handling
fun createJwtCookie(token: String): Cookie {
return createSecureCookie(
name = "jwt_token",
value = token,
maxAgeSeconds = 15 * 60, // 15 minutes
extensions = mapOf(
"SameSite" to "Strict",
"Priority" to "High"
)
)
}
// CSRF token cookie
fun createCsrfCookie(token: String): Cookie {
return Cookie(
name = "csrf_token",
value = token,
secure = true,
httpOnly = false, // Accessible to JavaScript for CSRF headers
maxAge = 3600,
extensions = mapOf("SameSite" to "Strict")
)
}// Manual encoding/decoding
val originalValue = "user data with spaces & special chars = here"
// Try different encodings
val rawEncoded = encodeCookieValue(originalValue, CookieEncoding.RAW)
val uriEncoded = encodeCookieValue(originalValue, CookieEncoding.URI_ENCODING)
val base64Encoded = encodeCookieValue(originalValue, CookieEncoding.BASE64_ENCODING)
val quotedEncoded = encodeCookieValue(originalValue, CookieEncoding.DQUOTES)
println("Original: $originalValue")
println("Raw: $rawEncoded")
println("URI: $uriEncoded")
println("Base64: $base64Encoded")
println("Quoted: $quotedEncoded")
// Decode back
val uriDecoded = decodeCookieValue(uriEncoded, CookieEncoding.URI_ENCODING)
val base64Decoded = decodeCookieValue(base64Encoded, CookieEncoding.BASE64_ENCODING)
println("URI decoded: $uriDecoded")
println("Base64 decoded: $base64Decoded")
println("Matches original: ${originalValue == uriDecoded}")// Safe cookie parsing
fun parseCookieSafe(header: String): List<Cookie> {
return try {
parseClientCookiesHeader(header)
} catch (e: Exception) {
println("Failed to parse cookie header: ${e.message}")
emptyList()
}
}
fun parseSetCookieSafe(header: String): Cookie? {
return try {
parseServerSetCookieHeader(header)
} catch (e: Exception) {
println("Failed to parse Set-Cookie header: ${e.message}")
null
}
}
// Validate cookie values
fun isValidCookieName(name: String): Boolean {
return name.isNotBlank() && !name.contains(Regex("[\\s,;=]"))
}
fun isValidCookieValue(value: String, encoding: CookieEncoding): Boolean {
return try {
val encoded = encodeCookieValue(value, encoding)
val decoded = decodeCookieValue(encoded, encoding)
decoded == value
} catch (e: Exception) {
false
}
}
// Usage
val validName = isValidCookieName("session_id") // true
val invalidName = isValidCookieName("session id") // false (space)
val validValue = isValidCookieValue("simple_value", CookieEncoding.URI_ENCODING) // true
val complexValue = isValidCookieValue("value with unicode 🍪", CookieEncoding.BASE64_ENCODING) // true