diff --git a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java index c364094af3..363384baae 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -197,6 +197,10 @@ public class Jaxb2XmlDecoder extends AbstractDecoder { catch (XMLStreamException ex) { throw Exceptions.propagate(ex); } + catch (Throwable ex) { + ex = (ex.getCause() instanceof XMLStreamException ? ex.getCause() : ex); + throw Exceptions.propagate(ex); + } finally { DataBufferUtils.release(dataBuffer); } diff --git a/spring-web/src/test/java/org/springframework/http/codec/xml/Jaxb2XmlDecoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/xml/Jaxb2XmlDecoderTests.java index 8e736de9e0..e4135a7c5a 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/xml/Jaxb2XmlDecoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/xml/Jaxb2XmlDecoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -19,11 +19,14 @@ package org.springframework.http.codec.xml; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; +import java.util.Map; import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.XMLEvent; import org.junit.jupiter.api.Test; +import reactor.core.Exceptions; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; @@ -66,6 +69,8 @@ public class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests { "" + ""; + private static final Map HINTS = Collections.emptyMap(); + private final Jaxb2XmlDecoder decoder = new Jaxb2XmlDecoder(); @@ -88,8 +93,7 @@ public class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests { @Test public void splitOneBranches() { - Flux xmlEvents = this.xmlEventDecoder - .decode(stringBuffer(POJO_ROOT), null, null, Collections.emptyMap()); + Flux xmlEvents = this.xmlEventDecoder.decode(toDataBufferMono(POJO_ROOT), null, null, HINTS); Flux> result = this.decoder.split(xmlEvents, new QName("pojo")); StepVerifier.create(result) @@ -109,9 +113,8 @@ public class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests { } @Test - public void splitMultipleBranches() throws Exception { - Flux xmlEvents = this.xmlEventDecoder - .decode(stringBuffer(POJO_CHILD), null, null, Collections.emptyMap()); + public void splitMultipleBranches() { + Flux xmlEvents = this.xmlEventDecoder.decode(toDataBufferMono(POJO_CHILD), null, null, HINTS); Flux> result = this.decoder.split(xmlEvents, new QName("pojo")); @@ -158,10 +161,9 @@ public class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests { } @Test - public void decodeSingleXmlRootElement() throws Exception { - Mono source = stringBuffer(POJO_ROOT); - Mono output = this.decoder.decodeToMono(source, ResolvableType.forClass(Pojo.class), - null, Collections.emptyMap()); + public void decodeSingleXmlRootElement() { + Mono source = toDataBufferMono(POJO_ROOT); + Mono output = this.decoder.decodeToMono(source, ResolvableType.forClass(Pojo.class), null, HINTS); StepVerifier.create(output) .expectNext(new Pojo("foofoo", "barbar")) @@ -170,10 +172,9 @@ public class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests { } @Test - public void decodeSingleXmlTypeElement() throws Exception { - Mono source = stringBuffer(POJO_ROOT); - Mono output = this.decoder.decodeToMono(source, ResolvableType.forClass(TypePojo.class), - null, Collections.emptyMap()); + public void decodeSingleXmlTypeElement() { + Mono source = toDataBufferMono(POJO_ROOT); + Mono output = this.decoder.decodeToMono(source, ResolvableType.forClass(TypePojo.class), null, HINTS); StepVerifier.create(output) .expectNext(new TypePojo("foofoo", "barbar")) @@ -182,10 +183,9 @@ public class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests { } @Test - public void decodeMultipleXmlRootElement() throws Exception { - Mono source = stringBuffer(POJO_CHILD); - Flux output = this.decoder.decode(source, ResolvableType.forClass(Pojo.class), - null, Collections.emptyMap()); + public void decodeMultipleXmlRootElement() { + Mono source = toDataBufferMono(POJO_CHILD); + Flux output = this.decoder.decode(source, ResolvableType.forClass(Pojo.class), null, HINTS); StepVerifier.create(output) .expectNext(new Pojo("foo", "bar")) @@ -195,10 +195,9 @@ public class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests { } @Test - public void decodeMultipleXmlTypeElement() throws Exception { - Mono source = stringBuffer(POJO_CHILD); - Flux output = this.decoder.decode(source, ResolvableType.forClass(TypePojo.class), - null, Collections.emptyMap()); + public void decodeMultipleXmlTypeElement() { + Mono source = toDataBufferMono(POJO_CHILD); + Flux output = this.decoder.decode(source, ResolvableType.forClass(TypePojo.class), null, HINTS); StepVerifier.create(output) .expectNext(new TypePojo("foo", "bar")) @@ -208,19 +207,27 @@ public class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests { } @Test - public void decodeError() throws Exception { + public void decodeError() { Flux source = Flux.concat( - stringBuffer(""), + toDataBufferMono(""), Flux.error(new RuntimeException())); - Mono output = this.decoder.decodeToMono(source, ResolvableType.forClass(Pojo.class), - null, Collections.emptyMap()); + Mono output = this.decoder.decodeToMono(source, ResolvableType.forClass(Pojo.class), null, HINTS); StepVerifier.create(output) .expectError(RuntimeException.class) .verify(); } + @Test // gh-24622 + public void decodeErrorWithXmlNotWellFormed() { + Mono source = toDataBufferMono("something"); + Mono result = this.decoder.decodeToMono(source, ResolvableType.forClass(Pojo.class), null, HINTS); + + StepVerifier.create(result).verifyErrorSatisfies(ex -> + assertThat(Exceptions.unwrap(ex)).isInstanceOf(XMLStreamException.class)); + } + @Test public void toExpectedQName() { assertThat(this.decoder.toQName(Pojo.class)).isEqualTo(new QName("pojo")); @@ -236,7 +243,7 @@ public class Jaxb2XmlDecoderTests extends AbstractLeakCheckingTests { } - private Mono stringBuffer(String value) { + private Mono toDataBufferMono(String value) { return Mono.defer(() -> { byte[] bytes = value.getBytes(StandardCharsets.UTF_8); DataBuffer buffer = this.bufferFactory.allocateBuffer(bytes.length);