4 Commits
6 changed files with 53 additions and 15 deletions

No files matched your search

+16 -6
View File
@@ -1,7 +1,7 @@
# Kiloparsec
__Recommended version is `0.3.3`: to keep the code compatible with current and further versions we
ask to upgrade to `0.3.2` at least.__ Starting from this version some pacakage names are changed for
__Recommended version is `0.4.1`: to keep the code compatible with current and further versions we
ask to upgrade to `0.4.2` at least.__ Starting from this version some pacakage names are changed for
better clarity and fast UDP endpoints are added.
The new generation of __PARanoid SECurity__ protocol, advanced, faster, more secure. It also allows connecting any "
@@ -22,6 +22,10 @@ provides the following transports:
Protocols >= 0.3.0 are not binary compatible with previous version due to more compact binary
format. The format from 0.3.0 onwards is supposed to keep compatible.
#### ID calculation algorithm is changed since 0.4.1
We recommend to upgrade to 0.4+ ASAP as public/shared key id derivation method was changed for even higher security.
### Supported native targets
- iosArm64, iosX64
@@ -33,20 +37,26 @@ format. The format from 0.3.0 onwards is supposed to keep compatible.
- JS (browser and nodeJS)
- JVM (android, macos, windows, linx, everywhere where JRE is installed)
## TCP/IP transport
## TCP/IP and UDP transports
It is the fastest based on async socket implementation of ktor client. It works everywhere but JS target as
These are the fastest based on async socket implementation of ktor client. They works everywhere but JS target as
there is currently no widely adopted sockets for browser javascript.
While UDP is faster than TCP/IP, it is less reliable, especially with commands and return values that serializes to more than 240 bytes approx, and has no retransmission facilities (use TCP!). UDP though shines when all you need is to [push](https://code.sergeych.net/docs/kiloparsec/kiloparsec/net.sergeych.kiloparsec/-remote-interface/push.html) with little or no data in it.
## Websock server
While it is much slower than pure TCP, it is still faster than any http-based transport. It uses binary frames based on
While it is much slower than TCP or UDP, it is still faster than any http-based API; it uses binary frames based on
the Ktor server framework to easily integrate with web services. We recommend using it instead of a classic HTTP API as
it beats it in terms of speed and server load even with HTTP/2.
We recommend to create the `KiloInterface<S>` instance and connect it to the websock and tcp servers in real
applications to get easy access from anywhere.
## Websocket client
It is slower than TCP or UDP, but it works on literally all platforms. See the sample below.
# Usage
The library should be used as maven dependency, not as source.
@@ -70,7 +80,7 @@ It could be, depending on your project structure, something like:
```kotlin
val commonMain by getting {
dependencies {
api("net.sergeych:kiloparsec:0.3.2")
api("net.sergeych:kiloparsec:0.4.1")
}
}
```
+2 -2
View File
@@ -6,7 +6,7 @@ plugins {
}
group = "net.sergeych"
version = "0.3.3"
version = "0.4.1"
repositories {
mavenCentral()
@@ -44,7 +44,7 @@ kotlin {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
api("io.ktor:ktor-client-core:$ktor_version")
api("net.sergeych:crypto2:0.4.2")
api("net.sergeych:crypto2:0.5.3")
}
}
val ktorSocketMain by creating {
@@ -74,7 +74,11 @@ open class LocalInterface<S> : Loggable by LogTag("LocalInterface${idCounter.inc
private val errorByClass = mutableMapOf<KClass<*>, String>()
private val errorBuilder = mutableMapOf<String, (String, UByteArray?) -> Throwable>()
fun <T : Throwable> registerError(
/**
* Register exception for automatic transmission over the kiloparsec connection using its [klass]
* and possibly override code. In most cases it is simpler to use [registerError].
*/
fun <T : Throwable> registerErrorClass(
klass: KClass<T>, code: String = klass.simpleName!!,
exceptionBuilder: (String, UByteArray?) -> T,
) {
@@ -82,10 +86,17 @@ open class LocalInterface<S> : Loggable by LogTag("LocalInterface${idCounter.inc
errorBuilder[code] = exceptionBuilder
}
/**
* Register an exception to be transmitted automatically over the kiloparsec connection.
* Example:
* ```kotlin
* localInterface.registerError { IllegalArgumentException(it) }
* ```
*/
inline fun <reified T : Throwable> registerError(
noinline exceptionBuilder: (String) -> T,
) {
registerError(T::class) { msg, _ -> exceptionBuilder(msg) }
registerErrorClass(T::class) { msg, _ -> exceptionBuilder(msg) }
}
val errorProviders = mutableListOf<LocalInterface<*>>()
@@ -47,7 +47,14 @@ interface RemoteInterface {
suspend fun <R> call(cmd: Command<Unit, R>): R = call(cmd, Unit)
/**
* Call the remote procedure with specified args and return its result
* Call the remote procedure with specified [args] and return its result of type [R]. The calling coroutine
* suspends until the request is performed on the remote side and the result value (also `Unit`) will be received.
*
* When it is not necessary to wait for the return value and/or command execution, it is recommended to
* use [push] instead.
*
* @throws RemoteException if the execution caused exception on the remote size
* @throws Exception for registered exceptions, see [LocalInterface.registerError], etc.
*/
suspend fun <A, R> call(cmd: Command<A, R>, args: A): R
@@ -55,6 +62,8 @@ interface RemoteInterface {
* Push the notification without waiting for reception or processing.
* It returns immediately after sending data to the transport (e.g., to the network).
* Use [call] if it is necessary to wait until the command will be received and processed by the remote.
*
* Push is onlu available for commands without returned value.
*/
suspend fun <A> push(cmd: Command<A, Unit>, args: A)
@@ -231,7 +231,6 @@ class Transport<S>(
.also { debug { "command executed: ${b.name}" } }
}
}
debug { "=---------------------------------------------" }
}
debug { "input step performed closed=$isClosed active=$isActive" }
} catch (_: ClosedSendChannelException) {
@@ -104,7 +104,7 @@ class InternetrTest {
val client = KiloClient<Unit>() {
addErrors(cli)
connect { connectUdpDevice("localhost:$port") }
connect { connectUdpDevice("127.0.0.1:$port") }
}
assertEquals("start", client.call(cmdLoad))
@@ -132,7 +132,7 @@ class InternetrTest {
var data: String,
)
val port = 27170 + Random.nextInt(1, 200)
val port = 27170 + Random.nextInt(1, 900)
val cmdSave by command<String, Unit>()
val cmdLoad by command<Unit, String>()
@@ -154,19 +154,28 @@ class InternetrTest {
}
}
val uServer = UdpServer(port)
KiloServer(cli, uServer.transportFlow) {
Session("unknown")
}
// second server
KiloServer(cli,acceptUdpDevice(uServer.port+1)) { Session("unknown2") }
val client = KiloClient<Unit>() {
addErrors(cli)
connect { connectUdpDevice("localhost:$port") }
connect { connectUdpDevice("127.0.0.1:$port") }
}
val client2 = KiloClient<Unit>() { connect { connectUdpDevice("127.0.0.1:${port+1}") } }
assertEquals("start", client.call(cmdLoad))
assertEquals("start", client2.call(cmdLoad))
client.call(cmdSave, "foobar")
client2.call(cmdSave, "buzz")
assertEquals("foobar", client.call(cmdLoad))
assertEquals("buzz", client2.call(cmdLoad))
val res = kotlin.runCatching { client.call(cmdException) }
assertIs<TestException>(res.exceptionOrNull())