Skip to content

Commit 9e5674f

Browse files
committed
KTOR-9411 Darwin: Throw FrameTooBigException instead of generic exception
1 parent 2413814 commit 9e5674f

5 files changed

Lines changed: 33 additions & 17 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/api/ktor-websockets.api

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,9 @@ public final class io/ktor/websocket/FrameParser$State : java/lang/Enum {
180180
}
181181

182182
public final class io/ktor/websocket/FrameTooBigException : java/lang/Exception, kotlinx/coroutines/CopyableThrowable {
183-
public fun <init> (J)V
183+
public synthetic fun <init> (J)V
184+
public fun <init> (JLjava/lang/Throwable;)V
185+
public synthetic fun <init> (JLjava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
184186
public fun createCopy ()Lio/ktor/websocket/FrameTooBigException;
185187
public synthetic fun createCopy ()Ljava/lang/Throwable;
186188
public final fun getFrameSize ()J

ktor-shared/ktor-websockets/api/ktor-websockets.klib.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ final class io.ktor.websocket/CloseReason { // io.ktor.websocket/CloseReason|nul
167167

168168
final class io.ktor.websocket/FrameTooBigException : kotlin/Exception, kotlinx.coroutines/CopyableThrowable<io.ktor.websocket/FrameTooBigException> { // io.ktor.websocket/FrameTooBigException|null[0]
169169
constructor <init>(kotlin/Long) // io.ktor.websocket/FrameTooBigException.<init>|<init>(kotlin.Long){}[0]
170+
constructor <init>(kotlin/Long, kotlin/Throwable? = ...) // io.ktor.websocket/FrameTooBigException.<init>|<init>(kotlin.Long;kotlin.Throwable?){}[0]
170171

171172
final val frameSize // io.ktor.websocket/FrameTooBigException.frameSize|{}frameSize[0]
172173
final fun <get-frameSize>(): kotlin/Long // io.ktor.websocket/FrameTooBigException.frameSize.<get-frameSize>|<get-frameSize>(){}[0]
Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
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

7-
import io.ktor.util.internal.*
8-
import kotlinx.coroutines.*
7+
import kotlinx.coroutines.CopyableThrowable
8+
import kotlinx.coroutines.ExperimentalCoroutinesApi
99

1010
/**
1111
* Raised when the frame is bigger than allowed in a current WebSocket session.
@@ -16,13 +16,18 @@ import kotlinx.coroutines.*
1616
*/
1717
@OptIn(ExperimentalCoroutinesApi::class)
1818
public class FrameTooBigException(
19-
public val frameSize: Long
20-
) : Exception(), CopyableThrowable<FrameTooBigException> {
19+
public val frameSize: Long,
20+
cause: Throwable? = null,
21+
) : Exception(cause), CopyableThrowable<FrameTooBigException> {
22+
23+
@Deprecated("Maintained for binary compatibility", level = DeprecationLevel.HIDDEN)
24+
public constructor(frameSize: Long) : this(frameSize, cause = null)
2125

2226
override val message: String
23-
get() = "Frame is too big: $frameSize"
27+
get() {
28+
val sizeSuffix = if (frameSize >= 0) ": $frameSize" else ""
29+
return "Frame is too big$sizeSuffix"
30+
}
2431

25-
override fun createCopy(): FrameTooBigException = FrameTooBigException(frameSize).also {
26-
it.initCauseBridge(this)
27-
}
32+
override fun createCopy(): FrameTooBigException = FrameTooBigException(frameSize, this)
2833
}

0 commit comments

Comments
 (0)