From 2a8c369cffd4614e7c06b7ee5387f135dd29d38d Mon Sep 17 00:00:00 2001 From: Spring Builds Date: Thu, 31 Mar 2022 09:04:49 +0000 Subject: [PATCH 1/3] Next development version (v5.3.19-SNAPSHOT) --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 75c9b894bcf..03750b79953 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -version=5.3.18-SNAPSHOT +version=5.3.19-SNAPSHOT org.gradle.jvmargs=-Xmx1536M org.gradle.caching=true org.gradle.parallel=true From 24cd3c1f4cfe34c17b79da1dd383112981f99de4 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Fri, 1 Apr 2022 16:07:05 +0100 Subject: [PATCH 2/3] Revert "Disable flaky integration tests for now" This reverts commit 1627f57f1f77abe17dd607c75476b9e4cb22ffbb in preparation for fixing the root cause --- .../result/method/annotation/MultipartIntegrationTests.java | 2 -- .../result/method/annotation/CoroutinesIntegrationTests.kt | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/MultipartIntegrationTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/MultipartIntegrationTests.java index 9f83dff6601..b60587452ac 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/MultipartIntegrationTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/MultipartIntegrationTests.java @@ -25,7 +25,6 @@ import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import org.junit.jupiter.api.Disabled; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @@ -63,7 +62,6 @@ import org.springframework.web.testfixture.http.server.reactive.bootstrap.Undert import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assumptions.assumeFalse; -@Disabled class MultipartIntegrationTests extends AbstractHttpHandlerIntegrationTests { private WebClient webClient; diff --git a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/method/annotation/CoroutinesIntegrationTests.kt b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/method/annotation/CoroutinesIntegrationTests.kt index 56e25eb9fc5..18ab6e1dda7 100644 --- a/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/method/annotation/CoroutinesIntegrationTests.kt +++ b/spring-webflux/src/test/kotlin/org/springframework/web/reactive/result/method/annotation/CoroutinesIntegrationTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2021 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. @@ -24,7 +24,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType -import org.junit.jupiter.api.Disabled import org.springframework.context.ApplicationContext import org.springframework.context.annotation.AnnotationConfigApplicationContext import org.springframework.context.annotation.ComponentScan @@ -40,7 +39,6 @@ import org.springframework.web.testfixture.http.server.reactive.bootstrap.HttpSe import reactor.core.publisher.Flux import java.time.Duration -@Disabled class CoroutinesIntegrationTests : AbstractRequestMappingIntegrationTests() { override fun initApplicationContext(): ApplicationContext { From d518a7d8c81e2cf7484942c37836219b3669240d Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Fri, 1 Apr 2022 17:36:14 +0100 Subject: [PATCH 3/3] AbstractListenerReadPublisher continues after 0 bytes If we read 0 bytes, e.g. chunked encoding markup read but not the actual data within it, don't stop reading since the server may or may not consider it necessary to call onDataAvailable again. Instead, we keep on reading, and although isReady likely returns false on the next iteration, it eliminates ambiguity and ensures the server will call onDataAvailable when more data arrives. Closes gh-28241 --- .../reactive/AbstractListenerReadPublisher.java | 15 ++++++++++++--- .../server/reactive/ServletServerHttpRequest.java | 10 +++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractListenerReadPublisher.java b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractListenerReadPublisher.java index 0845a9f25f0..de1f3ca5a60 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractListenerReadPublisher.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractListenerReadPublisher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -26,6 +26,8 @@ import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import reactor.core.publisher.Operators; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.core.log.LogDelegateFactory; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -56,6 +58,8 @@ public abstract class AbstractListenerReadPublisher implements Publisher { */ protected static Log rsReadLogger = LogDelegateFactory.getHiddenLog(AbstractListenerReadPublisher.class); + final static DataBuffer EMPTY_BUFFER = DefaultDataBufferFactory.sharedInstance.allocateBuffer(0); + private final AtomicReference state = new AtomicReference<>(State.UNSUBSCRIBED); @@ -180,7 +184,7 @@ public abstract class AbstractListenerReadPublisher implements Publisher { /** * Read and publish data one at a time until there is no more data, no more - * demand, or perhaps we completed in the mean time. + * demand, or perhaps we completed meanwhile. * @return {@code true} if there is more demand; {@code false} if there is * no more demand or we have completed. */ @@ -188,7 +192,12 @@ public abstract class AbstractListenerReadPublisher implements Publisher { long r; while ((r = this.demand) > 0 && (this.state.get() != State.COMPLETED)) { T data = read(); - if (data != null) { + if (data == EMPTY_BUFFER) { + if (rsReadLogger.isTraceEnabled()) { + rsReadLogger.trace(getLogPrefix() + "0 bytes read, trying again"); + } + } + else if (data != null) { if (r != Long.MAX_VALUE) { DEMAND_FIELD_UPDATER.addAndGet(this, -1L); } diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java index a84ddc6d6e3..7183234e1d4 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/ServletServerHttpRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 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. @@ -236,10 +236,10 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest { /** * Read from the request body InputStream and return a DataBuffer. * Invoked only when {@link ServletInputStream#isReady()} returns "true". - * @return a DataBuffer with data read, or {@link #EOF_BUFFER} if the input - * stream returned -1, or null if 0 bytes were read. + * @return a DataBuffer with data read, or + * {@link AbstractListenerReadPublisher#EMPTY_BUFFER} if 0 bytes were read, + * or {@link #EOF_BUFFER} if the input stream returned -1. */ - @Nullable DataBuffer readFromInputStream() throws IOException { int read = this.request.getInputStream().read(this.buffer); logBytesRead(read); @@ -254,7 +254,7 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest { return EOF_BUFFER; } - return null; + return AbstractListenerReadPublisher.EMPTY_BUFFER; } protected final void logBytesRead(int read) {