Skip to content

Commit e4ecf01

Browse files
committed
KTOR-9411 Darwin: Throw FrameTooBigException instead of generic exception
1 parent cdf5f68 commit e4ecf01

3 files changed

Lines changed: 24 additions & 11 deletions

File tree

ktor-client/ktor-client-darwin/darwin/src/io/ktor/client/engine/darwin/internal/DarwinWebsocketSession.kt

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2014-2026 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package io.ktor.client.engine.darwin.internal
@@ -8,20 +8,22 @@ import io.ktor.client.engine.darwin.*
88
import io.ktor.client.request.*
99
import io.ktor.http.*
1010
import io.ktor.util.date.*
11-
import io.ktor.utils.io.InternalAPI
11+
import io.ktor.utils.io.*
1212
import io.ktor.utils.io.core.*
1313
import io.ktor.websocket.*
1414
import kotlinx.cinterop.ExperimentalForeignApi
1515
import kotlinx.cinterop.UnsafeNumber
1616
import kotlinx.cinterop.convert
1717
import kotlinx.coroutines.*
18+
import kotlinx.coroutines.CancellationException
1819
import kotlinx.coroutines.channels.Channel
1920
import kotlinx.coroutines.channels.ReceiveChannel
2021
import kotlinx.coroutines.channels.SendChannel
2122
import kotlinx.coroutines.channels.consumeEach
2223
import kotlinx.io.readByteArray
2324
import platform.Foundation.*
2425
import platform.darwin.NSInteger
26+
import platform.posix.EMSGSIZE
2527
import kotlin.coroutines.CoroutineContext
2628
import kotlin.coroutines.resume
2729
import kotlin.coroutines.resumeWithException
@@ -204,7 +206,7 @@ internal class DarwinWebsocketSession(
204206
return
205207
}
206208

207-
val exception = DarwinHttpRequestException(error)
209+
val exception = convertWebsocketError(error)
208210
response.completeExceptionally(exception)
209211
socketJob.completeExceptionally(exception)
210212
}
@@ -239,7 +241,7 @@ private suspend fun NSURLSessionWebSocketTask.receiveMessage(): NSURLSessionWebS
239241
return@receiveMessageWithCompletionHandler
240242
}
241243

242-
it.resumeWithException(DarwinHttpRequestException(error))
244+
it.resumeWithException(convertWebsocketError(error))
243245
return@receiveMessageWithCompletionHandler
244246
}
245247
if (message == null) {
@@ -254,3 +256,11 @@ private suspend fun NSURLSessionWebSocketTask.receiveMessage(): NSURLSessionWebS
254256
@OptIn(UnsafeNumber::class)
255257
@Suppress("REDUNDANT_CALL_OF_CONVERSION_METHOD")
256258
internal fun NSURLSessionTask.getStatusCode() = (response() as NSHTTPURLResponse?)?.statusCode?.toInt()
259+
260+
@OptIn(UnsafeNumber::class, ExperimentalForeignApi::class)
261+
private fun convertWebsocketError(error: NSError): Exception = when {
262+
error.domain == NSPOSIXErrorDomain && error.code.convert<Int>() == EMSGSIZE -> {
263+
FrameTooBigException(frameSize = -1L, DarwinHttpRequestException(error))
264+
}
265+
else -> DarwinHttpRequestException(error)
266+
}

ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/WebSocketTest.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -547,9 +547,7 @@ class WebSocketTest : ClientLoader(except(ENGINES_WITHOUT_WS)) {
547547
val shortMessage = "abc"
548548
val longMessage = "def".repeat(500)
549549
test { client ->
550-
// TODO: KTOR-9411 Darwin throws DarwinHttpRequestException instead of FrameTooBigException
551-
// Replace Exception with FrameTooBigException after fix.
552-
assertFailsWith<Exception> {
550+
assertFailsWith<FrameTooBigException> {
553551
client.webSocket("$TEST_WEBSOCKET_SERVER/websockets/echo") {
554552
send(shortMessage)
555553
assertEquals(shortMessage, (incoming.receive() as Frame.Text).readText())

ktor-shared/ktor-websockets/common/src/io/ktor/websocket/FrameTooBigException.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
/*
2-
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2014-2026 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package io.ktor.websocket
66

77
import io.ktor.util.internal.*
8-
import kotlinx.coroutines.*
8+
import kotlinx.coroutines.CopyableThrowable
9+
import kotlinx.coroutines.ExperimentalCoroutinesApi
910

1011
/**
1112
* Raised when the frame is bigger than allowed in a current WebSocket session.
@@ -16,8 +17,12 @@ import kotlinx.coroutines.*
1617
*/
1718
@OptIn(ExperimentalCoroutinesApi::class)
1819
public class FrameTooBigException(
19-
public val frameSize: Long
20-
) : Exception(), CopyableThrowable<FrameTooBigException> {
20+
public val frameSize: Long,
21+
cause: Throwable? = null,
22+
) : Exception(cause), CopyableThrowable<FrameTooBigException> {
23+
24+
@Deprecated("Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
25+
public constructor(frameSize: Long) : this(frameSize, cause = null)
2126

2227
override val message: String
2328
get() = "Frame is too big: $frameSize"

0 commit comments

Comments
 (0)