Ktor 1.6.8 Help

Storages

Ktor allows you to store session data on the server and pass only a session ID between the server and the client. In this case, you can choose where to keep the payload on the server.

Built-in storages

The following storage types are available out-of-the-box:

  • SessionStorageMemory enables storing a session's content in memory. This storage keeps data while the server is running and discards information once the server stops.

  • directorySessionStorage can be used to store a session's data in a file under the specified directory.

To create the required storage type, pass it as a second parameter to the cookie or header method. For example, you can store cookies in the server memory as follows:

install(Sessions) { cookie<SampleSession>("SAMPLE_SESSION", storage = SessionStorageMemory()) }

To store cookies in a file under the .sessions directory, create the directorySessionStorage in this way:

import java.io.File // ... install(Sessions) { cookie<SampleSession>("SAMPLE_SESSION", storage = directorySessionStorage(File(".sessions"))) }

Custom storage

Ktor provides the SessionStorage interface that allows you to implement a custom storage.

interface SessionStorage { suspend fun write(id: String, provider: suspend (ByteWriteChannel) -> Unit) suspend fun invalidate(id: String) suspend fun <R> read(id: String, consumer: suspend (ByteReadChannel) -> R): R }

All three functions are suspending and use ByteWriteChannel and ByteReadChannel to read and write data from/to an asynchronous channel. The example below shows how to implement the SessionStorage interface to store session data in a ByteArray:

abstract class SimplifiedSessionStorage : SessionStorage { abstract suspend fun read(id: String): ByteArray? abstract suspend fun write(id: String, data: ByteArray?): Unit override suspend fun invalidate(id: String) { write(id, null) } override suspend fun <R> read(id: String, consumer: suspend (ByteReadChannel) -> R): R { val data = read(id) ?: throw NoSuchElementException("Session $id not found") return consumer(ByteReadChannel(data)) } override suspend fun write(id: String, provider: suspend (ByteWriteChannel) -> Unit) { return provider(CoroutineScope(Dispatchers.IO).reader(coroutineContext, autoFlush = true) { write(id, channel.readAvailable()) }.channel) } } suspend fun ByteReadChannel.readAvailable(): ByteArray { val data = ByteArrayOutputStream() val temp = ByteArray(1024) while (!isClosedForRead) { val read = readAvailable(temp) if (read <= 0) break data.write(temp, 0, read) } return data.toByteArray() }
Last modified: 28 May 2021