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
commit f1d096846fbb3606f4fff5bf197a2338215b0931 Author: Andriy Redko <[email protected]> AuthorDate: Thu Dec 2 08:37:25 2021 -0500 CXF-8620: AbstractHTTPServlet should support HTTP TRACE method dispatching if it is allowed by underlying container (#879) (cherry picked from commit ea7ea0c32683af9973f9897049f024c50ec6afa3) # Conflicts: # systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java --- .../main/java/org/apache/cxf/jaxrs/ext/TRACE.java | 34 +++++---------- .../cxf/transport/servlet/AbstractHTTPServlet.java | 6 +++ .../cxf/systest/jaxrs/resources/Library.java | 5 +++ .../cxf/systest/jaxrs/resources/LibraryApi.java | 6 +++ .../systest/jaxrs/spring/boot/SpringJaxrsTest.java | 51 ++++++++++++++++++++++ .../netty/AbstractNettyClientServerHttp2Test.java | 38 +++++++++++++--- .../apache/cxf/systest/http2/netty/BookStore.java | 12 +++++ .../cxf/systest/http2/netty/Http2TestClient.java | 4 ++ 8 files changed, 127 insertions(+), 29 deletions(-) diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/TRACE.java similarity index 54% copy from systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java copy to rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/TRACE.java index 911214d..fecb08b 100644 --- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/TRACE.java @@ -17,31 +17,17 @@ * under the License. */ -package org.apache.cxf.systest.jaxrs.resources; +package org.apache.cxf.jaxrs.ext; -import javax.ws.rs.DELETE; -import javax.ws.rs.DefaultValue; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; -public interface LibraryApi { - @Produces({ MediaType.APPLICATION_JSON }) - @GET - Response getBooks(@QueryParam("page") @DefaultValue("1") int page); +import javax.ws.rs.HttpMethod; - @Produces({ MediaType.APPLICATION_JSON }) - @Path("{id}") - @GET - Response getBook(@PathParam("id") String id); - - @DELETE - void deleteBooks(); - - @Path("/catalog") - Catalog catalog(); +@HttpMethod("TRACE") +@Target(value = ElementType.METHOD) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface TRACE { } diff --git a/rt/transports/http/src/main/java/org/apache/cxf/transport/servlet/AbstractHTTPServlet.java b/rt/transports/http/src/main/java/org/apache/cxf/transport/servlet/AbstractHTTPServlet.java index a6b5cbb..621a895 100644 --- a/rt/transports/http/src/main/java/org/apache/cxf/transport/servlet/AbstractHTTPServlet.java +++ b/rt/transports/http/src/main/java/org/apache/cxf/transport/servlet/AbstractHTTPServlet.java @@ -245,6 +245,12 @@ public abstract class AbstractHTTPServlet extends HttpServlet implements Filter throws ServletException, IOException { handleRequest(request, response); } + + @Override + protected void doTrace(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + handleRequest(request, response); + } /** * {@inheritDoc} diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Library.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Library.java index 53fe0bd..37da1f7 100644 --- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Library.java +++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/Library.java @@ -63,4 +63,9 @@ public class Library implements LibraryApi { public Catalog catalog() { return new Catalog(); } + + @Override + public Response traceBooks() { + return Response.status(Status.NOT_ACCEPTABLE).build(); + } } diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java index 911214d..01af5c7 100644 --- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java +++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/resources/LibraryApi.java @@ -29,6 +29,8 @@ import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import org.apache.cxf.jaxrs.ext.TRACE; + public interface LibraryApi { @Produces({ MediaType.APPLICATION_JSON }) @GET @@ -44,4 +46,8 @@ public interface LibraryApi { @Path("/catalog") Catalog catalog(); + + @TRACE + @Produces({ MediaType.APPLICATION_JSON }) + Response traceBooks(); } diff --git a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java index a9ced2e..7d8e58c 100644 --- a/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java +++ b/systests/spring-boot/src/test/java/org/apache/cxf/systest/jaxrs/spring/boot/SpringJaxrsTest.java @@ -29,6 +29,7 @@ import javax.ws.rs.ProcessingException; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; @@ -45,7 +46,9 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.system.OutputCaptureRule; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -108,6 +111,13 @@ public class SpringJaxrsTest { public JacksonJsonProvider jacksonJsonProvider() { return new JacksonJsonProvider(); } + + @Bean + public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() { + return customizer -> customizer.addConnectorCustomizers(connector -> { + connector.setAllowTrace(true); + }); + } } @Before @@ -421,6 +431,47 @@ public class SpringJaxrsTest { entry("status", "UNKNOWN")); } + @Test + public void testJaxrsCustomHttpMethodMetric() { + final WebTarget target = createWebTarget(); + + try (Response r = target.request().trace()) { + assertThat(r.getStatus()).isEqualTo(Status.NOT_ACCEPTABLE.getStatusCode()); + } + + await() + .atMost(Duration.ofSeconds(1)) + .ignoreException(MeterNotFoundException.class) + .until(() -> registry.get("cxf.server.requests").timers(), not(empty())); + RequiredSearch serverRequestMetrics = registry.get("cxf.server.requests"); + + Map<Object, Object> serverTags = serverRequestMetrics.timer().getId().getTags().stream() + .collect(toMap(Tag::getKey, Tag::getValue)); + + assertThat(serverTags) + .containsOnly( + entry("exception", "None"), + entry("method", "TRACE"), + entry("operation", "traceBooks"), + entry("uri", "/api/library"), + entry("outcome", "CLIENT_ERROR"), + entry("status", "406")); + + RequiredSearch clientRequestMetrics = registry.get("cxf.client.requests"); + + Map<Object, Object> clientTags = clientRequestMetrics.timer().getId().getTags().stream() + .collect(toMap(Tag::getKey, Tag::getValue)); + + assertThat(clientTags) + .containsOnly( + entry("exception", "None"), + entry("method", "TRACE"), + entry("operation", "UNKNOWN"), + entry("uri", "http://localhost:" + port + "/api/library"), + entry("outcome", "CLIENT_ERROR"), + entry("status", "406")); + } + private LibraryApi createApi(int portToUse) { final JAXRSClientFactoryBean factory = new JAXRSClientFactoryBean(); factory.setAddress("http://localhost:" + portToUse + "/api/library"); diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/AbstractNettyClientServerHttp2Test.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/AbstractNettyClientServerHttp2Test.java index 1263377..5945b1f 100644 --- a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/AbstractNettyClientServerHttp2Test.java +++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/AbstractNettyClientServerHttp2Test.java @@ -51,6 +51,21 @@ abstract class AbstractNettyClientServerHttp2Test extends AbstractBusClientServe } @Test + public void testBookTraceWithHttp2() throws Exception { + final Http2TestClient client = new Http2TestClient(isSecure()); + + final ClientResponse response = client + .request(getAddress()) + .accept("text/plain") + .path(getContext() + "/web/bookstore/trace") + .http2() + .trace(); + + assertThat(response.getResponseCode(), equalTo(406)); + assertThat(response.getProtocol(), equalTo("HTTP/2.0")); + } + + @Test public void testBookWithHttp2() throws Exception { final Http2TestClient client = new Http2TestClient(isSecure()); @@ -85,8 +100,24 @@ abstract class AbstractNettyClientServerHttp2Test extends AbstractBusClientServe @Test public void testBookWithHttp() throws Exception { + final WebClient wc = createWebClient("/web/bookstore/booknames"); + try (Response resp = wc.get()) { + assertThat(resp.getStatus(), equalTo(200)); + assertEquals("CXF in Action", resp.readEntity(String.class)); + } + } + + @Test + public void testBookTraceWithHttp() throws Exception { + final WebClient wc = createWebClient("/web/bookstore/trace"); + try (Response response = wc.invoke("TRACE", null)) { + assertThat(response.getStatus(), equalTo(406)); + } + } + + private WebClient createWebClient(final String path) { final WebClient wc = WebClient - .create(getAddress() + getContext() + "/web/bookstore/booknames") + .create(getAddress() + getContext() + path) .accept("text/plain"); if (isSecure()) { @@ -103,10 +134,7 @@ abstract class AbstractNettyClientServerHttp2Test extends AbstractBusClientServe params.setDisableCNCheck(true); } - try (Response resp = wc.get()) { - assertThat(resp.getStatus(), equalTo(200)); - assertEquals("CXF in Action", resp.readEntity(String.class)); - } + return wc; } protected abstract String getAddress(); diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookStore.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookStore.java index 4b4e1ca..4715c02 100644 --- a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookStore.java +++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/BookStore.java @@ -23,11 +23,15 @@ import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import org.apache.cxf.jaxrs.ext.StreamingResponse; +import org.apache.cxf.jaxrs.ext.TRACE; @Path("/web/bookstore") public class BookStore { @@ -63,6 +67,14 @@ public class BookStore { } }; } + + @TRACE + @Produces("application/xml") + @Consumes("application/xml") + @Path("/trace") + public Response traceBook() { + return Response.status(Status.NOT_ACCEPTABLE).build(); + } } diff --git a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/Http2TestClient.java b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/Http2TestClient.java index 32e4e64..b88e610 100644 --- a/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/Http2TestClient.java +++ b/systests/transport-netty/src/test/java/org/apache/cxf/systest/http2/netty/Http2TestClient.java @@ -163,6 +163,10 @@ public class Http2TestClient implements AutoCloseable { public ClientResponse get() throws Exception { return request(address, path, version, HttpMethod.GET, accept); } + + public ClientResponse trace() throws Exception { + return request(address, path, version, HttpMethod.TRACE, accept); + } } public RequestBuilder request(final String address) throws IOException {
