Skip to content

Commit 1905329

Browse files
authored
Avoid crashing when TLS TCP socket is closed (#3690)
* Avoid crashing when TLS TCP socket is closed When using a plain TCP socket with TLS, two worker jobs are spawned: - cio-tls-input-loop - cio-tls-output-loop These worker jobs live in their own scope, and exceptions they throw can't easily be caught by the caller: the only option is to install a `CoroutineExceptionHandler` for the whole TLS socket, which is quite hacky and may hide bugs. When the TCP socket is closed, this is only caught when trying to write on the output channel, which throws a `ClosedSendChannelException`. We now catch that exception and cleanly stop the background job. Fixes https://youtrack.jetbrains.com/issue/KTOR-5178/TLSSocket-cannot-catch-the-exception-thrown-by-appDataOutputLoop Fixes https://youtrack.jetbrains.com/issue/KTOR-4360/Android-Impossible-to-catch-the-ClosedSendChannelException-when-TLS-connection-socket-is-closed * fixup! Avoid crashing when TLS TCP socket is closed
1 parent fa5cba1 commit 1905329

File tree

2 files changed

+16
-1
lines changed

2 files changed

+16
-1
lines changed

ktor-network/ktor-network-tls/jvm/src/io/ktor/network/tls/TLSClientSessionJvm.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ private class TLSSocket(
4848
appDataOutputLoop(this.channel)
4949
}
5050

51-
@OptIn(ExperimentalCoroutinesApi::class)
5251
private suspend fun appDataInputLoop(pipe: ByteWriteChannel) {
5352
try {
5453
input.consumeEach { record ->
@@ -80,6 +79,8 @@ private class TLSSocket(
8079
buffer.flip()
8180
output.send(TLSRecord(TLSRecordType.ApplicationData, packet = buildPacket { writeFully(buffer) }))
8281
}
82+
} catch (_: ClosedSendChannelException) {
83+
// The socket was already closed, we should ignore that error.
8384
} finally {
8485
output.close()
8586
}

ktor-network/ktor-network-tls/jvm/test/io/ktor/network/tls/tests/ConnectionTest.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ class ConnectionTest {
5757
Unit
5858
}
5959

60+
@Test
61+
fun tlsWithCloseTest(): Unit = runBlocking {
62+
val selectorManager = ActorSelectorManager(Dispatchers.IO)
63+
val socket = aSocket(selectorManager)
64+
.tcp()
65+
.connect("www.google.com", port = 443)
66+
.tls(Dispatchers.Default)
67+
68+
val channel = socket.openWriteChannel(autoFlush = true)
69+
socket.close()
70+
assertEquals(42, channel.writeAvailable(ByteArray(42)))
71+
assertTrue(channel.isClosedForWrite)
72+
}
73+
6074
@Test
6175
@Ignore
6276
fun clientCertificatesAuthTest() {

0 commit comments

Comments
 (0)