diff --git a/spring-core/src/main/java/org/springframework/core/codec/AbstractCharSequenceDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/AbstractCharSequenceDecoder.java index 129da247b03..1918d7182db 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/AbstractCharSequenceDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/AbstractCharSequenceDecoder.java @@ -175,14 +175,18 @@ public abstract class AbstractCharSequenceDecoder extend public final T decode(DataBuffer dataBuffer, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { - Charset charset = getCharset(mimeType); - T value = decodeInternal(dataBuffer, charset); - DataBufferUtils.release(dataBuffer); - LogFormatUtils.traceDebug(logger, traceOn -> { - String formatted = LogFormatUtils.formatValue(value, !traceOn); - return Hints.getLogPrefix(hints) + "Decoded " + formatted; - }); - return value; + try { + Charset charset = getCharset(mimeType); + T value = decodeInternal(dataBuffer, charset); + LogFormatUtils.traceDebug(logger, traceOn -> { + String formatted = LogFormatUtils.formatValue(value, !traceOn); + return Hints.getLogPrefix(hints) + "Decoded " + formatted; + }); + return value; + } + finally { + DataBufferUtils.release(dataBuffer); + } } private Charset getCharset(@Nullable MimeType mimeType) { diff --git a/spring-core/src/main/java/org/springframework/core/io/buffer/DataBufferLimitException.java b/spring-core/src/main/java/org/springframework/core/io/buffer/DataBufferLimitException.java index eaeac1b9410..93e403277e2 100644 --- a/spring-core/src/main/java/org/springframework/core/io/buffer/DataBufferLimitException.java +++ b/spring-core/src/main/java/org/springframework/core/io/buffer/DataBufferLimitException.java @@ -31,8 +31,19 @@ package org.springframework.core.io.buffer; public class DataBufferLimitException extends IllegalStateException { + /** + * Create an instance with the given message. + */ public DataBufferLimitException(String message) { super(message); } + /** + * Create an instance with a message and a cause, e.g. {@link OutOfMemoryError}. + * @since 6.2.12 + */ + public DataBufferLimitException(String message, Throwable cause) { + super(message, cause); + } + } diff --git a/spring-core/src/main/java/org/springframework/core/io/buffer/NettyDataBuffer.java b/spring-core/src/main/java/org/springframework/core/io/buffer/NettyDataBuffer.java index 5936eaf8706..8ae4efb38db 100644 --- a/spring-core/src/main/java/org/springframework/core/io/buffer/NettyDataBuffer.java +++ b/spring-core/src/main/java/org/springframework/core/io/buffer/NettyDataBuffer.java @@ -379,7 +379,13 @@ public class NettyDataBuffer implements PooledDataBuffer { @Override public String toString() { - return this.byteBuf.toString(); + try { + return this.byteBuf.toString(); + } + catch (OutOfMemoryError ex) { + throw new DataBufferLimitException( + "Failed to convert data buffer to string: " + ex.getMessage(), ex); + } } diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java index 719012d8f4d..3b337046129 100644 --- a/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/DefaultWebTestClientBuilder.java @@ -67,7 +67,9 @@ class DefaultWebTestClientBuilder implements WebTestClient.Builder { static { ClassLoader loader = DefaultWebTestClientBuilder.class.getClassLoader(); REACTOR_NETTY_CLIENT_PRESENT = ClassUtils.isPresent("reactor.netty.http.client.HttpClient", loader); - JETTY_CLIENT_PRESENT = ClassUtils.isPresent("org.eclipse.jetty.client.HttpClient", loader); + JETTY_CLIENT_PRESENT = + ClassUtils.isPresent("org.eclipse.jetty.client.HttpClient", loader) && + ClassUtils.isPresent("org.eclipse.jetty.reactive.client.ReactiveRequest", loader); HTTP_COMPONENTS_CLIENT_PRESENT = ClassUtils.isPresent("org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient", loader) && ClassUtils.isPresent("org.apache.hc.core5.reactive.ReactiveDataConsumer", loader); diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java index bbab8fea18d..6835ebba144 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultWebClientBuilder.java @@ -64,7 +64,9 @@ final class DefaultWebClientBuilder implements WebClient.Builder { static { ClassLoader loader = DefaultWebClientBuilder.class.getClassLoader(); REACTOR_NETTY_CLIENT_PRESENT = ClassUtils.isPresent("reactor.netty.http.client.HttpClient", loader); - JETTY_CLIENT_PRESENT = ClassUtils.isPresent("org.eclipse.jetty.client.HttpClient", loader); + JETTY_CLIENT_PRESENT = + ClassUtils.isPresent("org.eclipse.jetty.client.HttpClient", loader) && + ClassUtils.isPresent("org.eclipse.jetty.reactive.client.ReactiveRequest", loader); HTTP_COMPONENTS_CLIENT_PRESENT = ClassUtils.isPresent("org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient", loader) && ClassUtils.isPresent("org.apache.hc.core5.reactive.ReactiveDataConsumer", loader);