docs
This commit is contained in:
parent
439e229294
commit
8a21a836e5
3
.gitignore
vendored
3
.gitignore
vendored
@ -23,8 +23,7 @@ out/
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
bin/
|
||||
.sts4-caches
|
||||
!**/src/main/**/bin/
|
||||
!**/src/test/**/bin/
|
||||
|
||||
|
@ -146,6 +146,11 @@ val ns: NettyApplicationEngine = embeddedServer(Netty, port = 8080, host = "0.0.
|
||||
|
||||
~~~
|
||||
|
||||
## See also:
|
||||
|
||||
- [Source documentation](https://code.sergeych.net/docs/kiloparsec/)
|
||||
- [Project's WIKI](https://gitea.sergeych.net/sergeych/kiloparsec/wiki)
|
||||
|
||||
# Details
|
||||
|
||||
It is not compatible with parsec family and no more based on an Universa crypto library. To better fit
|
||||
|
@ -2,6 +2,7 @@ plugins {
|
||||
kotlin("multiplatform") version "2.0.0"
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "2.0.0"
|
||||
`maven-publish`
|
||||
id("org.jetbrains.dokka") version "1.9.20"
|
||||
}
|
||||
|
||||
group = "net.sergeych"
|
||||
@ -135,3 +136,13 @@ kotlin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.dokkaHtml.configure {
|
||||
outputDirectory.set(buildDir.resolve("dokka"))
|
||||
dokkaSourceSets {
|
||||
configureEach {
|
||||
// includes.from("docs/bipack.md")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,13 +13,97 @@ import net.sergeych.mp_tools.globalLaunch
|
||||
import net.sergeych.tools.AtomicCounter
|
||||
|
||||
private val instances = AtomicCounter()
|
||||
@Suppress("unused")
|
||||
|
||||
/**
|
||||
* The Kiloparsec server.
|
||||
* Server accepts incoming connections and serves them using the same [clientInterface].
|
||||
*
|
||||
* ## Incoming connections
|
||||
*
|
||||
* Server collecting incoming connections provided by [connections] `Flow`. For each incoming connection
|
||||
* the Kiloparsec handshake is performed, then the session object is created, see below, and connection is
|
||||
* served with [clientInterface] until closed.
|
||||
*
|
||||
* ## Session param [S]
|
||||
*
|
||||
* After the successful handshake server creates new session for each connection calling the [sessionBuilder].
|
||||
* Then it creates a [KiloScope] so [KiloScope.session] holds this connection-specific instance. Then
|
||||
* [KiloInterface.onConnected] is called with this scope. Session can be used to hold connection state. Session
|
||||
* objects are not persistent, but could be initialized in [KiloInterface.onConnected] where the remote side
|
||||
* [KiloScope.remoteIdentity] is already verified and set.
|
||||
*
|
||||
* ## Usage:
|
||||
*
|
||||
* Create a shared library between you server and clients, to specify the interface (otherwise you can
|
||||
* share sources).
|
||||
*
|
||||
* Suppose we have session with a state:
|
||||
* ```kotlin
|
||||
* data class Session(
|
||||
* var data: String,
|
||||
* )
|
||||
*```
|
||||
*
|
||||
* And some commands to access and change it, in the shared library too:
|
||||
*
|
||||
* ```kotlin
|
||||
* val cmdSave by command<String, Unit>()
|
||||
* val cmdLoad by command<Unit, String>()
|
||||
* val cmdDrop by command<Unit, Unit>()
|
||||
* val cmdException by command<Unit, Unit>()
|
||||
* ```
|
||||
* Then the server code (TCP/IP variant) could look like:
|
||||
*
|
||||
* ```kotlin
|
||||
* // The server implementation (could be shared between server instances connected
|
||||
* // to different protocol adapters):
|
||||
*
|
||||
* val cli = KiloInterface<Session>().apply {
|
||||
* // Suppose we want to throw this exception at the caller site, so we need to register it:
|
||||
* registerError { SomeException() }
|
||||
*
|
||||
* // Session initialization. If you need a sessino to depend initially on the client's identity.
|
||||
* // you can do it here:
|
||||
* onConnected {
|
||||
* // check the remoteIdentity
|
||||
* session.data = if( remoteIdentity == somePublicVerifyingKey )
|
||||
* "known"
|
||||
* else
|
||||
* "unknown"
|
||||
* }
|
||||
* on(cmdSave) { session.data = it }
|
||||
* on(cmdLoad) {
|
||||
* session.data
|
||||
* }
|
||||
* on(cmdException) {
|
||||
* throw TestException()
|
||||
* }
|
||||
* on(cmdDrop) {
|
||||
* throw LocalInterface.BreakConnectionException()
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // Create the server instance that accepts incoming TCP/IP connections on all local interfaces using the
|
||||
* // specified port:
|
||||
*
|
||||
* val server = KiloServer(cli, acceptTcpDevice(port)) {
|
||||
* // This creates a new session
|
||||
* Session("unknown")
|
||||
* }
|
||||
* ```
|
||||
* @param S the type of the server session object, returned by [sessionBuilder]. See above
|
||||
* @param clientInterface the interface available for remote calls
|
||||
* @param connections flow of incoming connections. Server stops when the flow is fully collected (normally
|
||||
* it shouldn't)
|
||||
* @param serverSecretKey the [SigningKey] used to identify this server during Kiloparsec handshake.
|
||||
* @param sessionBuilder callback that creates session objects for successful incoming connections
|
||||
*/
|
||||
class KiloServer<S>(
|
||||
private val clientInterface: KiloInterface<S>,
|
||||
private val connections: Flow<InetTransportDevice>,
|
||||
private val serverSecretKey: SigningKey? = null,
|
||||
private val sessionBuilder: ()->S,
|
||||
): LogTag("KS:${instances.incrementAndGet()}") {
|
||||
private val sessionBuilder: () -> S,
|
||||
) : LogTag("KS:${instances.incrementAndGet()}") {
|
||||
|
||||
private val job = globalLaunch {
|
||||
connections.collect { device ->
|
||||
@ -27,22 +111,30 @@ class KiloServer<S>(
|
||||
try {
|
||||
info { "connected ${device}" }
|
||||
KiloServerConnection(clientInterface, device, sessionBuilder(), serverSecretKey)
|
||||
.apply { debug { "server connection is ready" }}
|
||||
.apply { debug { "server connection is ready" } }
|
||||
.run()
|
||||
}
|
||||
catch(_: CancellationException) {
|
||||
}
|
||||
catch(cce: LocalInterface.BreakConnectionException) {
|
||||
} catch (_: CancellationException) {
|
||||
} catch (cce: LocalInterface.BreakConnectionException) {
|
||||
info { "Closed exception caught, closing (${cce.flushSendQueue}" }
|
||||
}
|
||||
catch (t: Throwable) {
|
||||
} catch (t: Throwable) {
|
||||
exception { "unexpected while creating kiloclient" to t }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the server and cancel all pending sessions. Unlike finishing the flow passed
|
||||
* for [KiloServer.connections], it will cancel all currently active sessions.
|
||||
*/
|
||||
fun close() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
/**
|
||||
* Server is closed either if [close] was called or all [KiloServer.connections] were collected and flow was
|
||||
* closed.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
val isClosed: Boolean get() = job.isCompleted
|
||||
}
|
@ -15,17 +15,17 @@ class TcpTest {
|
||||
@Test
|
||||
fun tcpTest() = runTest {
|
||||
initCrypto()
|
||||
// Log.connectConsole(Log.Level.DEBUG)
|
||||
data class Session(
|
||||
var data: String
|
||||
)
|
||||
// Log.connectConsole(Log.Level.DEBUG)
|
||||
data class Session(
|
||||
var data: String,
|
||||
)
|
||||
|
||||
val port = 27170 + Random.nextInt(1, 200)
|
||||
|
||||
val cmdSave by command<String, Unit>()
|
||||
val cmdLoad by command<Unit, String>()
|
||||
val cmdDrop by command<Unit, Unit>()
|
||||
val cmdException by command<Unit, Unit>()
|
||||
val cmdSave by command<String, Unit>()
|
||||
val cmdLoad by command<Unit, String>()
|
||||
val cmdDrop by command<Unit, Unit>()
|
||||
val cmdException by command<Unit, Unit>()
|
||||
|
||||
val cli = KiloInterface<Session>().apply {
|
||||
registerError { TestException() }
|
||||
|
Loading…
x
Reference in New Issue
Block a user