This is an automated email from the ASF dual-hosted git repository. reta pushed a commit to branch 3.3.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 2303b1077181cf08b10f86fb219174def87e1478 Author: Andriy Redko <[email protected]> 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) (cherry picked from commit fc916e199db82f3564bf97490fd529c143ffe440) # Conflicts: # systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSAsyncClientTest.java # systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSClientServerBookTest.java --- .../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 | 82 +++++++++++++++++++-- .../systest/jaxrs/JAXRSClientServerBookTest.java | 86 +++++++++++----------- 6 files changed, 137 insertions(+), 52 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 511dba4..78c884b 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 @@ -978,6 +978,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 8e3b132..577b435 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 @@ -1660,8 +1660,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 e601712..1d8fdfb 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 c8341c9..afe5f68 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; @@ -52,6 +53,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; @@ -68,6 +70,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; @@ -477,9 +480,73 @@ public class JAXRSAsyncClientTest extends AbstractBusClientServerTestBase { assertThat(response.getStatus(), equalTo(404)); } } + + @Test + public void testNettyClientGet() throws Exception { + final String address = "http://localhost:" + PORT + "/bookstore/books/wildcard"; + + JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean(); + bean.setTransportId(NettyHttpTransportFactory.DEFAULT_NAMESPACES.get(0)); + bean.setAddress(address); + + WebClient webClient = bean.createWebClient(); + + try (Response response = webClient + .to(address, false) + .accept("text/plain") + .async() + .get() + .get(10, TimeUnit.SECONDS)) { + assertThat(response.getStatus(), equalTo(200)); + } finally { + webClient.close(); + } + } - private WebClient createWebClient(String address) { - return WebClient.create(address); + @Test + public void testNettyClientDeleteWithBody() throws Exception { + final String address = "http://localhost:" + PORT + "/bookstore/deletebody"; + + JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean(); + bean.setTransportId(NettyHttpTransportFactory.DEFAULT_NAMESPACES.get(0)); + bean.setAddress(address); + + WebClient webClient = bean.createWebClient(); + + try { + Book book = webClient + .to(address, false) + .accept("application/xml") + .async() + .method("DELETE", Entity.entity(new Book("Delete", 123L), "application/xml"), Book.class) + .get(20, TimeUnit.SECONDS); + assertEquals("Delete", book.getName()); + } finally { + 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("")); + } + + @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) { @@ -512,8 +579,8 @@ public class JAXRSAsyncClientTest extends AbstractBusClientServerTestBase { throw new RuntimeException(); } - } + @Consumes("application/xml") private static class FaultyBookReader implements MessageBodyReader<Book> { @@ -528,19 +595,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 f4ccdd0..acf30d7 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 @@ -46,6 +46,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; @@ -57,6 +60,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; @@ -95,12 +99,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; @@ -112,8 +118,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(); } @@ -358,7 +363,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); @@ -437,7 +441,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"; @@ -481,8 +484,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"); @@ -564,7 +566,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"); @@ -574,7 +575,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")); } @@ -675,8 +675,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); @@ -692,8 +691,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); @@ -713,8 +711,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); @@ -745,8 +742,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); @@ -779,8 +775,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); @@ -794,8 +789,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 CacheControlFeature cacheControlFeature = new CacheControlFeature(); @@ -834,8 +828,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 CacheControlFeature cacheControlFeature = new CacheControlFeature(); @@ -876,8 +869,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 CacheControlFeature cacheControlFeature = new CacheControlFeature(); @@ -924,6 +916,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 { @@ -975,8 +977,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()); @@ -996,9 +997,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(); @@ -1015,8 +1014,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(); @@ -1030,9 +1028,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(); @@ -1245,7 +1241,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); @@ -1279,15 +1274,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); @@ -1301,8 +1293,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<>(); @@ -2992,4 +2983,13 @@ public class JAXRSClientServerBookTest extends AbstractBusClientServerTestBase { private String getStringFromInputStream(InputStream in) throws Exception { return IOUtils.toString(in); } + + @Provider + private static class TestClientResponseFilter implements ClientResponseFilter { + @Override + public void filter(ClientRequestContext requestContext, + ClientResponseContext responseContext) throws IOException { + responseContext.getHeaders().add("X-Filter", "true"); + } + } }
