From 4f380790420ad1b2800dd04cf6a563732af2a2fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Deleuze?= Date: Tue, 9 Jul 2024 12:02:55 +0200 Subject: [PATCH] Throw proper DecodingException in Kotlin Serialization decoders Closes gh-33138 --- .../KotlinSerializationBinaryDecoder.java | 12 +++++++-- .../KotlinSerializationStringDecoder.java | 24 +++++++++++++++-- .../KotlinSerializationJsonDecoderTests.kt | 26 +++++++++++++++++-- ...KotlinSerializationProtobufDecoderTests.kt | 16 ++++++++++++ 4 files changed, 72 insertions(+), 6 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryDecoder.java index 6ecffdd0e5c2..6d703f96bf7c 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationBinaryDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -109,7 +109,15 @@ public Mono decodeToMono(Publisher inputStream, ResolvableTy } return this.byteArrayDecoder .decodeToMono(inputStream, elementType, mimeType, hints) - .map(byteArray -> format().decodeFromByteArray(serializer, byteArray)); + .handle((byteArray, sink) -> { + try { + sink.next(format().decodeFromByteArray(serializer, byteArray)); + sink.complete(); + } + catch (IllegalArgumentException ex) { + sink.error(new DecodingException("Decoding error: " + ex.getMessage(), ex)); + } + }); }); } diff --git a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringDecoder.java index 13a184f149ce..4b71dadf7a56 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/KotlinSerializationStringDecoder.java @@ -26,6 +26,7 @@ import reactor.core.publisher.Mono; import org.springframework.core.ResolvableType; +import org.springframework.core.codec.CodecException; import org.springframework.core.codec.Decoder; import org.springframework.core.codec.DecodingException; import org.springframework.core.codec.StringDecoder; @@ -101,7 +102,14 @@ public Flux decode(Publisher inputStream, ResolvableType ele } return this.stringDecoder .decode(inputStream, elementType, mimeType, hints) - .map(string -> format().decodeFromString(serializer, string)); + .handle((string, sink) -> { + try { + sink.next(format().decodeFromString(serializer, string)); + } + catch (IllegalArgumentException ex) { + sink.error(processException(ex)); + } + }); }); } @@ -115,8 +123,20 @@ public Mono decodeToMono(Publisher inputStream, ResolvableTy } return this.stringDecoder .decodeToMono(inputStream, elementType, mimeType, hints) - .map(string -> format().decodeFromString(serializer, string)); + .handle((string, sink) -> { + try { + sink.next(format().decodeFromString(serializer, string)); + sink.complete(); + } + catch (IllegalArgumentException ex) { + sink.error(processException(ex)); + } + }); }); } + private CodecException processException(IllegalArgumentException ex) { + return new DecodingException("Decoding error: " + ex.getMessage(), ex); + } + } diff --git a/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt b/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt index 6c5fe1b5c5b3..990b35a828b9 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt @@ -21,14 +21,13 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.springframework.core.Ordered import org.springframework.core.ResolvableType +import org.springframework.core.codec.DecodingException import org.springframework.core.io.buffer.DataBuffer import org.springframework.core.testfixture.codec.AbstractDecoderTests import org.springframework.http.MediaType import reactor.core.publisher.Flux import reactor.core.publisher.Mono -import reactor.test.StepVerifier import reactor.test.StepVerifier.FirstStep -import java.lang.UnsupportedOperationException import java.math.BigDecimal import java.nio.charset.Charset import java.nio.charset.StandardCharsets @@ -82,6 +81,29 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests -> + step + .expectError(DecodingException::class.java) + .verify() }, null, null) + } + + @Test + fun decodeToMonoWithUnexpectedFormat() { + val input = Flux.concat( + stringBuffer("{\"ba\":\"b1\",\"fo\":\"f1\"}\n"), + ) + + testDecodeToMono(input, ResolvableType.forClass(Pojo::class.java), { step: FirstStep -> + step.expectError(DecodingException::class.java) + .verify() }, null, null) + } + @Test fun decodeStreamWithSingleBuffer() { val input = Flux.concat( diff --git a/spring-web/src/test/kotlin/org/springframework/http/codec/protobuf/KotlinSerializationProtobufDecoderTests.kt b/spring-web/src/test/kotlin/org/springframework/http/codec/protobuf/KotlinSerializationProtobufDecoderTests.kt index 146e9752d25b..fd57c368e314 100644 --- a/spring-web/src/test/kotlin/org/springframework/http/codec/protobuf/KotlinSerializationProtobufDecoderTests.kt +++ b/spring-web/src/test/kotlin/org/springframework/http/codec/protobuf/KotlinSerializationProtobufDecoderTests.kt @@ -24,6 +24,7 @@ import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.springframework.core.Ordered import org.springframework.core.ResolvableType +import org.springframework.core.codec.DecodingException import org.springframework.core.io.buffer.DataBuffer import org.springframework.core.testfixture.codec.AbstractDecoderTests import org.springframework.http.MediaType @@ -86,6 +87,21 @@ class KotlinSerializationProtobufDecoderTests : AbstractDecoderTests -> + step + .expectError(DecodingException::class.java) + .verify() + }, null, null) + } + private fun byteBuffer(value: Any): Mono { return Mono.defer { val bytes = ProtoBuf.Default.encodeToByteArray(serializer(Pojo::class.java), value)