Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26564b6081 | ||
|
|
9ddb1209c9 | ||
|
|
b68232653a | ||
|
|
2e4f551e8e |
No files matched your search
@@ -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
@@ -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())
|
||||
|
||||
Reference in new issue
Block a user