Ktor HTTP Client Core for tvOS ARM64 - multiplatform asynchronous HTTP client library with coroutines support
—
The Ktor HTTP Client Core provides comprehensive cookie management functionality through the HttpCookies plugin. This allows automatic handling of HTTP cookies across requests with configurable storage backends and support for custom cookie storage implementations.
The main plugin for cookie management that automatically handles cookie storage and retrieval.
object HttpCookies : HttpClientPlugin<HttpCookies.Config, HttpCookies> {
class Config {
var storage: CookiesStorage = AcceptAllCookiesStorage()
fun default()
fun storage(storage: CookiesStorage)
}
}Base interface for implementing custom cookie storage backends.
interface CookiesStorage {
suspend fun get(requestUrl: Url): List<Cookie>
suspend fun addCookie(requestUrl: Url, cookie: Cookie)
fun close()
}Default storage implementation that accepts and stores all cookies with in-memory storage.
class AcceptAllCookiesStorage : CookiesStorage {
override suspend fun get(requestUrl: Url): List<Cookie>
override suspend fun addCookie(requestUrl: Url, cookie: Cookie)
override fun close()
}Read-only storage implementation that provides a fixed set of cookies for all requests.
class ConstantCookiesStorage(
private val cookies: List<Cookie>
) : CookiesStorage {
constructor(vararg cookies: Cookie)
override suspend fun get(requestUrl: Url): List<Cookie>
override suspend fun addCookie(requestUrl: Url, cookie: Cookie)
override fun close()
}Represents an HTTP cookie with all its attributes.
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()
) {
fun copy(
name: String = this.name,
value: String = this.value,
encoding: CookieEncoding = this.encoding,
maxAge: Int = this.maxAge,
expires: GMTDate? = this.expires,
domain: String? = this.domain,
path: String? = this.path,
secure: Boolean = this.secure,
httpOnly: Boolean = this.httpOnly,
extensions: Map<String, String?> = this.extensions
): Cookie
}
enum class CookieEncoding {
URI_ENCODING,
DQUOTES,
RAW
}val client = HttpClient {
install(HttpCookies)
}
// First request - server sets cookies
val loginResponse = client.post("https://example.com/login") {
setBody("username=john&password=secret")
contentType(ContentType.Application.FormUrlEncoded)
}
// Subsequent requests automatically include cookies
val profileResponse = client.get("https://example.com/profile")
// Cookies from login are automatically sent
client.close()val client = HttpClient {
install(HttpCookies) {
storage = AcceptAllCookiesStorage()
}
}
// Or with constant cookies
val client2 = HttpClient {
install(HttpCookies) {
storage = ConstantCookiesStorage(
Cookie("session_id", "abc123"),
Cookie("user_pref", "dark_mode")
)
}
}val cookieStorage = AcceptAllCookiesStorage()
val client = HttpClient {
install(HttpCookies) {
storage = cookieStorage
}
}
// Add cookies manually
cookieStorage.addCookie(
Url("https://example.com"),
Cookie(
name = "auth_token",
value = "bearer_xyz789",
domain = "example.com",
path = "/api",
secure = true,
httpOnly = true,
maxAge = 3600 // 1 hour
)
)
// Cookies will be included in matching requests
val response = client.get("https://example.com/api/data")val secureCookie = Cookie(
name = "secure_session",
value = "encrypted_data",
domain = ".example.com", // Available to all subdomains
path = "/secure", // Only for /secure paths
secure = true, // HTTPS only
httpOnly = true, // No JavaScript access
maxAge = 7200, // 2 hours lifetime
extensions = mapOf(
"SameSite" to "Strict" // CSRF protection
)
)Cookies are automatically filtered based on domain and path matching rules:
val client = HttpClient {
install(HttpCookies) {
storage = AcceptAllCookiesStorage()
}
}
// Set cookies with different scopes
client.get("https://api.example.com/login") // May set cookies for .example.com
client.get("https://app.example.com/dashboard") // Gets cookies for .example.com
client.get("https://other.com/data") // No cookies from example.comval encodedCookie = Cookie(
name = "special_chars",
value = "hello world & more!",
encoding = CookieEncoding.URI_ENCODING // URL-encodes special characters
)
val quotedCookie = Cookie(
name = "quoted_value",
value = "contains spaces",
encoding = CookieEncoding.DQUOTES // Wraps in double quotes
)
val rawCookie = Cookie(
name = "raw_value",
value = "no-encoding-needed",
encoding = CookieEncoding.RAW // No encoding applied
)class ThreadSafeCookieStorage : CookiesStorage {
private val cookies = mutableMapOf<String, MutableList<Cookie>>()
private val mutex = Mutex()
override suspend fun get(requestUrl: Url): List<Cookie> = mutex.withLock {
val host = requestUrl.host
return cookies[host]?.filter { cookie ->
// Apply domain and path matching logic
matchesDomain(cookie.domain, host) &&
matchesPath(cookie.path, requestUrl.encodedPath)
} ?: emptyList()
}
override suspend fun addCookie(requestUrl: Url, cookie: Cookie) = mutex.withLock {
val host = requestUrl.host
cookies.getOrPut(host) { mutableListOf() }.add(cookie)
}
override fun close() {
// Cleanup resources
}
private fun matchesDomain(cookieDomain: String?, requestHost: String): Boolean {
// Implement domain matching rules
return cookieDomain == null || requestHost.endsWith(cookieDomain)
}
private fun matchesPath(cookiePath: String?, requestPath: String): Boolean {
// Implement path matching rules
return cookiePath == null || requestPath.startsWith(cookiePath)
}
}class FileCookieStorage(private val file: File) : CookiesStorage {
private val cookies = mutableMapOf<String, MutableList<Cookie>>()
init {
loadCookies()
}
override suspend fun get(requestUrl: Url): List<Cookie> {
return cookies[requestUrl.host] ?: emptyList()
}
override suspend fun addCookie(requestUrl: Url, cookie: Cookie) {
cookies.getOrPut(requestUrl.host) { mutableListOf() }.add(cookie)
saveCookies()
}
override fun close() {
saveCookies()
}
private fun loadCookies() {
if (file.exists()) {
// Load cookies from file (implementation dependent)
}
}
private fun saveCookies() {
// Save cookies to file (implementation dependent)
}
}val client = HttpClient {
install(HttpCookies) {
storage = AcceptAllCookiesStorage()
}
}
// Always use secure cookies for sensitive data
val secureCookie = Cookie(
name = "auth_session",
value = "encrypted_token",
secure = true, // Only send over HTTPS
httpOnly = true, // Prevent XSS attacks
extensions = mapOf(
"SameSite" to "Strict" // Prevent CSRF attacks
)
)class FilteringCookieStorage(
private val delegate: CookiesStorage,
private val allowedDomains: Set<String>
) : CookiesStorage {
override suspend fun get(requestUrl: Url): List<Cookie> {
return if (requestUrl.host in allowedDomains) {
delegate.get(requestUrl)
} else {
emptyList()
}
}
override suspend fun addCookie(requestUrl: Url, cookie: Cookie) {
if (requestUrl.host in allowedDomains) {
delegate.addCookie(requestUrl, cookie)
}
}
override fun close() = delegate.close()
}
val client = HttpClient {
install(HttpCookies) {
storage = FilteringCookieStorage(
AcceptAllCookiesStorage(),
setOf("api.example.com", "secure.example.com")
)
}
}val cookieStorage = AcceptAllCookiesStorage()
val client = HttpClient {
install(HttpCookies) {
storage = cookieStorage
}
}
// Make requests that set cookies
client.get("https://example.com/login")
// Inspect stored cookies
val storedCookies = cookieStorage.get(Url("https://example.com"))
println("Stored cookies: ${storedCookies.joinToString { "${it.name}=${it.value}" }}")val client = HttpClient {
install(HttpCookies)
install(HttpSend) {
intercept { request ->
// Log cookies being sent
val cookieHeader = request.headers["Cookie"]
println("Sending cookies: $cookieHeader")
execute(request)
}
}
}secure and httpOnly flags for sensitive cookiesInstall with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-core-tvosarm64