|
| 1 | +@file:OptIn(DelicateCoroutinesApi::class) |
| 2 | + |
| 3 | +package dev.suresh |
| 4 | + |
| 5 | +import io.ktor.network.selector.SelectorManager |
| 6 | +import io.ktor.network.sockets.aSocket |
| 7 | +import io.ktor.network.sockets.awaitClosed |
| 8 | +import io.ktor.network.sockets.openReadChannel |
| 9 | +import io.ktor.network.sockets.openWriteChannel |
| 10 | +import java.net.InetSocketAddress |
| 11 | +import java.time.Duration |
| 12 | +import kotlin.concurrent.thread |
| 13 | +import kotlinx.coroutines.DelicateCoroutinesApi |
| 14 | +import kotlinx.coroutines.async |
| 15 | +import kotlinx.coroutines.newSingleThreadContext |
| 16 | +import kotlinx.coroutines.runBlocking |
| 17 | +import kotlinx.coroutines.test.runTest |
| 18 | +import org.junit.jupiter.api.Assertions.assertEquals |
| 19 | + |
| 20 | +class KtorTests { |
| 21 | + |
| 22 | + // @Test |
| 23 | + fun tcpServer() = runTest { |
| 24 | + val address = InetSocketAddress.createUnresolved("localhost", 9999) |
| 25 | + val selectorManager = SelectorManager(newSingleThreadContext("TcpRunner")) |
| 26 | + |
| 27 | + aSocket(selectorManager).tcp().bind(address.hostName, address.port).use { serverSocket -> |
| 28 | + println("TCP server is listening at ${serverSocket.localAddress}") |
| 29 | + |
| 30 | + repeat(5_000) { |
| 31 | + // If the thread hangs, this won't complete within the timeout |
| 32 | + println("Awaiting new connection") |
| 33 | + val socketAsync = async { serverSocket.accept() } |
| 34 | + aSocket(selectorManager).tcp().connect(address.hostName, address.port) |
| 35 | + val socket = socketAsync.await() |
| 36 | + println("Accepted connection from ${socket.remoteAddress}, $socket") |
| 37 | + |
| 38 | + println("Closing connection to ${socket.remoteAddress}") |
| 39 | + |
| 40 | + val thread = thread { |
| 41 | + val readChannel = socket.openReadChannel() |
| 42 | + val writeChannel = socket.openWriteChannel(autoFlush = true) |
| 43 | + |
| 44 | + socket.close() |
| 45 | + runBlocking { |
| 46 | + // With manual flushAndClose of the WriteChannel, 3.2.0 works. |
| 47 | + // |
| 48 | + // Note: This is _usually_ not needed, only a small percentage of connections |
| 49 | + // fail to close properly without it. |
| 50 | + // writeChannel.flushAndClose() |
| 51 | + socket.awaitClosed() |
| 52 | + } |
| 53 | + } |
| 54 | + |
| 55 | + thread.join(Duration.ofMillis(500)) |
| 56 | + assertEquals(Thread.State.TERMINATED, thread.state) |
| 57 | + } |
| 58 | + } |
| 59 | + } |
| 60 | +} |
0 commit comments