You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by re...@apache.org on 2025/12/27 19:58:39 UTC
(cxf) branch main updated: CXF-9192: Inconsistent best effort read of body by web-client when protocol is HTTP/2 (#2792)
This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/main by this push:
new a2c0fcb567 CXF-9192: Inconsistent best effort read of body by web-client when protocol is HTTP/2 (#2792)
a2c0fcb567 is described below
commit a2c0fcb567382fbeb4eb950e4e9117d08cc9736d
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Sat Dec 27 14:58:33 2025 -0500
CXF-9192: Inconsistent best effort read of body by web-client when protocol is HTTP/2 (#2792)
---
.../cxf/transport/http/HttpClientHTTPConduit.java | 20 ++++++++++++--
.../AbstractJettyClientServerHttp2Test.java | 31 ++++++++++++++++++++++
.../apache/cxf/systest/http2_jetty/BookStore.java | 25 +++++++++++++++++
3 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java
index 59a0a977c1..5efbef4c1e 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HttpClientHTTPConduit.java
@@ -1147,6 +1147,7 @@ public class HttpClientHTTPConduit extends URLConnectionHTTPConduit {
}
@Override
+ @SuppressWarnings("checkstyle:NestedIfDepth")
protected InputStream getInputStream() throws IOException {
HttpResponse<InputStream> resp = getResponse();
String method = (String)outMessage.get(Message.HTTP_REQUEST_METHOD);
@@ -1172,9 +1173,24 @@ public class HttpClientHTTPConduit extends URLConnectionHTTPConduit {
}
} else if (!fChunk.isPresent() || !"chunked".equals(fChunk.get())) {
if (resp.version() == Version.HTTP_2) {
- InputStream in = resp.body();
+ final InputStream in = resp.body();
+ // The InputStream::available is a best effort, if it returns 0, issuing
+ // the InputStream::read will either block if data is expected or return
+ // immediately
if (in.available() <= 0) {
- try (in) {
+ final PushbackInputStream pbin = new PushbackInputStream(in);
+ try {
+ // HttpResponseInputStream will block if there is data to be read
+ final int c = pbin.read();
+ if (c != -1) {
+ pbin.unread((byte) c);
+ return new HttpClientFilteredInputStream(pbin);
+ }
+ } catch (final IOException ex) {
+ // ignore
+ }
+
+ try (pbin) {
return null;
}
}
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/AbstractJettyClientServerHttp2Test.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/AbstractJettyClientServerHttp2Test.java
index 386a59a993..98fa6c4f25 100644
--- a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/AbstractJettyClientServerHttp2Test.java
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/AbstractJettyClientServerHttp2Test.java
@@ -21,6 +21,7 @@ package org.apache.cxf.systest.http2_jetty;
import jakarta.ws.rs.core.Response;
import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.jaxrs.client.ClientConfiguration;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.systest.http2_jetty.Http2TestClient.ClientResponse;
import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
@@ -108,6 +109,36 @@ abstract class AbstractJettyClientServerHttp2Test extends AbstractBusClientServe
assertEquals("CXF in Action", resp.readEntity(String.class));
}
}
+
+ @Test
+ public void testBookWithHttp2Redirect() throws Exception {
+ final WebClient wc = WebClient
+ .create(getAddress() + getContext() + "/web/bookstore/redirect")
+ .accept("application/xml");
+
+ final ClientConfiguration config = WebClient.getConfig(wc);
+ config.getRequestContext().put(HTTPConduit.FORCE_HTTP_VERSION, "2");
+ config.getHttpConduit().getClient().setAutoRedirect(false);
+
+ if (isSecure()) {
+ final HTTPConduit conduit = WebClient.getConfig(wc).getHttpConduit();
+ TLSClientParameters params = conduit.getTlsClientParameters();
+
+ if (params == null) {
+ params = new TLSClientParameters();
+ conduit.setTlsClientParameters(params);
+ }
+
+ // Create TrustManager instance which trusts all clients and servers
+ params.setTrustManagers(InsecureTrustManager.getNoOpX509TrustManagers());
+ params.setDisableCNCheck(true);
+ }
+
+ try (Response resp = wc.get()) {
+ assertThat(resp.getStatus(), equalTo(307));
+ assertEquals("Error while getting books", resp.readEntity(String.class));
+ }
+ }
protected abstract String getAddress();
protected abstract String getContext();
diff --git a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookStore.java b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookStore.java
index bbc3c51e20..90a31eb64b 100644
--- a/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookStore.java
+++ b/systests/transports/src/test/java/org/apache/cxf/systest/http2_jetty/BookStore.java
@@ -20,12 +20,20 @@
package org.apache.cxf.systest.http2_jetty;
import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.LockSupport;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.StreamingOutput;
+import jakarta.ws.rs.core.UriInfo;
import org.apache.cxf.jaxrs.ext.StreamingResponse;
@Path("/web/bookstore")
@@ -39,6 +47,23 @@ public class BookStore {
return "CXF in Action".getBytes();
}
+ @GET
+ @Path("/redirect")
+ @Produces("application/xml")
+ public Response redirect(@Context final UriInfo info) {
+ return Response
+ .temporaryRedirect(info.getBaseUriBuilder().path("/web/bookstore/bookstream").build())
+ .entity(new StreamingOutput() {
+ @Override
+ public void write(OutputStream out) throws IOException {
+ LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(500));
+ out.write("Error while getting books".getBytes(StandardCharsets.UTF_8));
+ out.flush();
+ }
+ })
+ .build();
+ }
+
@GET
@Path("/bookstream")
@Produces("application/xml")