diff --git a/README.md b/README.md index ae155cf..17b4333 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -Current stable version is __0.4.1-SNAPSHOT__. The full-dpulex RPC over websock is ok, the security level is still disabled until the protocol part will be sabilized. +Current stable version is __0.4.2-SNAPSHOT__. The full-dpulex RPC over websock is ok, the security level is still disabled until the protocol part will be sabilized. This is a connection-agnostic, kotlin multiplaftorm library providing full-duplex RPC type binary protocol, effective to work with binary data, such as encrypted data, keys, multimedia, etc. Default implementation uses websockets transport (binary frames) available on all supported platofrms (currently, JS and JVM). @@ -46,7 +46,7 @@ repsitories { //... dependencies { - api("net.sergeych:parsec3:0.3.3") + api("net.sergeych:parsec3:0.4.2-SNAPSHOT") } ``` diff --git a/build.gradle.kts b/build.gradle.kts index 5af23b0..2d9f5b9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,7 +10,7 @@ plugins { } group = "net.sergeych" -version = "0.4.1-SNAPSHOT" +version = "0.4.2-SNAPSHOT" repositories { mavenCentral() @@ -22,6 +22,7 @@ kotlin { jvmToolchain { languageVersion.set(JavaLanguageVersion.of("8")) } + jvm { compilations.all { kotlinOptions.jvmTarget = "1.8" @@ -39,6 +40,10 @@ kotlin { } } sourceSets { + all { + languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi") + languageSettings.optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") + } val commonMain by getting { dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3") @@ -83,9 +88,11 @@ kotlin { repositories { maven { url = uri("https://maven.universablockchain.com/") + val mavenUser: String by project + val mavenPassword: String by project credentials { - username = System.getenv("maven_user") - password = System.getenv("maven_password") + username = mavenUser // System.getenv("maven_user") + password = mavenPassword // System.getenv("maven_password") } } } diff --git a/src/commonMain/kotlin/net.sergeych.parsec3/AdapterBuilder.kt b/src/commonMain/kotlin/net.sergeych.parsec3/AdapterBuilder.kt index dd64068..945eb27 100644 --- a/src/commonMain/kotlin/net.sergeych.parsec3/AdapterBuilder.kt +++ b/src/commonMain/kotlin/net.sergeych.parsec3/AdapterBuilder.kt @@ -16,7 +16,7 @@ class AdapterBuilder>( f: AdapterBuilder.() -> Unit, ) { - internal var sessionProducer: (suspend () -> S) = { WithAdapter() as S} + internal var sessionProducer: (suspend () -> S) = { WithAdapter() as S } private set @@ -31,6 +31,17 @@ class AdapterBuilder>( api.on(ca, block) } + val onCancelHandlers = mutableListOf Unit>() + + + /** + * Set a onCancel handler that will be called upon adapter cancelling. Several adapters + * could be registered and will be called in order of registration + */ + fun onCancel(f: suspend () -> Unit) { + onCancelHandlers += f + } + @Suppress("unused") inline fun addError(code: String, noinline handler: (String?) -> T) { exceptionRegistry.register(code, handler) @@ -41,13 +52,23 @@ class AdapterBuilder>( exceptionRegistry.putAll(otherRegistry) } - suspend fun createWith(input: Flow, f: suspend (ByteArray)->Unit ): Adapter { + suspend fun createWith(input: Flow, f: suspend (ByteArray) -> Unit): Adapter { val s = sessionProducer() return Adapter( - s, api, exceptionRegistry) { f(it) } - .also { a-> + s, api, exceptionRegistry + ) { f(it) } + .also { a -> s._adapter = a - globalLaunch { input.collect { a.receiveFrame(it)} } + if (onCancelHandlers.isNotEmpty()) { + a.onCancel = { + for (h in onCancelHandlers) try { + h() + } catch (t: Throwable) { + t.printStackTrace() + } + } + } + globalLaunch { input.collect { a.receiveFrame(it) } } } } diff --git a/src/commonMain/kotlin/net.sergeych.parsec3/KVStorage.kt b/src/commonMain/kotlin/net.sergeych.parsec3/KVStorage.kt index aba32f4..0725dcd 100644 --- a/src/commonMain/kotlin/net.sergeych.parsec3/KVStorage.kt +++ b/src/commonMain/kotlin/net.sergeych.parsec3/KVStorage.kt @@ -51,7 +51,7 @@ class KVStorageDelegate( private var cachedValue: T = defaultValue private var cacheReady = false - operator fun getValue(thisRef: Nothing?, property: KProperty<*>): T { + operator fun getValue(thisRef: Any?, property: KProperty<*>): T { if (cacheReady) return cachedValue val data = storage.get(name(property)) if (data == null) @@ -62,7 +62,7 @@ class KVStorageDelegate( return cachedValue } - operator fun setValue(thisRef: Nothing?, property: KProperty<*>, value: T) { + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { // if (!cacheReady || value != cachedValue) { cachedValue = value cacheReady = true @@ -83,6 +83,7 @@ class MemoryKVStorage(copyFrom: KVStorage? = null) : KVStorage { // is used while underlying is null: private val data = mutableMapOf() + @Suppress("unused") fun connectToStorage(other: KVStorage) { other.addAll(this) underlying = other @@ -110,10 +111,10 @@ class MemoryKVStorage(copyFrom: KVStorage? = null) : KVStorage { } override val keys: Collection - get() = underlying?.let { it.keys } ?: data.keys + get() = underlying?.keys ?: data.keys override fun clear() { - underlying?.let { it.clear() } ?: data.clear() + underlying?.clear() ?: data.clear() } init { diff --git a/src/commonMain/kotlin/net.sergeych.parsec3/Parsec3SecureClient.kt b/src/commonMain/kotlin/net.sergeych.parsec3/Parsec3SecureClient.kt index 9e6eb07..fe0072f 100644 --- a/src/commonMain/kotlin/net.sergeych.parsec3/Parsec3SecureClient.kt +++ b/src/commonMain/kotlin/net.sergeych.parsec3/Parsec3SecureClient.kt @@ -1,7 +1,7 @@ package net.sergeych.parsec3 -/** - * Parsec3 secure adapter. - * @param transport a parsec3 transport to establish connection with, for example [Parsec3WSClient]. - */ -class Parsec3SecureClient(transport: Parsec3Transport) \ No newline at end of file +///** +// * Parsec3 secure adapter. +// * @param transport a parsec3 transport to establish connection with, for example [Parsec3WSClient]. +// */ +//class Parsec3SecureClient(transport: Parsec3Transport) \ No newline at end of file diff --git a/src/commonTest/kotlin/parsec3/AdapterTest.kt b/src/commonTest/kotlin/parsec3/AdapterTest.kt index af9e820..23c6d63 100644 --- a/src/commonTest/kotlin/parsec3/AdapterTest.kt +++ b/src/commonTest/kotlin/parsec3/AdapterTest.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.test.runTest import net.sergeych.parsec3.* import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertTrue internal class AdapterTest { @@ -74,6 +75,8 @@ internal class AdapterTest { it.register("foo_x") { IllegalArgumentException(it) } } + var b1Cancelled = false + var b2Cancelled = false val b1 = AdapterBuilder(ApiS1, er) { newSession { TestSession("42") } on(api.foo) { @@ -85,6 +88,9 @@ internal class AdapterTest { on(ApiS1.ex2) { throw IllegalArgumentException() } + onCancel { + b1Cancelled = true + } } val b2 = AdapterBuilder(ApiS2(), er) { on(api.bar) { @@ -100,6 +106,7 @@ internal class AdapterTest { throw t } } + onCancel { b2Cancelled = true } } val a1 = b1.createWith(ch21.receiveAsFlow()) { @@ -122,6 +129,9 @@ internal class AdapterTest { ch21.cancel() a1.cancel() a2.cancel() + + assertTrue { b1Cancelled } + assertTrue { b2Cancelled } } } \ No newline at end of file diff --git a/src/commonTest/kotlin/parsec3/MemoryKVStorageTest.kt b/src/commonTest/kotlin/parsec3/MemoryKVStorageTest.kt index 49c90f6..484e6ad 100644 --- a/src/commonTest/kotlin/parsec3/MemoryKVStorageTest.kt +++ b/src/commonTest/kotlin/parsec3/MemoryKVStorageTest.kt @@ -1,12 +1,18 @@ package parsec3 import net.sergeych.mptools.toDump +import net.sergeych.parsec3.KVStorage import net.sergeych.parsec3.MemoryKVStorage import net.sergeych.parsec3.stored import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull +class TextSt(kvs: KVStorage) { + var x: String? by kvs.stored(null) +} + +@Suppress("UNUSED_VARIABLE") internal class MemoryKVStorageTest { @Test @@ -18,12 +24,20 @@ internal class MemoryKVStorageTest { s["foo"] = byteArrayOf(1,2,3) var bar: String? by s.stored(null) + val bal: String? by s.stored(null) assertNull(bar) bar = "foo" println(s.get("bar")?.toDump()) assertEquals("foo", bar) - var foo: String by s.stored("", "bar") + val foo: String by s.stored("", "bar") assertEquals("foo", foo) + + val t1 = TextSt(s) + assertNull(t1.x) + t1.x = "bar" + assertEquals("bar",t1.x) + val t2 = TextSt(s) + assertEquals("bar",t2.x) // // var i: Int? by s(defaultValue = 17) // i = 19 diff --git a/src/jvmMain/kotlin/net/sergeych/parsec3/WsServer.kt b/src/jvmMain/kotlin/net/sergeych/parsec3/WsServer.kt index a605b39..b228ae7 100644 --- a/src/jvmMain/kotlin/net/sergeych/parsec3/WsServer.kt +++ b/src/jvmMain/kotlin/net/sergeych/parsec3/WsServer.kt @@ -6,10 +6,7 @@ import io.ktor.server.websocket.* import io.ktor.websocket.* import net.sergeych.mp_logger.LogTag import net.sergeych.mp_logger.warning -import net.sergeych.unikrypto.DiffieHellman import java.time.Duration -import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicLong /** * Creates a ktor server initialization module capable to perform p3 transport layer (not secure). @@ -52,25 +49,23 @@ fun >Application.parsec3TransportServer( } adapter.close() } - var totalConnections = AtomicLong(0) - var activeConnections = AtomicInteger(0) } } -fun >Application.parsec3SecureServer( - api: H, - exceptionsRegistry: ExceptionsRegistry = ExceptionsRegistry(), - path: String = "/api/p3", - f: AdapterBuilder.() -> Unit, -) { - val log = LogTag("P3WSS") -// parsec3TransportServer(api, ex) -// install(WebSockets) { -// pingPeriod = Duration.ofSeconds(45) -// timeout = Duration.ofSeconds(15) -// maxFrameSize = Long.MAX_VALUE -// masking = false -// } - val dh = DiffieHellman() -} +//fun >Application.parsec3SecureServer( +// api: H, +// exceptionsRegistry: ExceptionsRegistry = ExceptionsRegistry(), +// path: String = "/api/p3", +// f: AdapterBuilder.() -> Unit, +//) { +// val log = LogTag("P3WSS") +//// parsec3TransportServer(api, ex) +//// install(WebSockets) { +//// pingPeriod = Duration.ofSeconds(45) +//// timeout = Duration.ofSeconds(15) +//// maxFrameSize = Long.MAX_VALUE +//// masking = false +//// } +// val dh = DiffieHellman() +//} diff --git a/src/jvmTest/kotlin/net/sergeych/parsec3/WsServerKtTest.kt b/src/jvmTest/kotlin/net/sergeych/parsec3/WsServerKtTest.kt index e19ef82..b8c845f 100644 --- a/src/jvmTest/kotlin/net/sergeych/parsec3/WsServerKtTest.kt +++ b/src/jvmTest/kotlin/net/sergeych/parsec3/WsServerKtTest.kt @@ -31,7 +31,7 @@ internal class WsServerKtTest { @Test fun testWsServer() { - embeddedServer(Netty, port = 8081) { + embeddedServer(Netty, port = 8089) { parsec3TransportServer( TestApiServer, ) { @@ -42,7 +42,7 @@ internal class WsServerKtTest { } }.start(wait = false) - val client = Parsec3WSClient("ws://localhost:8081/api/p3", TestApiClient) { + val client = Parsec3WSClient("ws://localhost:8089/api/p3", TestApiClient) { on(api.bar) { "bar:$it" }