mp
11/27/2018, 4:54 PMSaša Šijak
11/27/2018, 8:23 PMdave08
11/27/2018, 9:10 PMmp
11/27/2018, 11:07 PMmp
11/27/2018, 11:08 PMmp
11/27/2018, 11:08 PMdave08
11/28/2018, 3:16 AMmp
11/28/2018, 1:35 PMdave08
11/28/2018, 1:36 PMmp
11/28/2018, 1:36 PMmp
11/28/2018, 1:36 PMdave08
11/28/2018, 1:37 PMdave08
11/28/2018, 1:40 PMmp
11/28/2018, 1:40 PMmp
11/28/2018, 1:41 PMdave08
11/28/2018, 1:41 PMmp
11/28/2018, 1:41 PMmp
11/28/2018, 1:42 PMdave08
11/28/2018, 1:42 PMdave08
11/28/2018, 1:43 PMmp
11/28/2018, 1:43 PMmp
11/28/2018, 1:44 PMdave08
11/28/2018, 1:46 PMmp
11/28/2018, 1:47 PMdave08
11/28/2018, 1:48 PMmp
11/28/2018, 1:51 PMdave08
11/28/2018, 1:52 PMmp
11/28/2018, 1:52 PMmp
11/28/2018, 1:52 PMclass RedisSessionStorage(private val redis: RedisAsyncCommands<String, ByteArray>,
ttl: Duration,
private val keyPrefix: String = "session_") : SimplifiedSessionStorage() {
private val ttlMillis = ttl.toMillis()
override suspend fun read(id: String): ByteArray? {
return redis.get(effectiveId(id))
.asDeferred()
.await()?.also {
redis.pexpire(effectiveId(id), ttlMillis)
.asDeferred()
.await()
}
}
override suspend fun write(id: String, data: ByteArray?) {
if (data == null) {
redis.del(effectiveId(id))
} else {
redis.set(effectiveId(id), data, SetArgs().px(ttlMillis))
}.asDeferred()
.await()
}
private fun effectiveId(id: String): String = keyPrefix + id
}
class StringByteCodec : RedisCodec<String, ByteArray> {
private val keyCodec = StringCodec()
private val valCodec = ByteArrayCodec()
override fun decodeKey(bytes: ByteBuffer?): String = keyCodec.decodeKey(bytes)
override fun encodeValue(value: ByteArray?): ByteBuffer = valCodec.encodeValue(value)
override fun encodeKey(key: String?): ByteBuffer = keyCodec.encodeKey(key)
override fun decodeValue(bytes: ByteBuffer?): ByteArray = valCodec.decodeValue(bytes)
}
/**
* Since we don't have an obvious place to hang on to re-usable buffers, use this helper from
* <https://ktor.io/features/sessions.html>. It does allocate, but oh well...
*/
abstract class SimplifiedSessionStorage : SessionStorage {
abstract suspend fun read(id: String): ByteArray?
abstract suspend fun write(id: String, data: ByteArray?)
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(<http://Dispatchers.IO|Dispatchers.IO>).reader(coroutineContext, autoFlush = true) {
val data = ByteArrayOutputStream()
val temp = ByteArray(1024)
while (!channel.isClosedForRead) {
val read = channel.readAvailable(temp)
if (read <= 0) break
data.write(temp, 0, read)
}
write(id, data.toByteArray())
}.channel)
}
}
mp
11/28/2018, 1:52 PMmp
11/28/2018, 1:53 PMdata class SessionData(val userId: UUID)
would be suitable for what you store in the session.mp
11/28/2018, 1:54 PMcall.sessions.set(SessionData(theUserId))
mp
11/28/2018, 1:56 PMinstall(Authentication)
block, you'd do session<SessionData>("USER_AUTH") { lookUpUserFromSession(it)?.let { user -> SomeUserPrincipalClass(user.foo, user.bar, user.baz) }
mp
11/28/2018, 1:57 PMcall.authentication.principal<SomeUserPrincipalClass>()
. If you have roles or something like that associated with users, that type would be a good place to hold them since you will have just loaded your user record.dave08
11/28/2018, 1:59 PM