Skip to content

Commit 22536b1

Browse files
committed
Use REPLACE for malformed bytes so truncation preserves declared charset
When FileItemContentReader truncates at MAX_CONTENT_BYTES a cut in the middle of a multibyte character no longer triggers the fallback path. REPLACE substitutes the incomplete sequence with U+FFFD using the declared charset; REPORT was throwing and silently switching to the JVM default charset for the whole string.
1 parent fd0ed70 commit 22536b1

2 files changed

Lines changed: 15 additions & 5 deletions

File tree

internal-api/src/main/java/datadog/trace/api/http/MultipartContentDecoder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ public static String decodeBytes(byte[] buf, int length, String contentType) {
1414
try {
1515
return charset
1616
.newDecoder()
17-
.onMalformedInput(CodingErrorAction.REPORT)
18-
.onUnmappableCharacter(CodingErrorAction.REPORT)
17+
.onMalformedInput(CodingErrorAction.REPLACE)
18+
.onUnmappableCharacter(CodingErrorAction.REPLACE)
1919
.decode(ByteBuffer.wrap(buf, 0, length))
2020
.toString();
2121
} catch (CharacterCodingException e) {

internal-api/src/test/java/datadog/trace/api/http/MultipartContentDecoderTest.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,24 @@ void decodeBytesUsesMachineDefaultFallbackWhenBytesCannotBeDecoded() {
6464
}
6565

6666
@Test
67-
void decodeBytesUsesMachineDefaultFallbackWhenDeclaredCharsetCannotDecodeBytes() {
67+
void decodeBytesReplacesMalformedBytesWithReplacementCharacterUsingDeclaredCharset() {
68+
// 0xE9 (ISO-8859-1 'é') is not valid UTF-8; REPLACE substitutes U+FFFD
6869
byte[] bytes = "café".getBytes(StandardCharsets.ISO_8859_1);
69-
String expected = new String(bytes, Charset.defaultCharset());
7070
assertEquals(
71-
expected,
71+
"caf�",
7272
MultipartContentDecoder.decodeBytes(bytes, bytes.length, "text/plain; charset=UTF-8"));
7373
}
7474

75+
@Test
76+
void decodeBytesHandlesTruncationAtMultibyteCharacterBoundary() {
77+
// "€" encodes as 3 bytes in UTF-8: E2 82 AC
78+
byte[] complete = "hello€".getBytes(StandardCharsets.UTF_8); // 8 bytes
79+
// Pass only 6 bytes: "hello" + first byte of "€" (incomplete sequence)
80+
String result = MultipartContentDecoder.decodeBytes(complete, 6, "text/plain; charset=UTF-8");
81+
// Incomplete sequence → U+FFFD with declared charset, not fallback to JVM default
82+
assertEquals("hello�", result);
83+
}
84+
7585
@ParameterizedTest
7686
@NullAndEmptySource
7787
void extractCharsetReturnsNullForNullOrEmptyContentType(String contentType) {

0 commit comments

Comments
 (0)