partially implemented ktor utils

This commit is contained in:
000Sanya
2020-09-22 12:20:22 +10:00
parent b84367f47c
commit 1dcf17a35d
38 changed files with 541 additions and 0 deletions

24
ktor/server/build.gradle Normal file
View File

@@ -0,0 +1,24 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
apply from: "$mppJavaProjectPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api internalProject("micro_utils.ktor.common")
}
}
jvmMain {
dependencies {
api "io.ktor:ktor-server:$ktor_version"
api "io.ktor:ktor-server-host-common:$ktor_version"
api "io.ktor:ktor-websockets:$ktor_version"
}
}
}
}

View File

@@ -0,0 +1,43 @@
package dev.inmo.micro_utils.ktor.server
import dev.inmo.micro_utils.ktor.common.CorrectCloseException
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import io.ktor.http.cio.websocket.*
import io.ktor.routing.Route
import io.ktor.websocket.webSocket
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.serialization.SerializationStrategy
private suspend fun DefaultWebSocketSession.checkReceivedAndCloseIfExists() {
if (incoming.poll() != null) {
close()
throw CorrectCloseException
}
}
fun <T> Route.includeWebsocketHandling(
suburl: String,
flow: Flow<T>,
converter: (T) -> ByteArray
) {
webSocket(suburl) {
safely {
flow.collect {
checkReceivedAndCloseIfExists()
send(converter(it))
}
}
}
}
fun <T> Route.includeWebsocketHandling(
suburl: String,
flow: Flow<T>,
serializer: SerializationStrategy<T>
) = includeWebsocketHandling(
suburl,
flow
) {
standardKtorSerialFormat.encodeToByteArray(serializer, it)
}

View File

@@ -0,0 +1,11 @@
package dev.inmo.micro_utils.ktor.server
import com.soywiz.klock.DateTime
import dev.inmo.micro_utils.ktor.common.FromToDateTime
import io.ktor.http.Parameters
val Parameters.extractFromToDateTime: FromToDateTime
get() = FromToDateTime(
get("from") ?.toDoubleOrNull() ?.let { DateTime(it) },
get("to") ?.toDoubleOrNull() ?.let { DateTime(it) }
)

View File

@@ -0,0 +1,65 @@
package dev.inmo.micro_utils.ktor.server
import dev.inmo.micro_utils.ktor.common.standardKtorSerialFormat
import io.ktor.application.ApplicationCall
import io.ktor.http.HttpStatusCode
import io.ktor.response.respond
import io.ktor.response.respondBytes
import io.ktor.util.toByteArray
import kotlinx.serialization.*
suspend fun <T> ApplicationCall.unianswer(
answerSerializer: SerializationStrategy<T>,
answer: T
) {
respondBytes(
standardKtorSerialFormat.encodeToByteArray(answerSerializer, answer),
standardKtorSerialFormatContentType
)
}
suspend fun <T> ApplicationCall.uniload(
deserializer: DeserializationStrategy<T>
) = standardKtorSerialFormat.decodeFromByteArray(
deserializer,
request.receiveChannel().toByteArray()
)
suspend fun ApplicationCall.getParameterOrSendError(
field: String
) = parameters[field].also {
if (it == null) {
respond(HttpStatusCode.BadRequest, "Request must contains $field")
}
}
fun ApplicationCall.getQueryParameter(
field: String
) = request.queryParameters[field]
suspend fun ApplicationCall.getQueryParameterOrSendError(
field: String
) = getQueryParameter(field).also {
if (it == null) {
respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
}
}
fun <T> ApplicationCall.decodeUrlQueryValue(
field: String,
deserializer: DeserializationStrategy<T>
) = getQueryParameter(field) ?.let {
standardKtorSerialFormat.decodeFromHexString(
deserializer,
it
)
}
suspend fun <T> ApplicationCall.decodeUrlQueryValueOrSendError(
field: String,
deserializer: DeserializationStrategy<T>
) = decodeUrlQueryValue(field, deserializer).also {
if (it == null) {
respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
}
}

View File

@@ -0,0 +1,21 @@
package dev.inmo.micro_utils.ktor.server.configurators
import io.ktor.application.Application
import io.ktor.application.install
import io.ktor.features.CachingHeaders
import kotlinx.serialization.Contextual
data class ApplicationCachingHeadersConfigurator(
private val elements: List<@Contextual Element>
) : KtorApplicationConfigurator {
interface Element { operator fun CachingHeaders.Configuration.invoke() }
override fun Application.configure() {
install(CachingHeaders) {
elements.forEach {
it.apply { invoke() }
}
}
}
}

View File

@@ -0,0 +1,27 @@
package dev.inmo.micro_utils.ktor.server.configurators
import io.ktor.application.*
import io.ktor.routing.Route
import io.ktor.routing.Routing
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
@Serializable
class ApplicationRoutingConfigurator(
private val elements: List<@Contextual Element>
) : KtorApplicationConfigurator {
interface Element { operator fun Route.invoke() }
override fun Application.configure() {
try {
feature(Routing)
} catch (e: IllegalStateException) {
install(Routing) {
elements.forEach {
it.apply { invoke() }
}
}
}
}
}

View File

@@ -0,0 +1,20 @@
package dev.inmo.micro_utils.ktor.server.configurators
import io.ktor.application.Application
import io.ktor.application.install
import io.ktor.sessions.Sessions
import kotlinx.serialization.Contextual
class ApplicationSessionsConfigurator(
private val elements: List<@Contextual Element>
) : KtorApplicationConfigurator {
interface Element { operator fun Sessions.Configuration.invoke() }
override fun Application.configure() {
install(Sessions) {
elements.forEach {
it.apply { invoke() }
}
}
}
}

View File

@@ -0,0 +1,7 @@
package dev.inmo.micro_utils.ktor.server.configurators
import io.ktor.application.Application
interface KtorApplicationConfigurator {
fun Application.configure()
}

View File

@@ -0,0 +1,21 @@
package dev.inmo.micro_utils.ktor.server.configurators
import io.ktor.application.Application
import io.ktor.application.install
import io.ktor.features.StatusPages
import kotlinx.serialization.Contextual
class StatusPagesConfigurator(
private val elements: List<@Contextual Element>
) : KtorApplicationConfigurator {
interface Element { operator fun StatusPages.Configuration.invoke() }
override fun Application.configure() {
install(StatusPages) {
elements.forEach {
it.apply { invoke() }
}
}
}
}

View File

@@ -0,0 +1,5 @@
package dev.inmo.micro_utils.ktor.server
import io.ktor.http.ContentType
val standardKtorSerialFormatContentType = ContentType.Application.Cbor