This is an automated email from the ASF dual-hosted git repository.

reta pushed a commit to branch 4.0.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/4.0.x-fixes by this push:
     new 1b4b7d655a CXF-9192: Inconsistent best effort read of body by 
web-client when protocol is HTTP/2 (#2792)
1b4b7d655a is described below

commit 1b4b7d655a424a2f6a8c44d460f59bf7527328dd
Author: Andriy Redko <[email protected]>
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)
    
    (cherry picked from commit a2c0fcb567382fbeb4eb950e4e9117d08cc9736d)
    (cherry picked from commit d0180742823ade77527218e023e67ee18aa7842e)
---
 .../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")

Reply via email to