Skip to content

Commit c5a91b9

Browse files
authored
KTOR-8581 Avoid passing the max parameter of the decode method and catch the MalformedInputException (#4928)
1 parent 030299e commit c5a91b9

3 files changed

Lines changed: 63 additions & 8 deletions

File tree

  • ktor-client
    • ktor-client-plugins/ktor-client-logging/common/src/io/ktor/client/plugins/logging
    • ktor-client-tests/common/test/io/ktor/client/tests
  • ktor-test-server/src/main/kotlin/test/server/tests

ktor-client/ktor-client-plugins/ktor-client-logging/common/src/io/ktor/client/plugins/logging/Logging.kt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -151,17 +151,25 @@ public val Logging: ClientPlugin<LoggingConfig> = createClientPlugin("Logging",
151151
}
152152

153153
val buffer = Buffer().apply { writeFully(firstChunk, 0, firstReadSize) }
154-
val firstChunkText = charset.newDecoder().decode(buffer, firstReadSize)
155154

156-
var lastCharIndex = -1
157-
for (ch in firstChunkText) {
158-
lastCharIndex += 1
155+
val firstChunkText = try {
156+
charset.newDecoder().decode(buffer)
157+
} catch (_: MalformedInputException) {
158+
isBinary = true
159+
""
159160
}
160161

161-
for ((i, ch) in firstChunkText.withIndex()) {
162-
if (ch == '\ufffd' && i != lastCharIndex) {
163-
isBinary = true
164-
break
162+
if (!isBinary) {
163+
var lastCharIndex = -1
164+
for (ch in firstChunkText) {
165+
lastCharIndex += 1
166+
}
167+
168+
for ((i, ch) in firstChunkText.withIndex()) {
169+
if (ch == '\ufffd' && i != lastCharIndex) {
170+
isBinary = true
171+
break
172+
}
165173
}
166174
}
167175

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import kotlin.test.Test
2424
import kotlin.test.assertEquals
2525
import kotlin.test.assertNotNull
2626
import kotlin.test.assertTrue
27+
import kotlin.test.fail
2728

2829
@OptIn(DelicateCoroutinesApi::class)
2930
class LoggingTest : ClientLoader() {
@@ -556,4 +557,44 @@ class LoggingTest : ClientLoader() {
556557
testLogger.verify()
557558
}
558559
}
560+
561+
@Test
562+
fun testBinaryDecodingWithOkHttpFormat() = clientTests(except("web:Js")) {
563+
val lines = mutableListOf<String>()
564+
config {
565+
Logging {
566+
logger = object : Logger {
567+
override fun log(message: String) {
568+
lines.addAll(message.split("\n"))
569+
}
570+
}
571+
level = LogLevel.BODY
572+
format = LoggingFormat.OkHttp
573+
}
574+
}
575+
576+
test { client ->
577+
client.get("$TEST_SERVER/content/binary")
578+
}
579+
580+
after {
581+
val iter = lines.iterator()
582+
583+
assertEquals("--> GET /content/binary", iter.next())
584+
assertEquals("Accept-Charset: UTF-8", iter.next())
585+
assertEquals("Accept: */*", iter.next())
586+
assertEquals("--> END GET", iter.next())
587+
assertMatch(Regex("<-- 200 OK /content/binary \\(\\d+ms\\)"), iter.next())
588+
589+
val lastLine = lines.find { it.startsWith("<-- END HTTP") }
590+
assertNotNull(lastLine)
591+
assertMatch(Regex("<-- END HTTP \\(\\d+ms, binary 8-byte body omitted\\)"), lastLine)
592+
}
593+
}
594+
595+
private fun assertMatch(regex: Regex, actual: String) {
596+
if (!regex.matches(actual)) {
597+
fail("Regex $regex doesn't match $actual")
598+
}
599+
}
559600
}

ktor-test-server/src/main/kotlin/test/server/tests/Content.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ internal fun Application.contentTestServer() {
111111
}
112112
)
113113
}
114+
115+
get("/binary") {
116+
call.respondBytes(contentType = ContentType.Image.PNG) {
117+
byteArrayOf((0x89).toByte(), 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a)
118+
}
119+
}
114120
}
115121
}
116122
}

0 commit comments

Comments
 (0)