refactor syslog server to use Dispatchers.IO and SupervisorScope (try to fix connection refused error)

This commit is contained in:
Adam 2022-10-16 12:34:07 +02:00
parent 71386ded3b
commit 0f9e51df31

View file

@ -16,12 +16,12 @@ import io.ktor.utils.io.readUTF8Line
import java.util.EnumSet import java.util.EnumSet
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.coroutines.yield import kotlinx.coroutines.yield
@ -56,53 +56,47 @@ class SyslogSocketServer(
get() = _messages.asSharedFlow() get() = _messages.asSharedFlow()
suspend fun start(): Unit = coroutineScope { suspend fun start(): Unit = withContext(Dispatchers.IO) {
log("started listening: ${serverSocketTcp.localAddress}") log("started listening: ${serverSocketTcp.localAddress}")
while (isActive) { while (isActive) {
val socket: Socket = serverSocketTcp.accept() val socket: Socket = serverSocketTcp.accept()
log("new socket ${socket.description()}")
launch { launch {
val channel = socket.openReadChannel() log("new socket ${socket.description()}")
try { handleConnection(socket)
while (socket.socketContext.isActive && !channel.isClosedForRead) {
val line = runCatching {
channel.readUTF8Line()
}.fold(
onSuccess = { line ->
if (line == null) {
log("received 'null' from ${socket.description()} - closing connection")
socket.close()
}
// log("received '$line' from ${socket.description()}")
line
},
onFailure = { ex ->
if (ex is CancellationException) throw ex
log("reading line from socket failed ${socket.description()}, cause: $ex")
null
},
)
if (line != null) {
val syslog = SyslogMsg(syslogParser.parseLine(line))
_messages.emit(syslog)
}
yield()
}
} catch (e: Exception) {
if (e is CancellationException) throw e
log("closing socket ${socket.description()}, cause: ${e::class.java.name} ${e.message}")
e.printStackTrace()
withContext(Dispatchers.IO) { socket.close() }
}
} }
yield() yield()
} }
} }
private suspend fun handleConnection(
socket: Socket
) = supervisorScope {
try {
val channel = socket.openReadChannel()
while (socket.socketContext.isActive && !channel.isClosedForRead) {
val line = channel.readUTF8Line()
requireNotNull(line) {
"received 'null' line from ${socket.description()}"
}
val syslog = SyslogMsg(syslogParser.parseLine(line))
_messages.emit(syslog)
yield()
}
} catch (e: Throwable) {
if (e is CancellationException) throw e
log("closing socket ${socket.description()}, cause: ${e::class.java.name} ${e.message}")
e.printStackTrace()
withContext(Dispatchers.IO) { socket.close() }
}
}
companion object { companion object {
private fun log(msg: String) = println("[SyslogSocketServer] $msg") private fun log(msg: String) = println("[SyslogSocketServer] $msg")