ktorio / ktor

Framework for quickly creating connected applications in Kotlin with minimal effort
https://ktor.io
Apache License 2.0
12.8k stars 1.04k forks source link

Session Storage Using Redis #1227

Open blackmo18 opened 5 years ago

blackmo18 commented 5 years ago

Hi I'm just wondering whether it is possible to use redis as session storage together with Ktor Session module since redis allow an entry to have timed life. It might be easier to handle session expiration.

If it is possible, what is the proper way of doing it?

cy6erGn0m commented 5 years ago

First of all, an async redis client need to be completed https://github.com/ktorio/ktor-clients/tree/master/ktor-client-redis

To implement a custom session storage you need to implement SessionStorage.

blackmo18 commented 5 years ago

Hello @cy6erGn0m , I followed the sample on handling cookies/headers using redis in https://ktor.io/servers/features/sessions/cookie-header.html

and i used lettuce for redis client since it allows asynchronous operation io.lettuce:lettuce-core:5.1.7.RELEASE

but I'm getting different session id from what is stored in redis vs what I get from calling session in application call, or I missed something

post("$root$base/say-hello") {
          ......
            val trueSession = call.sessions.get<ChatSession>()

            Logger.debug { "Request POST | session :: $trueSession"}
          ......
}

with the following setup in storing session:

session module

install(Sessions) {
        cookie<ChatSession>("SESSION", storage = RedisSessionStorage(rediscient)) {
            cookie.path = "/"
        }
    }

intercept(ApplicationCallPipeline.Features) {
    if (call.sessions.get<SocketRouter.ChatSession>() == null) {
        call.sessions.set(SocketRouter.ChatSession(generateNonce()))
    }
}

session

data class ChatSession(val id: String)

session storage

class RedisSessionStorage(private val redisClient: Redis) : SimplifiedSessionStorage() {
    override suspend fun read(id: String): ByteArray? {
        Logger.debug { "read-key: $id" }
        val result = redisClient.async.get(id).await()
        return result.hexStringToByteArray()
    }

    override suspend fun write(id: String, data: ByteArray?) {
        Logger.debug { "write-key: $id" }
        if (data == null) {
            redisClient.async.del(id)
        } else {
            redisClient.async.set(id, data.toHex())
        }
    }
}
cy6erGn0m commented 5 years ago

Shouldn't you wait for the write completion similar to read implementation?

blackmo18 commented 5 years ago

I tried putting await on it, still, the value of id on session storage vs the id on calling session from application call on the routing is not the same.

Tho the value of session is still the same in every call from the client, I must be missing something since no error at all.

cy6erGn0m commented 5 years ago

What is your client? browser? browser+js ?

blackmo18 commented 5 years ago

I'm testing using postman, and android client.

doyaaaaaken commented 4 years ago

Hi, what is the status of this issue now? PR #504 is closed by stale bot.

I implemented RedisSessionStorage by myself to implement SessionStorage interface, but for system reliability I hope this feature is officially supported and then I gradly use it.

oleg-larshin commented 4 years ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

vishwas-trivedi commented 7 months ago

Hi, any update on this issue? I am trying to implement RedisSessionStorage but it would be great to have this feature officially supported, and us developers being able to us it out of the box.