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 2021/11/10 00:34:12 UTC

[cxf] branch 3.4.x-fixes updated: CXF-8616: Calling oneway methods using async client hangs response indefinitely (#872)

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

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


The following commit(s) were added to refs/heads/3.4.x-fixes by this push:
     new fc916e1  CXF-8616: Calling oneway methods using async client hangs response indefinitely (#872)
fc916e1 is described below

commit fc916e199db82f3564bf97490fd529c143ffe440
Author: Andriy Redko <dr...@gmail.com>
AuthorDate: Tue Nov 9 17:15:20 2021 -0500

    CXF-8616: Calling oneway methods using async client hangs response indefinitely (#872)
    
    (cherry picked from commit b2eafdee7243776527531cfdeec2f82980424783)
---
 .../main/java/org/apache/cxf/message/Message.java  |  7 ++
 .../apache/cxf/jaxrs/client/AbstractClient.java    |  2 +
 .../org/apache/cxf/transport/http/HTTPConduit.java |  7 +-
 .../org/apache/cxf/systest/jaxrs/BookStore.java    |  5 ++
 .../cxf/systest/jaxrs/JAXRSAsyncClientTest.java    | 37 +++++++--
 .../systest/jaxrs/JAXRSClientServerBookTest.java   | 87 +++++++++++-----------
 6 files changed, 92 insertions(+), 53 deletions(-)

diff --git a/core/src/main/java/org/apache/cxf/message/Message.java b/core/src/main/java/org/apache/cxf/message/Message.java
index bcb8e04..5a74b61 100644
--- a/core/src/main/java/org/apache/cxf/message/Message.java
+++ b/core/src/main/java/org/apache/cxf/message/Message.java
@@ -92,6 +92,13 @@ public interface Message extends StringMap {
      * Default value is true
      */
     String PROCESS_202_RESPONSE_ONEWAY_OR_PARTIAL = "org.apache.cxf.transport.process202Response";
+    
+    /**
+     * Boolean property specifying if 202 response is partial/oneway response, should it be
+     * propagated down to message observers or not.
+     * Default value is false.
+     */
+    String PROPAGATE_202_RESPONSE_ONEWAY_OR_PARTIAL = "org.apache.cxf.transport.propagate202Response"; 
 
     /**
      * Boolean property specifying if the thread which runs a request is
diff --git a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
index 1ca60dc..bba3873 100644
--- a/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
+++ b/rt/rs/client/src/main/java/org/apache/cxf/jaxrs/client/AbstractClient.java
@@ -955,6 +955,8 @@ public abstract class AbstractClient implements Client {
     }
 
     protected void setSupportOnewayResponseProperty(Message outMessage) {
+        // Do propagate the response down to observer chain
+        outMessage.put(Message.PROPAGATE_202_RESPONSE_ONEWAY_OR_PARTIAL, true);
         if (!outMessage.getExchange().isOneWay()) {
             outMessage.put(Message.PROCESS_ONEWAY_RESPONSE, true);
         }
diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
index c2d104e..85e5a7f 100644
--- a/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
+++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/http/HTTPConduit.java
@@ -1661,8 +1661,13 @@ public abstract class HTTPConduit
                         }
                     }
                     exchange.put("IN_CHAIN_COMPLETE", Boolean.TRUE);
-                    
+
                     exchange.setInMessage(inMessage);
+                    if (MessageUtils.getContextualBoolean(outMessage, 
+                            Message.PROPAGATE_202_RESPONSE_ONEWAY_OR_PARTIAL, false)) {
+                        incomingObserver.onMessage(inMessage);
+                    }
+
                     return;
                 }
             } else {
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
index 2d6516e..1810e53 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/BookStore.java
@@ -1544,6 +1544,11 @@ public class BookStore {
     }
 
     @POST
+    @Path("/no-content")
+    public void noContent() {
+    }
+
+    @POST
     @Path("/books/customstatus")
     @Produces("application/xml")
     @Consumes("text/xml")
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSAsyncClientTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSAsyncClientTest.java
index 15e4c04..c8c8add 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSAsyncClientTest.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSAsyncClientTest.java
@@ -26,6 +26,7 @@ import java.io.OutputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
@@ -53,6 +54,7 @@ import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.ext.MessageBodyReader;
 import javax.ws.rs.ext.MessageBodyWriter;
+import javax.ws.rs.ext.Provider;
 import javax.xml.ws.Holder;
 
 import org.apache.cxf.jaxrs.client.ClientConfiguration;
@@ -71,6 +73,7 @@ import org.junit.Test;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -525,9 +528,28 @@ public class JAXRSAsyncClientTest extends AbstractBusClientServerTestBase {
             webClient.close();
         }
     }
+
+    @Test
+    public void testBookNoContent() throws Exception {
+        final String address = "http://localhost:" + PORT + "/bookstore/no-content";
+        WebClient client = createWebClient(address);
+        Response r = client.type("*/*").async().post(null).get();
+        assertEquals(204, r.getStatus());
+        assertThat(r.readEntity(String.class), equalTo(""));
+    }
     
-    private WebClient createWebClient(String address) {
-        return WebClient.create(address);
+    @Test
+    public void testBookOneway() throws Exception {
+        final String address = "http://localhost:" + PORT + "/bookstore/oneway";
+        WebClient client = createWebClient(address, new TestResponseFilter());
+        Response r = client.type("*/*").async().post(null).get();
+        assertEquals(202, r.getStatus());
+        assertThat(r.getEntity(), is(nullValue()));
+        assertThat(r.getHeaderString("X-Filter"), equalTo("true"));
+    }
+
+    private WebClient createWebClient(String address, Object ... providers) {
+        return WebClient.create(address, Arrays.asList(providers));
     }
 
     private InvocationCallback<Object> createCallback(final Holder<Object> holder) {
@@ -561,8 +583,8 @@ public class JAXRSAsyncClientTest extends AbstractBusClientServerTestBase {
             throw new RuntimeException();
 
         }
-
     }
+    
     @Consumes("application/xml")
     private static class FaultyBookReader implements MessageBodyReader<Book> {
 
@@ -577,19 +599,18 @@ public class JAXRSAsyncClientTest extends AbstractBusClientServerTestBase {
             WebApplicationException {
             throw new RuntimeException();
         }
-
-
     }
-
+    
+    @Provider
     public static class TestResponseFilter implements ClientResponseFilter {
 
         @Override
         public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext)
             throws IOException {
-
+            responseContext.getHeaders().add("X-Filter", "true");
         }
-
     }
+    
     private static class GenericInvocationCallback<T> implements InvocationCallback<T> {
         private Object result;
 
diff --git a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
index 1c1885b..fb5260e 100644
--- a/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
+++ b/systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java
@@ -45,6 +45,9 @@ import javax.ws.rs.ServerErrorException;
 import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.client.Client;
 import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientResponseContext;
+import javax.ws.rs.client.ClientResponseFilter;
 import javax.ws.rs.client.Entity;
 import javax.ws.rs.client.ResponseProcessingException;
 import javax.ws.rs.client.WebTarget;
@@ -56,6 +59,7 @@ import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Variant;
+import javax.ws.rs.ext.Provider;
 import javax.ws.rs.ext.ReaderInterceptor;
 import javax.xml.bind.JAXBElement;
 import javax.xml.namespace.QName;
@@ -94,12 +98,14 @@ import org.apache.http.util.EntityUtils;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import static org.hamcrest.CoreMatchers.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -111,8 +117,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
     @BeforeClass
     public static void startServers() throws Exception {
         AbstractResourceInfo.clearAllMaps();
-        assertTrue("server did not launch correctly",
-                   launchServer(BookServer.class, true));
+        assertTrue("server did not launch correctly", launchServer(BookServer.class, true));
         createStaticBus();
     }
 
@@ -357,7 +362,6 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
         String address = "http://localhost:" + PORT + "/bookstore/customresponse";
         WebClient wc = WebClient.create(address);
         Response r = wc.accept("application/xml").get(Response.class);
-
         r.bufferEntity();
 
         String bookStr = r.readEntity(String.class);
@@ -436,7 +440,6 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
         assertEquals(123L, book.getId());
     }
 
-
     @Test
     public void testGetIntroChapterFromSelectedBook() {
         String address = "http://localhost:" + PORT + "/bookstore/books(id=le=123)/chapter";
@@ -480,8 +483,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testProxyWrongAddress() throws Exception {
-        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT2 + "/wrongaddress",
-                                                    BookStore.class);
+        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT2 + "/wrongaddress", BookStore.class);
         try {
             store.getBook("123");
             fail("ClientException expected");
@@ -563,7 +565,6 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
         MultivaluedMap<String, Object> headers = wc.getResponse().getMetadata();
         assertEquals("123", headers.getFirst("BookId"));
         assertEquals(MultivaluedMap.class.getName(), headers.getFirst("MAP-NAME"));
-
         assertNotNull(headers.getFirst("Date"));
 
         wc.header("PLAIN-MAP", "true");
@@ -573,7 +574,6 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
         headers = wc.getResponse().getMetadata();
         assertEquals("321", headers.getFirst("BookId"));
         assertEquals(Map.class.getName(), headers.getFirst("MAP-NAME"));
-
         assertNotNull(headers.getFirst("Date"));
     }
 
@@ -674,8 +674,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testPostCollectionGetBooksWebClient() throws Exception {
-        String endpointAddress =
-            "http://localhost:" + PORT + "/bookstore/collections3";
+        String endpointAddress = "http://localhost:" + PORT + "/bookstore/collections3";
         WebClient wc = WebClient.create(endpointAddress);
         wc.accept("application/xml").type("application/xml");
         Book b1 = new Book("CXF in Action", 123L);
@@ -691,8 +690,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testPostCollectionGenericEntityWebClient() throws Exception {
-        String endpointAddress =
-            "http://localhost:" + PORT + "/bookstore/collections3";
+        String endpointAddress = "http://localhost:" + PORT + "/bookstore/collections3";
         WebClient wc = WebClient.create(endpointAddress);
         wc.accept("application/xml").type("application/xml");
         Book b1 = new Book("CXF in Action", 123L);
@@ -712,8 +710,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testPostGetCollectionGenericEntityAndType() throws Exception {
-        String endpointAddress =
-            "http://localhost:" + PORT + "/bookstore/collections";
+        String endpointAddress = "http://localhost:" + PORT + "/bookstore/collections";
         WebClient wc = WebClient.create(endpointAddress);
         wc.accept("application/xml").type("application/xml");
         Book b1 = new Book("CXF in Action", 123L);
@@ -744,8 +741,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testPostCollectionOfBooksWebClient() throws Exception {
-        String endpointAddress =
-            "http://localhost:" + PORT + "/bookstore/collections";
+        String endpointAddress = "http://localhost:" + PORT + "/bookstore/collections";
         WebClient wc = WebClient.create(endpointAddress);
         wc.accept("application/xml").type("application/xml");
         Book b1 = new Book("CXF in Action", 123L);
@@ -778,8 +774,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testPostObjectGetCollection() throws Exception {
-        String endpointAddress =
-            "http://localhost:" + PORT + "/bookstore/collectionBook";
+        String endpointAddress = "http://localhost:" + PORT + "/bookstore/collectionBook";
         WebClient wc = WebClient.create(endpointAddress);
         wc.accept("application/xml").type("application/xml");
         Book b1 = new Book("Book", 666L);
@@ -793,8 +788,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testCaching() throws Exception {
-        String endpointAddress =
-            "http://localhost:" + PORT + "/bookstore/books/response/123";
+        String endpointAddress = "http://localhost:" + PORT + "/bookstore/books/response/123";
 
         // Add the CacheControlFeature to cache books returned by the service on the client side
         try (CacheControlFeature cacheControlFeature = new CacheControlFeature()) {
@@ -832,8 +826,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testCachingExpires() throws Exception {
-        String endpointAddress =
-            "http://localhost:" + PORT + "/bookstore/books/response2/123";
+        String endpointAddress = "http://localhost:" + PORT + "/bookstore/books/response2/123";
 
         // Add the CacheControlFeature to cache books returned by the service on the client side
         try (CacheControlFeature cacheControlFeature = new CacheControlFeature()) {
@@ -873,8 +866,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testCachingExpiresUsingETag() throws Exception {
-        String endpointAddress =
-            "http://localhost:" + PORT + "/bookstore/books/response3/123";
+        String endpointAddress = "http://localhost:" + PORT + "/bookstore/books/response3/123";
 
         // Add the CacheControlFeature to cache books returned by the service on the client side
         try (CacheControlFeature cacheControlFeature = new CacheControlFeature()) {
@@ -921,6 +913,16 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
         assertEquals(202, r.getStatus());
         assertFalse(r.getHeaders().isEmpty());
     }
+    
+    @Test
+    public void testOnewayWebClientWithResponseFilter() throws Exception {
+        final ClientResponseFilter filter = new TestClientResponseFilter();
+        WebClient client = WebClient.create("http://localhost:" + PORT + "/bookstore/oneway", Arrays.asList(filter));
+        Response r = client.header("OnewayRequest", "true").post(null);
+        assertEquals(202, r.getStatus());
+        assertFalse(r.getHeaders().isEmpty());
+        assertThat(r.getHeaderString("X-Filter"), equalTo("true"));
+    }
 
     @Test
     public void testOnewayWebClient2() throws Exception {
@@ -972,8 +974,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testBookWithSpaceProxyNonEncodedSemicolon() throws Exception {
-        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT,
-                                                    BookStore.class);
+        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT, BookStore.class);
         Book book = store.getBookWithSemicolon("123;", "custom;:header");
         assertEquals(123L, book.getId());
         assertEquals("CXF in Action;", book.getName());
@@ -993,9 +994,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
         List<Object> providers = new LinkedList<>();
         providers.add(new BookServer.NotReturnedExceptionMapper());
         providers.add(new BookServer.NotFoundExceptionMapper());
-        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT,
-                                                    BookStore.class,
-                                                    providers);
+        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT, BookStore.class, providers);
         try {
             store.getBookWithExceptions(true);
             fail();
@@ -1012,8 +1011,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testBookWithExceptionsNoMapper() throws Exception {
-        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT,
-                                                    BookStore.class);
+        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT, BookStore.class);
         try {
             store.getBookWithExceptions(true);
             fail();
@@ -1027,9 +1025,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
         List<Object> providers = new LinkedList<>();
         providers.add(new BookServer.NotReturnedExceptionMapper());
         providers.add(BookServer.NotFoundExceptionMapper.class);
-        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT,
-                                                    BookStore.class,
-                                                    providers);
+        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT, BookStore.class, providers);
         try {
             store.getBookWithExceptions2(true);
             fail();
@@ -1242,7 +1238,6 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
     @Test
     public void testAddBookProxyResponse() {
         Book b = new Book("CXF rocks", 123L);
-
         BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT, BookStore.class);
         Response r = store.addBook(b);
         assertNotNull(r);
@@ -1276,15 +1271,12 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testGetJAXBElementXmlRootBookCollection() throws Exception {
-        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT,
-                                                    BookStore.class);
+        BookStore store = JAXRSClientFactory.create("http://localhost:" + PORT, BookStore.class);
         Book b1 = new Book("CXF in Action", 123L);
         Book b2 = new Book("CXF Rocks", 124L);
         List<JAXBElement<Book>> books = new ArrayList<>();
-        books.add(new JAXBElement<Book>(new QName("bookRootElement"),
-            Book.class, b1));
-        books.add(new JAXBElement<Book>(new QName("bookRootElement"),
-            Book.class, b2));
+        books.add(new JAXBElement<Book>(new QName("bookRootElement"), Book.class, b1));
+        books.add(new JAXBElement<Book>(new QName("bookRootElement"), Book.class, b2));
         List<JAXBElement<Book>> books2 = store.getJAXBElementBookXmlRootCollection(books);
         assertNotNull(books2);
         assertNotSame(books, books2);
@@ -1298,8 +1290,7 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
     }
     @Test
     public void testGetJAXBElementXmlRootBookCollectionWebClient() throws Exception {
-        WebClient store = WebClient.create("http://localhost:" + PORT
-                                           + "/bookstore/jaxbelementxmlrootcollections");
+        WebClient store = WebClient.create("http://localhost:" + PORT + "/bookstore/jaxbelementxmlrootcollections");
         Book b1 = new Book("CXF in Action", 123L);
         Book b2 = new Book("CXF Rocks", 124L);
         List<Book> books = new ArrayList<>();
@@ -2657,7 +2648,6 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
 
     @Test
     public void testGetCDJSON() throws Exception {
-
         getAndCompareAsStrings("http://localhost:" + PORT + "/bookstore/cd/123",
                                "resources/expected_get_cdjson.txt",
                                "application/json", "application/json", 200);
@@ -2981,4 +2971,13 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase {
         }
         return str;
     }
+    
+    @Provider
+    private static class TestClientResponseFilter implements ClientResponseFilter {
+        @Override
+        public void filter(ClientRequestContext requestContext,
+                ClientResponseContext responseContext) throws IOException {
+            responseContext.getHeaders().add("X-Filter", "true");
+        }
+    }
 }