work in porgress: fixing close connection
This commit is contained in:
		
							parent
							
								
									c0ca802a30
								
							
						
					
					
						commit
						93dc66acc5
					
				@ -31,7 +31,8 @@ class Command<A, R>(
 | 
			
		||||
    suspend fun exec(packedArgs: UByteArray, handler: suspend (A) -> R): UByteArray =
 | 
			
		||||
        BipackEncoder.encode(
 | 
			
		||||
            resultSerializer,
 | 
			
		||||
            handler(BipackDecoder.decode(packedArgs.toDataSource(), argsSerializer))
 | 
			
		||||
            handler(
 | 
			
		||||
                BipackDecoder.decode(packedArgs.toDataSource(), argsSerializer))
 | 
			
		||||
        ).toUByteArray()
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
 | 
			
		||||
@ -56,6 +56,7 @@ class KiloClient<S>(
 | 
			
		||||
                    delay(1000)
 | 
			
		||||
                } catch (_: CancellationException) {
 | 
			
		||||
                    debug { "cancelled" }
 | 
			
		||||
                    break
 | 
			
		||||
                } catch (t: Throwable) {
 | 
			
		||||
                    exception { "unexpected exception" to t }
 | 
			
		||||
                    delay(1000)
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,15 @@
 | 
			
		||||
package net.sergeych.kiloparsec
 | 
			
		||||
 | 
			
		||||
import kotlinx.coroutines.CompletableDeferred
 | 
			
		||||
import net.sergeych.mp_logger.LogTag
 | 
			
		||||
import net.sergeych.mp_logger.Loggable
 | 
			
		||||
import net.sergeych.mp_logger.debug
 | 
			
		||||
import net.sergeych.mp_logger.info
 | 
			
		||||
import net.sergeych.tools.AtomicCounter
 | 
			
		||||
import net.sergeych.utools.pack
 | 
			
		||||
 | 
			
		||||
private val idCounter = AtomicCounter(0)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class is not normally used directly. This is a local interface that supports
 | 
			
		||||
 * secure transport command layer (encrypted calls/results) to work with [KiloRemoteInterface].
 | 
			
		||||
@ -12,7 +19,7 @@ import net.sergeych.utools.pack
 | 
			
		||||
internal class KiloL0Interface<T>(
 | 
			
		||||
    private val clientInterface: LocalInterface<KiloScope<T>>,
 | 
			
		||||
    private val deferredParams: CompletableDeferred<KiloParams<T>>,
 | 
			
		||||
): LocalInterface<Unit>() {
 | 
			
		||||
) : LocalInterface<Unit>(), Loggable by LogTag("KL0:${idCounter.incrementAndGet()}") {
 | 
			
		||||
    init {
 | 
			
		||||
        // local interface uses the same session as a client:
 | 
			
		||||
        addErrorProvider(clientInterface)
 | 
			
		||||
@ -27,7 +34,10 @@ internal class KiloL0Interface<T>(
 | 
			
		||||
                    0u,
 | 
			
		||||
                    clientInterface.execute(params.scope, call.name, call.serializedArgs)
 | 
			
		||||
                )
 | 
			
		||||
            } catch (t: Throwable) {
 | 
			
		||||
            } catch(t: RemoteInterface.ClosedException) {
 | 
			
		||||
                throw t
 | 
			
		||||
            }
 | 
			
		||||
            catch (t: Throwable) {
 | 
			
		||||
                clientInterface.encodeError(0u, t)
 | 
			
		||||
            }
 | 
			
		||||
            params.encrypt(pack(result))
 | 
			
		||||
 | 
			
		||||
@ -36,7 +36,7 @@ data class KiloParams<S>(
 | 
			
		||||
    val sessionKeyPair: KeyExchangeSessionKeyPair,
 | 
			
		||||
    val scopeSession: S,
 | 
			
		||||
    val remoteIdentity: SigningKey.Public?,
 | 
			
		||||
    val remoteTransport: RemoteInterface
 | 
			
		||||
    val remoteTransport: RemoteInterface,
 | 
			
		||||
) {
 | 
			
		||||
    @Serializable
 | 
			
		||||
    data class Package(
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,9 @@ class KiloServer<S>(
 | 
			
		||||
                }
 | 
			
		||||
                catch(_: CancellationException) {
 | 
			
		||||
                }
 | 
			
		||||
                catch(_: RemoteInterface.ClosedException) {
 | 
			
		||||
                    info { "Closed exception caught, closing" }
 | 
			
		||||
                }
 | 
			
		||||
                catch (t: Throwable) {
 | 
			
		||||
                    exception { "unexpected while creating kiloclient" to t }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,6 @@ class KiloServerConnection<S>(
 | 
			
		||||
                    null,
 | 
			
		||||
                    this@KiloServerConnection
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                Handshake(1u, pair.publicKey, serverSigningKey?.seal(params!!.token))
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,17 @@
 | 
			
		||||
package net.sergeych.kiloparsec
 | 
			
		||||
 | 
			
		||||
import net.sergeych.mp_logger.LogTag
 | 
			
		||||
import net.sergeych.mp_logger.Loggable
 | 
			
		||||
import net.sergeych.mp_logger.info
 | 
			
		||||
import net.sergeych.tools.AtomicCounter
 | 
			
		||||
import net.sergeych.utools.firstNonNull
 | 
			
		||||
import kotlin.reflect.KClass
 | 
			
		||||
 | 
			
		||||
private typealias RawCommandHandler<C> = suspend (C, UByteArray) -> UByteArray
 | 
			
		||||
 | 
			
		||||
open class LocalInterface<S> {
 | 
			
		||||
private val idCounter = AtomicCounter()
 | 
			
		||||
 | 
			
		||||
open class LocalInterface<S>: Loggable by LogTag("LocalInterface${idCounter.incrementAndGet()}") {
 | 
			
		||||
 | 
			
		||||
    private val commands = mutableMapOf<String, RawCommandHandler<S>>()
 | 
			
		||||
 | 
			
		||||
@ -72,14 +78,16 @@ open class LocalInterface<S> {
 | 
			
		||||
 | 
			
		||||
    fun encodeError(forId: UInt, t: Throwable): Transport.Block.Error =
 | 
			
		||||
        getErrorCode(t)?.let { Transport.Block.Error(forId, it, t.message) }
 | 
			
		||||
            ?: Transport.Block.Error(forId, "UnknownError", t.message)
 | 
			
		||||
            ?: Transport.Block.Error(forId, "UnknownError", "${t::class.simpleName}: ${t.message}")
 | 
			
		||||
 | 
			
		||||
    open fun getErrorBuilder(code: String): ((String, UByteArray?) -> Throwable)? =
 | 
			
		||||
        errorBuilder[code] ?: errorProviders.firstNonNull { it.getErrorBuilder(code) }
 | 
			
		||||
 | 
			
		||||
    fun decodeError(tbe: Transport.Block.Error): Throwable =
 | 
			
		||||
        getErrorBuilder(tbe.code)?.invoke(tbe.message, tbe.extra)
 | 
			
		||||
            ?: RemoteInterface.RemoteException(tbe)
 | 
			
		||||
            ?: RemoteInterface.RemoteException(tbe).also {
 | 
			
		||||
                info { "can't decode error ${tbe.code}: ${tbe.message}" }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
    fun decodeAndThrow(tbe: Transport.Block.Error): Nothing {
 | 
			
		||||
        throw decodeError(tbe)
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
package net.sergeych.kiloparsec
 | 
			
		||||
 | 
			
		||||
import kotlinx.coroutines.*
 | 
			
		||||
import kotlinx.coroutines.channels.ClosedReceiveChannelException
 | 
			
		||||
import kotlinx.coroutines.channels.ClosedSendChannelException
 | 
			
		||||
import kotlinx.coroutines.channels.ReceiveChannel
 | 
			
		||||
import kotlinx.coroutines.channels.SendChannel
 | 
			
		||||
import kotlinx.coroutines.sync.Mutex
 | 
			
		||||
@ -147,6 +149,12 @@ class Transport<S>(
 | 
			
		||||
            debug { "awaiting incoming blocks" }
 | 
			
		||||
            while (isActive && !isClosed) {
 | 
			
		||||
                try {
 | 
			
		||||
                    debug { "input step starting closed=$isClosed active=$isActive"}
 | 
			
		||||
                    if( isClosed ) {
 | 
			
		||||
                        info { "breaking transort loop on closed"}
 | 
			
		||||
                        break
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    device.input.receive().let { packed ->
 | 
			
		||||
                        debug { "<<<\n${packed.toDump()}" }
 | 
			
		||||
                        val b = unpack<Block>(packed)
 | 
			
		||||
@ -178,9 +186,11 @@ class Transport<S>(
 | 
			
		||||
                                        )
 | 
			
		||||
                                    )
 | 
			
		||||
                                } catch (x: RemoteInterface.ClosedException) {
 | 
			
		||||
                                    // strange case: handler throws closed?
 | 
			
		||||
                                    error { "not supported: command handler for $b has thrown ClosedException" }
 | 
			
		||||
                                    send(Block.Error(b.id, "UnexpectedException", x.message))
 | 
			
		||||
                                    // handler forced close
 | 
			
		||||
                                    warning { "handler requested closing of the connection"}
 | 
			
		||||
                                    isClosed = true
 | 
			
		||||
                                    runCatching { device.close() }
 | 
			
		||||
                                    throw x
 | 
			
		||||
                                } catch (x: RemoteInterface.RemoteException) {
 | 
			
		||||
                                    send(Block.Error(b.id, x.code, x.text, x.extra))
 | 
			
		||||
                                } catch (t: Throwable) {
 | 
			
		||||
@ -189,19 +199,34 @@ class Transport<S>(
 | 
			
		||||
                                    .also { debug { "command executed: ${b.name}" } }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        debug { "=---------------------------------------------"}
 | 
			
		||||
                    }
 | 
			
		||||
                } catch (_: CancellationException) {
 | 
			
		||||
                    debug { "input step performed closed=$isClosed active=$isActive"}
 | 
			
		||||
                } catch (_: ClosedSendChannelException) {
 | 
			
		||||
                    info { "closed send channel" }
 | 
			
		||||
                    isClosed = true
 | 
			
		||||
                } catch (_: ClosedReceiveChannelException) {
 | 
			
		||||
                    info { "closed receive channel"}
 | 
			
		||||
                    isClosed = true
 | 
			
		||||
                }
 | 
			
		||||
                catch (_: CancellationException) {
 | 
			
		||||
                    info { "loop is cancelled" }
 | 
			
		||||
                    isClosed = true
 | 
			
		||||
                } catch( _: RemoteInterface.ClosedException) {
 | 
			
		||||
                    debug { "git closed exception here, ignoring" }
 | 
			
		||||
                    isClosed = true
 | 
			
		||||
                } catch (t: Throwable) {
 | 
			
		||||
                    exception { "channel closed on error" to t }
 | 
			
		||||
                    info { "isa? $isActive / $isClosed" }
 | 
			
		||||
                    runCatching { device.close() }
 | 
			
		||||
                    isClosed = true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            debug { "leaving transport loop" }
 | 
			
		||||
            access.withLock {
 | 
			
		||||
                debug { "access lock obtained"}
 | 
			
		||||
                isClosed = true
 | 
			
		||||
                debug { "closgin device $device" }
 | 
			
		||||
                runCatching { device.close() }
 | 
			
		||||
                for (c in calls.values) c.completeExceptionally(RemoteInterface.ClosedException())
 | 
			
		||||
                calls.clear()
 | 
			
		||||
            }
 | 
			
		||||
@ -211,8 +236,13 @@ class Transport<S>(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun send(block: Block) {
 | 
			
		||||
        try {
 | 
			
		||||
            device.output.send(pack(block))
 | 
			
		||||
        }
 | 
			
		||||
        catch(_: ClosedSendChannelException) {
 | 
			
		||||
            throw RemoteInterface.ClosedException()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@ package net.sergeych.kiloparsec.adapter
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.channels.ReceiveChannel
 | 
			
		||||
import kotlinx.coroutines.channels.SendChannel
 | 
			
		||||
import net.sergeych.kiloparsec.RemoteInterface
 | 
			
		||||
import net.sergeych.kiloparsec.Transport
 | 
			
		||||
import net.sergeych.tools.AtomicCounter
 | 
			
		||||
 | 
			
		||||
@ -10,7 +11,7 @@ private val counter = AtomicCounter()
 | 
			
		||||
open class ProxyDevice(
 | 
			
		||||
    inputChannel: Channel<UByteArray>,
 | 
			
		||||
    outputChannel: Channel<UByteArray>,
 | 
			
		||||
    private val onClose: suspend ()->Unit = {}): Transport.Device {
 | 
			
		||||
    private val onClose: suspend ()->Unit = { throw RemoteInterface.ClosedException() }): Transport.Device {
 | 
			
		||||
 | 
			
		||||
    override val input: ReceiveChannel<UByteArray> = inputChannel
 | 
			
		||||
    override val output: SendChannel<UByteArray> = outputChannel
 | 
			
		||||
 | 
			
		||||
@ -7,8 +7,12 @@ import io.ktor.websocket.*
 | 
			
		||||
import kotlinx.coroutines.CancellationException
 | 
			
		||||
import kotlinx.coroutines.cancel
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.channels.ClosedReceiveChannelException
 | 
			
		||||
import kotlinx.coroutines.channels.ClosedSendChannelException
 | 
			
		||||
import kotlinx.coroutines.channels.ReceiveChannel
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import net.sergeych.crypto2.SigningKey
 | 
			
		||||
import net.sergeych.crypto2.toDump
 | 
			
		||||
import net.sergeych.kiloparsec.KiloClient
 | 
			
		||||
import net.sergeych.kiloparsec.KiloConnectionData
 | 
			
		||||
import net.sergeych.kiloparsec.KiloInterface
 | 
			
		||||
@ -55,23 +59,29 @@ fun <S>websocketClient(
 | 
			
		||||
                    println("SENDING!!!")
 | 
			
		||||
                    send("Helluva")
 | 
			
		||||
                    launch {
 | 
			
		||||
                        try {
 | 
			
		||||
                            for (block in output) {
 | 
			
		||||
                                send(block.toByteArray())
 | 
			
		||||
                            }
 | 
			
		||||
                            log.info { "input is closed, closing the websocket" }
 | 
			
		||||
                        } catch (_: ClosedSendChannelException) {
 | 
			
		||||
                            log.info { "send channel closed" }
 | 
			
		||||
                        }
 | 
			
		||||
                        cancel()
 | 
			
		||||
                    }
 | 
			
		||||
                    for (f in incoming) {
 | 
			
		||||
                        if (f is Frame.Binary) {
 | 
			
		||||
                            input.send(f.readBytes().toUByteArray())
 | 
			
		||||
                            input.send(f.readBytes().toUByteArray().also {
 | 
			
		||||
                                println("incoming\n${it.toDump()}")
 | 
			
		||||
                            })
 | 
			
		||||
                        } else {
 | 
			
		||||
                            log.warning { "ignoring unexpected frame of type ${f.frameType}" }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch(_:CancellationException) {
 | 
			
		||||
                }
 | 
			
		||||
                catch(t: Throwable) {
 | 
			
		||||
                } catch (_: CancellationException) {
 | 
			
		||||
                } catch( _: ClosedReceiveChannelException) {
 | 
			
		||||
                    log.warning { "receive channel closed unexpectedly" }
 | 
			
		||||
                } catch (t: Throwable) {
 | 
			
		||||
                    log.exception { "unexpected error" to t }
 | 
			
		||||
                }
 | 
			
		||||
                log.info { "closing connection" }
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ inline fun <reified T: Throwable>assertThrows(f: ()->Unit): T {
 | 
			
		||||
    }
 | 
			
		||||
    catch(x: Throwable) {
 | 
			
		||||
        if( x is T ) return x
 | 
			
		||||
        fail("expected to throw $name but instead threw ${x::class.simpleName}: $x")
 | 
			
		||||
        println("expected to throw $name but instead threw ${x::class.simpleName}: $x\b\n${x.stackTraceToString()}")
 | 
			
		||||
        fail("expected to throw $name but instead threw ${x::class.simpleName}: $x\b\n${x.stackTraceToString()}")
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -6,15 +6,16 @@ import io.ktor.server.websocket.*
 | 
			
		||||
import io.ktor.websocket.*
 | 
			
		||||
import kotlinx.coroutines.cancel
 | 
			
		||||
import kotlinx.coroutines.channels.Channel
 | 
			
		||||
import kotlinx.coroutines.channels.ClosedReceiveChannelException
 | 
			
		||||
import kotlinx.coroutines.channels.ClosedSendChannelException
 | 
			
		||||
import kotlinx.coroutines.isActive
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import net.sergeych.crypto2.SigningKey
 | 
			
		||||
import net.sergeych.crypto2.toDump
 | 
			
		||||
import net.sergeych.kiloparsec.KiloInterface
 | 
			
		||||
import net.sergeych.kiloparsec.KiloServerConnection
 | 
			
		||||
import net.sergeych.mp_logger.LogTag
 | 
			
		||||
import net.sergeych.mp_logger.debug
 | 
			
		||||
import net.sergeych.mp_logger.warning
 | 
			
		||||
import net.sergeych.kiloparsec.RemoteInterface
 | 
			
		||||
import net.sergeych.mp_logger.*
 | 
			
		||||
import net.sergeych.tools.AtomicCounter
 | 
			
		||||
import java.time.Duration
 | 
			
		||||
 | 
			
		||||
@ -49,7 +50,7 @@ fun <S> Application.setupWebsocketServer(
 | 
			
		||||
            }
 | 
			
		||||
            val server = KiloServerConnection(
 | 
			
		||||
                localInterface,
 | 
			
		||||
                ProxyDevice(input, output) { input.close() },
 | 
			
		||||
                ProxyDevice(input, output),
 | 
			
		||||
                createSession(),
 | 
			
		||||
                serverKey
 | 
			
		||||
            )
 | 
			
		||||
@ -58,14 +59,27 @@ fun <S> Application.setupWebsocketServer(
 | 
			
		||||
            for (f in incoming) {
 | 
			
		||||
                log.debug { "incoming frame: ${f.frameType}" }
 | 
			
		||||
                if (f is Frame.Binary)
 | 
			
		||||
                    try {
 | 
			
		||||
                        input.send(f.readBytes().toUByteArray().also {
 | 
			
		||||
                            log.debug { "in frame\n${it.toDump()}" }
 | 
			
		||||
                        })
 | 
			
		||||
                    } catch (_: RemoteInterface.ClosedException) {
 | 
			
		||||
                        log.info { "caught local closed exception, closing" }
 | 
			
		||||
                        break
 | 
			
		||||
                    } catch (_: ClosedReceiveChannelException) {
 | 
			
		||||
                        log.info { "receive channel is closed, closing connection" }
 | 
			
		||||
                        break
 | 
			
		||||
                    } catch (t: Throwable) {
 | 
			
		||||
                        log.exception { "unexpected exception, server connection will close" to t }
 | 
			
		||||
                        break
 | 
			
		||||
                    }
 | 
			
		||||
                else
 | 
			
		||||
                    log.warning { "unknown frame type ${f.frameType}, ignoring" }
 | 
			
		||||
            }
 | 
			
		||||
            log.debug { "closing the server" }
 | 
			
		||||
            println("****************prec")
 | 
			
		||||
            cancel()
 | 
			
		||||
            println("****************postc")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,11 @@
 | 
			
		||||
package net.sergeych.kiloparsec
 | 
			
		||||
 | 
			
		||||
import assertThrows
 | 
			
		||||
import io.ktor.server.engine.*
 | 
			
		||||
import io.ktor.server.netty.*
 | 
			
		||||
import kotlinx.coroutines.delay
 | 
			
		||||
import kotlinx.coroutines.flow.take
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.coroutines.test.runTest
 | 
			
		||||
import net.sergeych.crypto2.initCrypto
 | 
			
		||||
import net.sergeych.kiloparsec.adapter.acceptTcpDevice
 | 
			
		||||
@ -10,9 +14,7 @@ import net.sergeych.kiloparsec.adapter.setupWebsocketServer
 | 
			
		||||
import net.sergeych.kiloparsec.adapter.websocketClient
 | 
			
		||||
import net.sergeych.mp_logger.Log
 | 
			
		||||
import java.net.InetAddress
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertEquals
 | 
			
		||||
import kotlin.test.assertTrue
 | 
			
		||||
import kotlin.test.*
 | 
			
		||||
 | 
			
		||||
class ClientTest {
 | 
			
		||||
 | 
			
		||||
@ -57,6 +59,7 @@ class ClientTest {
 | 
			
		||||
    fun webSocketTest() = runTest {
 | 
			
		||||
        initCrypto()
 | 
			
		||||
//        fun Application.
 | 
			
		||||
        val cmdClose by command<Unit,Unit>()
 | 
			
		||||
        val cmdGetFoo by command<Unit,String>()
 | 
			
		||||
        val cmdSetFoo by command<String,Unit>()
 | 
			
		||||
        val cmdCheckConnected by command<Unit,Boolean>()
 | 
			
		||||
@ -64,12 +67,23 @@ class ClientTest {
 | 
			
		||||
        Log.connectConsole(Log.Level.DEBUG)
 | 
			
		||||
 | 
			
		||||
        data class Session(var foo: String="not set")
 | 
			
		||||
        var closeCounter = 0
 | 
			
		||||
        val serverInterface = KiloInterface<Session>().apply {
 | 
			
		||||
            var connectedCalled = false
 | 
			
		||||
            onConnected { connectedCalled = true }
 | 
			
		||||
            on(cmdGetFoo) { session.foo }
 | 
			
		||||
            on(cmdSetFoo) { session.foo = it }
 | 
			
		||||
            on(cmdCheckConnected) { connectedCalled }
 | 
			
		||||
            on(cmdClose) {
 | 
			
		||||
                throw RemoteInterface.ClosedException()
 | 
			
		||||
//                if( closeCounter < 2 ) {
 | 
			
		||||
//                    println("-------------------------- call close!")
 | 
			
		||||
//                    throw RemoteInterface.ClosedException()
 | 
			
		||||
//                }
 | 
			
		||||
//                else {
 | 
			
		||||
//                    println("close counter $closeCounter, ignoring")
 | 
			
		||||
//                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
//       val server = setupWebsoketServer()
 | 
			
		||||
        val ns: NettyApplicationEngine = embeddedServer(Netty, port = 8080, host = "0.0.0.0", module = {
 | 
			
		||||
@ -77,6 +91,14 @@ class ClientTest {
 | 
			
		||||
        }).start(wait = false)
 | 
			
		||||
 | 
			
		||||
        val client = websocketClient<Unit>("ws://localhost:8080/kp")
 | 
			
		||||
        val states = mutableListOf<Boolean>()
 | 
			
		||||
        val collector = launch {
 | 
			
		||||
            client.state.collect {
 | 
			
		||||
                println("got: $closeCounter/$it")
 | 
			
		||||
                states += it
 | 
			
		||||
                if( !it) { closeCounter++ }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        println(1)
 | 
			
		||||
        assertEquals(true, client.call(cmdCheckConnected))
 | 
			
		||||
        assertTrue { client.state.value }
 | 
			
		||||
@ -87,9 +109,16 @@ class ClientTest {
 | 
			
		||||
        println(4)
 | 
			
		||||
        assertEquals("foo", client.call(cmdGetFoo))
 | 
			
		||||
        println(5)
 | 
			
		||||
 | 
			
		||||
        assertThrows<RemoteInterface.ClosedException> {
 | 
			
		||||
            client.call(cmdClose)
 | 
			
		||||
        }
 | 
			
		||||
        println("0------------------------------------------------------------------------------connection should be closed")
 | 
			
		||||
//        assertFalse { client.state.value }
 | 
			
		||||
//        assertEquals("foo", client.call(cmdGetFoo))
 | 
			
		||||
        client.close()
 | 
			
		||||
        ns.stop()
 | 
			
		||||
        collector.cancel()
 | 
			
		||||
        println("----= states: $states")
 | 
			
		||||
        println("stopped server")
 | 
			
		||||
        println("closed client")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user