Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ allprojects {

group = "com.ucasoft.ktor"

version = "0.63.3"
version = "0.70.2"

repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Dependensies.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import org.gradle.api.Project

const val ktorVersion = "3.4.2"
const val kotestVersion = "6.1.10"
const val kotestVersion = "6.1.11"

fun Project.ktor(module: String) = "io.ktor:ktor-$module:$ktorVersion"

Expand Down
4 changes: 2 additions & 2 deletions ktor-simple-cache/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Ktor Simple Cache
Base solution which provides the plugin implementation and abstract class for cache providers.

[![Maven Central with version prefix filter](https://img.shields.io/maven-central/v/com.ucasoft.ktor/ktor-simple-cache/0.63.3?color=blue)](https://search.maven.org/artifact/com.ucasoft.ktor/ktor-simple-cache/0.63.3/jar)
[![Maven Central with version prefix filter](https://img.shields.io/maven-central/v/com.ucasoft.ktor/ktor-simple-cache/0.70.2?color=blue)](https://search.maven.org/artifact/com.ucasoft.ktor/ktor-simple-cache/0.70.2/jar)
## Setup
### Gradle
```kotlin
repositories {
mavenCentral()
}

implementation("com.ucasoft.ktor:ktor-simple-cache:0.63.3")
implementation("com.ucasoft.ktor:ktor-simple-cache:0.70.2")
```
## Usage
```kotlin
Expand Down
6 changes: 4 additions & 2 deletions ktor-simple-cache/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ kotlin {
useJUnitPlatform()
}
}
linuxArm64()
linuxX64()
macosX64()
macosArm64()
mingwX64()
sourceSets {
val commonMain by getting {
dependencies {
implementation(ktorServer("core"))
api(ktorServer("core"))
}
kotlin.srcDir("src/main/kotlin")
}
Expand Down
4 changes: 2 additions & 2 deletions ktor-simple-memory-cache/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Ktor Simple Memory Cache
Memory cache provider for Ktor Simple Cache plugin

[![Maven Central with version prefix filter](https://img.shields.io/maven-central/v/com.ucasoft.ktor/ktor-simple-memory-cache/0.63.3?color=blue)](https://search.maven.org/artifact/com.ucasoft.ktor/ktor-simple-memory-cache/0.63.3/jar)
[![Maven Central with version prefix filter](https://img.shields.io/maven-central/v/com.ucasoft.ktor/ktor-simple-memory-cache/0.70.2?color=blue)](https://search.maven.org/artifact/com.ucasoft.ktor/ktor-simple-memory-cache/0.70.2/jar)
## Setup
### Gradle
```kotlin
repositories {
mavenCentral()
}

implementation("com.ucasoft.ktor:ktor-simple-memory-cache:0.63.3")
implementation("com.ucasoft.ktor:ktor-simple-memory-cache:0.70.2")
```
## Usage
```kotlin
Expand Down
4 changes: 3 additions & 1 deletion ktor-simple-memory-cache/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ plugins {
kotlin {
jvmToolchain(11)
jvm()
linuxArm64()
linuxX64()
macosX64()
macosArm64()
mingwX64()
sourceSets {
val commonMain by getting {
dependencies {
Expand Down
4 changes: 2 additions & 2 deletions ktor-simple-redis-cache/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Ktor Simple Redis Cache
Redis cache provider for Ktor Simple Cache plugin

[![Maven Central with version prefix filter](https://img.shields.io/maven-central/v/com.ucasoft.ktor/ktor-simple-redis-cache/0.63.3?color=blue)](https://search.maven.org/artifact/com.ucasoft.ktor/ktor-simple-redis-cache/0.63.3/jar)
[![Maven Central with version prefix filter](https://img.shields.io/maven-central/v/com.ucasoft.ktor/ktor-simple-redis-cache/0.70.2?color=blue)](https://search.maven.org/artifact/com.ucasoft.ktor/ktor-simple-redis-cache/0.70.2/jar)
## Setup
### Gradle
```kotlin
repositories {
mavenCentral()
}

implementation("com.ucasoft.ktor:ktor-simple-redis-cache:0.63.3")
implementation("com.ucasoft.ktor:ktor-simple-redis-cache:0.70.2")
```
## Usage
```kotlin
Expand Down
10 changes: 7 additions & 3 deletions ktor-simple-redis-cache/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ kotlin {
useJUnitPlatform()
}
}
linuxArm64()
linuxX64()
macosArm64()
mingwX64()
sourceSets {
val jvmMain by getting {
val commonMain by getting {
dependencies {
implementation(project(":ktor-simple-cache"))
implementation("redis.clients:jedis:7.4.0")
implementation("com.google.code.gson:gson:2.13.2")
implementation("io.github.domgew:kedis:0.0.12")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.11.0")
}
kotlin.srcDir("src/main/kotlin")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,103 @@
package com.ucasoft.ktor.simpleRedisCache

import com.google.gson.Gson
import com.ucasoft.ktor.simpleCache.SimpleCacheConfig
import com.ucasoft.ktor.simpleCache.SimpleCacheProvider
import redis.clients.jedis.DefaultJedisClientConfig
import redis.clients.jedis.RedisClient
import io.github.domgew.kedis.KedisClient
import io.github.domgew.kedis.arguments.value.SetOptions
import io.github.domgew.kedis.commands.KedisValueCommands
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.http.content.OutgoingContent
import io.ktor.utils.io.ByteChannel
import io.ktor.utils.io.readRemaining
import kotlinx.io.readByteArray
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds

class SimpleRedisCacheProvider(config: Config) : SimpleCacheProvider(config) {

private val jedis = RedisClient.builder().hostAndPort(config.host, config.port).clientConfig(
DefaultJedisClientConfig.builder().ssl(config.ssl).build()
).build()
private val kedis = KedisClient.builder {
hostAndPort(config.host, config.port)
connectTimeout = 250.milliseconds
}

override suspend fun getCache(key: String): Any? =
if (jedis.exists(key)) SimpleRedisCacheObject.fromCache(jedis[key]) else null
kedis.execute(KedisValueCommands.get(key))?.let { Json.decodeFromString<CachedResponse>(it).toOutgoingContent() }

Check warning on line 27 in ktor-simple-redis-cache/src/main/kotlin/com/ucasoft/ktor/simpleRedisCache/SimpleRedisCacheProvider.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace function call with indexed accessor.

See more on https://sonarcloud.io/project/issues?id=Scogun_ktor-simple-cache&issues=AZ22kLE8q3TQ4lOWkZqI&open=AZ22kLE8q3TQ4lOWkZqI&pullRequest=29

override suspend fun setCache(key: String, content: Any, invalidateAt: Duration?) {
val expired = (invalidateAt ?: this.invalidateAt).inWholeMilliseconds
jedis.psetex(key, expired, SimpleRedisCacheObject.fromObject(content).toString())
val outgoing = content as OutgoingContent
kedis.execute(KedisValueCommands.set(key, Json.encodeToString(CachedResponse(

Check warning on line 32 in ktor-simple-redis-cache/src/main/kotlin/com/ucasoft/ktor/simpleRedisCache/SimpleRedisCacheProvider.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Replace function call with indexed accessor.

See more on https://sonarcloud.io/project/issues?id=Scogun_ktor-simple-cache&issues=AZ22kLE8q3TQ4lOWkZqJ&open=AZ22kLE8q3TQ4lOWkZqJ&pullRequest=29
bytes = outgoing.toByteArray(),
contentType = outgoing.contentType?.toString(),
status = outgoing.status?.value,
contentLength = outgoing.contentLength
)), SetOptions(expire = SetOptions.ExpireOption.ExpiresInMilliseconds(expired))))
}

class Config internal constructor() : SimpleCacheProvider.Config() {

var host = "localhost"

var port = 6379

var ssl = false
}
}

private class SimpleRedisCacheObject(val type: String, val content: String) {
@Serializable
private data class CachedResponse(
val bytes: ByteArray,
val contentType: String?,
val status: Int?,
val contentLength: Long?
) {

override fun toString() = "$type%#%$content"
fun toOutgoingContent() = object : OutgoingContent.ByteArrayContent() {
override fun bytes() = bytes
override val contentType = this@CachedResponse.contentType?.let { ContentType.parse(it) }
override val status = this@CachedResponse.status?.let { HttpStatusCode.fromValue(it) }
override val contentLength = this@CachedResponse.contentLength
}

companion object {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

fun fromObject(`object`: Any) = SimpleRedisCacheObject(`object`::class.java.name, Gson().toJson(`object`))
other as CachedResponse

fun fromCache(cache: String): Any {
val data = cache.split("%#%")
return Gson().fromJson(data.last(), Class.forName(data.first()))
}
if (status != other.status) return false
if (contentLength != other.contentLength) return false
if (!bytes.contentEquals(other.bytes)) return false
if (contentType != other.contentType) return false

return true
}

override fun hashCode(): Int {
var result = status ?: 0
result = 31 * result + (contentLength?.hashCode() ?: 0)
result = 31 * result + bytes.contentHashCode()
result = 31 * result + (contentType?.hashCode() ?: 0)
return result
}
}

fun SimpleCacheConfig.redisCache(
configure : SimpleRedisCacheProvider.Config.() -> Unit
){
provider = SimpleRedisCacheProvider(SimpleRedisCacheProvider.Config().apply(configure))
}

private suspend fun OutgoingContent.toByteArray(): ByteArray = when (this) {
is OutgoingContent.ByteArrayContent -> bytes()
is OutgoingContent.NoContent -> byteArrayOf()
is OutgoingContent.ReadChannelContent -> readFrom().readRemaining().readByteArray()
is OutgoingContent.WriteChannelContent -> {
val channel = ByteChannel(autoFlush = true)
writeTo(channel)
channel.close()
channel.readRemaining().readByteArray()
}
else -> byteArrayOf()
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import io.kotest.matchers.shouldNotBe
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
Expand Down Expand Up @@ -100,6 +101,30 @@ internal class RedisCacheTests {
}
}

@Test
fun `test content type`() {
testApplication {
install(SimpleCache) {
redisCache {
invalidateAt = 10.seconds
this.host = redisContainer.host
this.port = redisContainer.firstMappedPort
}
}

application(Application::testApplication)

val textResponse1 = client.get("/text")
textResponse1.status.shouldBe(HttpStatusCode.OK)
textResponse1.contentType()?.withoutParameters().shouldBe(ContentType.Text.Plain)

val textResponse2 = client.get("/text")
textResponse2.status shouldBe HttpStatusCode.OK
textResponse2.contentType()?.withoutParameters().shouldBe(ContentType.Text.Plain)
textResponse2.bodyAsText().shouldBe(textResponse1.bodyAsText())
}
}

companion object {

@Container
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.ucasoft.ktor.simpleRedisCache
import com.ucasoft.ktor.simpleCache.cacheOutput
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.http.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
Expand Down Expand Up @@ -30,5 +31,10 @@ fun Application.testApplication() {
call.respond(TestResponse())
}
}
cacheOutput {
get("text") {
call.respondText("Hello World with ${Random.nextInt()}", ContentType.Text.Plain)
}
}
}
}
Loading