セッションストレージ

Estimated reading time: 1 minute

SessionStorageMemoryDirectoryStorageという事前に定義された2つのストレージがあります。 そして他の組み合わせ可能なストレージとしてCacheStorageがあります。

DirectoryStorageCacheStorageio.ktor:ktor-server-sessions:$ktor_versionアーティファクトに依存しています。

このモードでは、実際のセッションコンテンツの代わりにセッションIDを送信します。 このIDは、特定のSessionStorageを使ってコンテンツをサーバサイドに保存するため使われます。 このモードはcookieまたはheaderメソッド内でストレージを第二引数で指定することで使えます。

例:

install(Sessions) {
    cookie<SampleSession>("SESSION_FEATURE_SESSION_ID", SessionStorageMemory()) {
        cookie.path = "/"
    }
}

カスタムSessionStorage

SessionStorageはセッションペイロードの参照・保存の責務をもちます。 インターフェースはsuspend可能になっており、データを非同期に変換することができます。

データはストリームとして変換され、呼び出し先はバイナリペイロードを提供するコンシューマとプロバイダを渡す必要があります。 呼び出し先はバイトチャネルのオープン・クローズの責務をもちます。

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
}

もしストレージが情報をストリームとして保存するよい方法を提供していなければ、 ByteArrayを使って単純に読み書きするようなシンプルなアダプターを使うこともできます。 また、プリミティブなストリームベースバージョンでAPIを処理する方法を知るための例として使用することもできます。

SimplifiedSessionStorage.kt
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()
}