From 652950633f0e45362e10a4eb8898e6afb2411440 Mon Sep 17 00:00:00 2001 From: sergeych Date: Fri, 9 Dec 2022 11:51:00 +0100 Subject: [PATCH] even better ktor server integration --- README.md | 8 ++- build.gradle.kts | 6 +- .../superlogin/server/SuperloginServer.kt | 62 +++++++++++++++++-- .../kotlin/net/sergeych/WsServerKtTest.kt | 23 ++++--- 4 files changed, 85 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index bd27df3..0b4dbe9 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,12 @@ It also contains useful tools for this type of application: - `AccessControlObject` to contain recoverable password-protected data with a backup `secret` word to restore access. -Will be published under MIT when become usable. +## Usage + +Currently, complie it and publisj into mavenLocal, and use from there. Soon will be published to some public maven repo. + +## License + +Will be published under MIT as soon as first RC will be published. diff --git a/build.gradle.kts b/build.gradle.kts index 99929e5..5494fcd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -60,7 +60,11 @@ kotlin { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4") } } - val jvmMain by getting + val jvmMain by getting { + dependencies { + implementation("io.ktor:ktor-server-core:$ktor_version") + } + } val jvmTest by getting { dependencies { implementation("io.ktor:ktor-server-core:$ktor_version") diff --git a/src/jvmMain/kotlin/net/sergeych/superlogin/server/SuperloginServer.kt b/src/jvmMain/kotlin/net/sergeych/superlogin/server/SuperloginServer.kt index 819c73e..377ee1e 100644 --- a/src/jvmMain/kotlin/net/sergeych/superlogin/server/SuperloginServer.kt +++ b/src/jvmMain/kotlin/net/sergeych/superlogin/server/SuperloginServer.kt @@ -1,8 +1,10 @@ package net.sergeych.superlogin.server +import io.ktor.server.application.* import net.sergeych.parsec3.AdapterBuilder import net.sergeych.parsec3.CommandHost import net.sergeych.parsec3.WithAdapter +import net.sergeych.parsec3.parsec3TransportServer import net.sergeych.superlogin.* import net.sergeych.unikrypto.SignedRecord import kotlin.random.Random @@ -12,9 +14,61 @@ fun randomACOLike(): ByteArray { } /** - * Create a superlogin server on a curretn adapter builder, using some session builder lambda. + * Create superlogin server using default parsec 3 server transport providing session creation + * lambda and a block to set up server-specific api. The session must inherit [SLServerSession] + * with application-specific data, and implement all of its abstract methods at list (also it + * might need t override some default methods, see docs. For example: + * ~~~ + * + * // Application-specific API declaration + * class TestApiServer : CommandHost() { + * val foobar by command() + * } + * + * // application data to be included in the "user" data transfered to and from the client: + * data class TestData(clientData: String) + * + * // session state, is kept until the user connection (e.g. session) is closed, in the RAM: + * class TestSession(var sessionData: String) : SLServerSession() + * + * // Create a server in a ktor context: + * fun Application.serverModule() { + * SuperloginServer(TestApiServer(), { TestSession("foobar") }) { + * // application API implementation + * on(api.foobar) { + * "Session data: ${sessionData}, loginName: $currentLoginName user data: $superloginData" + * } + * } + * } + * ~~~ + * + * @param api service specific API declaration (usually in the shared code) + * @param sessionBuilder function that creates new session. Session is created on every incoming + * connection so try not to do any time and resource consuming operations there + * @param adapterBuilder block that should implement service-specific api. + * @param D application specific data (included in the session and transferred with registration + * and login) + * @param T service-specific session, class implementing any session-based code specific to the + * application but also implement necessary `superlogin` methods (implementing abstract + * ones) that implement user login data persistence and search. + */ +inline fun ,> Application.SuperloginServer( + api: CommandHost, + crossinline sessionBuilder: suspend AdapterBuilder>.()->T, + crossinline adapterBuilder: AdapterBuilder>.()->Unit +) { + parsec3TransportServer(api) { + setupSuperloginServer { sessionBuilder() } + adapterBuilder() + } +} + + +/** + * Set up a superlogin server manually, on a current adapter builder, using some session builder lambda. * Note that session must inherit [SLServerSession] and implement necessary abstract methods - * that do store and retrieve user data. It sould be used like: + * that do store and retrieve user data. Usually you can do it easier with [SuperloginServer] call. + * If you want to do it manually (fr example using a custom transport), do it like this: * ``` * parsec3TransportServer(TestApiServer()) { * // the session extends SLServerSession and contain app-specific data and @@ -31,8 +85,8 @@ fun randomACOLike(): ByteArray { * * @param sessionBuilder code that builds a new session over the adapter. */ -inline fun , H : CommandHost> AdapterBuilder.superloginServer( - crossinline sessionBuilder: suspend AdapterBuilder.()->T +inline fun , H : CommandHost> AdapterBuilder.setupSuperloginServer( + crossinline sessionBuilder: suspend AdapterBuilder.() -> T, ) { newSession { sessionBuilder() } addErrors(SuperloginExceptionsRegistry) diff --git a/src/jvmTest/kotlin/net/sergeych/WsServerKtTest.kt b/src/jvmTest/kotlin/net/sergeych/WsServerKtTest.kt index 69b18fd..b555203 100644 --- a/src/jvmTest/kotlin/net/sergeych/WsServerKtTest.kt +++ b/src/jvmTest/kotlin/net/sergeych/WsServerKtTest.kt @@ -5,16 +5,13 @@ import io.ktor.server.engine.* import io.ktor.server.netty.* import kotlinx.coroutines.runBlocking import kotlinx.serialization.Serializable -import net.sergeych.parsec3.CommandHost -import net.sergeych.parsec3.Parsec3WSClient -import net.sergeych.parsec3.WithAdapter -import net.sergeych.parsec3.parsec3TransportServer +import net.sergeych.parsec3.* import net.sergeych.superlogin.* import net.sergeych.superlogin.client.LoginState import net.sergeych.superlogin.client.Registration import net.sergeych.superlogin.client.SuperloginClient import net.sergeych.superlogin.server.SLServerSession -import net.sergeych.superlogin.server.superloginServer +import net.sergeych.superlogin.server.setupSuperloginServer import net.sergeych.unikrypto.PublicKey import superlogin.assertThrowsAsync import kotlin.random.Random @@ -193,6 +190,7 @@ internal class WsServerKtTest { assertTrue { slc.isLoggedIn } assertEquals("foo", slc.call(api.loginName)) + } } @@ -212,10 +210,19 @@ internal class WsServerKtTest { } +inline fun , H : CommandHost> Application.SuperloginServer( + api: H, + crossinline sessionBuilder: suspend AdapterBuilder.()->T, + crossinline adapterBuilder: AdapterBuilder.()->Unit +) { + parsec3TransportServer(api) { + setupSuperloginServer { sessionBuilder() } + adapterBuilder() + } +} + fun Application.testServerModule() { - parsec3TransportServer(TestApiServer()) { -// superloginServer(TestServerTraits,TestApiServer>()) { - superloginServer { TestSession() } + SuperloginServer(TestApiServer(), { TestSession() }) { on(api.loginName) { println("login name called. now we have $currentLoginName : $superloginData") currentLoginName