93 lines
3.3 KiB
Kotlin
93 lines
3.3 KiB
Kotlin
package net.sergeych.kiloparsec.adapter
|
|
|
|
import io.ktor.server.application.*
|
|
import io.ktor.server.routing.*
|
|
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.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.kiloparsec.RemoteInterface
|
|
import net.sergeych.mp_logger.*
|
|
import net.sergeych.tools.AtomicCounter
|
|
import java.time.Duration
|
|
|
|
fun <S> Application.setupWebsocketServer(
|
|
localInterface: KiloInterface<S>,
|
|
path: String = "/kp",
|
|
serverKey: SigningKey? = null,
|
|
createSession: () -> S,
|
|
) {
|
|
|
|
install(Routing)
|
|
install(WebSockets) {
|
|
pingPeriod = Duration.ofSeconds(15)
|
|
timeout = Duration.ofSeconds(15)
|
|
maxFrameSize = Long.MAX_VALUE
|
|
masking = false
|
|
}
|
|
val counter = AtomicCounter()
|
|
routing {
|
|
webSocket(path) {
|
|
val log = LogTag("KWS:${counter.incrementAndGet()}")
|
|
log.debug { "opening the connection" }
|
|
val input = Channel<UByteArray>(256)
|
|
val output = Channel<UByteArray>(256)
|
|
launch {
|
|
log.debug { "starting output pump" }
|
|
while (isActive) {
|
|
try {
|
|
send(output.receive().toByteArray())
|
|
}
|
|
catch(_: ClosedReceiveChannelException) {
|
|
log.debug { "closing output pump as output channel is closed" }
|
|
break
|
|
}
|
|
}
|
|
log.debug { "closing output pump" }
|
|
}
|
|
val server = KiloServerConnection(
|
|
localInterface,
|
|
ProxyDevice(input, output),
|
|
createSession(),
|
|
serverKey
|
|
)
|
|
launch {
|
|
server.run()
|
|
close()
|
|
}
|
|
log.debug { "KSC started, looking for incoming frames" }
|
|
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.warning { "caught local closed exception (strange!), 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" }
|
|
close()
|
|
cancel()
|
|
log.debug { "server wbesock processing done" }
|
|
}
|
|
}
|
|
}
|