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
The following commit(s) were added to refs/heads/3.3.x-fixes by this push: new 1a4505c CXF-8120: JAX-RS 2.1 SSE client: forbidden response without WebApplicationException 1a4505c is described below commit 1a4505c09fa403262a54e0fa745edf78a8c3ba0c Author: reta <drr...@gmail.com> AuthorDate: Sun Sep 22 12:26:32 2019 -0400 CXF-8120: JAX-RS 2.1 SSE client: forbidden response without WebApplicationException (cherry picked from commit 88e3eb24de7ee6498240aac557ccf3d75a1be9af) --- .../org/apache/cxf/jaxrs/utils/ExceptionUtils.java | 12 ++++ .../apache/cxf/jaxrs/client/AbstractClient.java | 10 +--- .../cxf/jaxrs/sse/client/SseEventSourceImpl.java | 10 +++- .../jaxrs/sse/client/SseEventSourceImplTest.java | 64 ++++++++++++++++++++-- 4 files changed, 80 insertions(+), 16 deletions(-) diff --git a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ExceptionUtils.java b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ExceptionUtils.java index 5e8b014..a8c607a 100644 --- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ExceptionUtils.java +++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/ExceptionUtils.java @@ -21,6 +21,7 @@ package org.apache.cxf.jaxrs.utils; import java.io.PrintWriter; import java.io.StringWriter; +import java.lang.reflect.Constructor; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; @@ -169,4 +170,15 @@ public final class ExceptionUtils { return toWebApplicationException(ex, response); } } + + public static WebApplicationException toWebApplicationException(Response response) { + try { + final Class<?> exceptionClass = ExceptionUtils.getWebApplicationExceptionClass(response, + WebApplicationException.class); + final Constructor<?> ctr = exceptionClass.getConstructor(Response.class); + return (WebApplicationException)ctr.newInstance(response); + } catch (Throwable ex) { + return new WebApplicationException(response); + } + } } 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 c6f6a88..80eb2cc 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 @@ -23,7 +23,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.annotation.Annotation; -import java.lang.reflect.Constructor; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -536,14 +535,7 @@ public abstract class AbstractClient implements Client { } protected WebApplicationException convertToWebApplicationException(Response r) { - try { - Class<?> exceptionClass = ExceptionUtils.getWebApplicationExceptionClass(r, - WebApplicationException.class); - Constructor<?> ctr = exceptionClass.getConstructor(Response.class); - return (WebApplicationException)ctr.newInstance(r); - } catch (Throwable ex2) { - return new WebApplicationException(r); - } + return ExceptionUtils.toWebApplicationException(r); } protected <T> T readBody(Response r, Message outMessage, Class<T> cls, diff --git a/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/client/SseEventSourceImpl.java b/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/client/SseEventSourceImpl.java index 3a7f78e..f0c2acf 100644 --- a/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/client/SseEventSourceImpl.java +++ b/rt/rs/sse/src/main/java/org/apache/cxf/jaxrs/sse/client/SseEventSourceImpl.java @@ -41,6 +41,7 @@ import javax.ws.rs.sse.SseEventSource; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.jaxrs.client.WebClient; +import org.apache.cxf.jaxrs.utils.ExceptionUtils; /** * SSE Event Source implementation @@ -191,13 +192,20 @@ public class SseEventSourceImpl implements SseEventSource { // A client can be told to stop reconnecting using the HTTP 204 No Content // response code. In this case, we should give up. - if (response.getStatus() == 204) { + final int status = response.getStatus(); + if (status == 204) { LOG.fine("SSE endpoint " + target.getUri() + " returns no data, disconnecting"); state.set(SseSourceState.CLOSED); response.close(); return; } + // Convert unsuccessful responses to instances of WebApplicationException + if (status != 304 && status >= 300) { + LOG.fine("SSE connection to " + target.getUri() + " returns " + status); + throw ExceptionUtils.toWebApplicationException(response); + } + // Should not happen but if close() was called from another thread, we could // end up there. if (state.get() == SseSourceState.CLOSED) { diff --git a/rt/rs/sse/src/test/java/org/apache/cxf/jaxrs/sse/client/SseEventSourceImplTest.java b/rt/rs/sse/src/test/java/org/apache/cxf/jaxrs/sse/client/SseEventSourceImplTest.java index 1c0ae15..787b5d5 100644 --- a/rt/rs/sse/src/test/java/org/apache/cxf/jaxrs/sse/client/SseEventSourceImplTest.java +++ b/rt/rs/sse/src/test/java/org/apache/cxf/jaxrs/sse/client/SseEventSourceImplTest.java @@ -34,6 +34,8 @@ import javax.ws.rs.Produces; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import javax.ws.rs.sse.InboundSseEvent; import javax.ws.rs.sse.SseEventSource; @@ -57,7 +59,8 @@ public class SseEventSourceImplTest { enum Type { NO_CONTENT, NO_SERVER, BUSY, - EVENT, EVENT_JUST_DATA, EVENT_JUST_NAME, EVENT_NO_RETRY, EVENT_BAD_RETRY, EVENT_MIXED, EVENT_BAD_NEW_LINES; + EVENT, EVENT_JUST_DATA, EVENT_JUST_NAME, EVENT_NO_RETRY, EVENT_BAD_RETRY, EVENT_MIXED, EVENT_BAD_NEW_LINES, + EVENT_NOT_AUTHORIZED; } private static final String EVENT = "event: event\n" @@ -276,6 +279,36 @@ public class SseEventSourceImplTest { } @Test + public void testReconnectAndNotAuthorized() throws InterruptedException, IOException { + try (SseEventSource eventSource = withReconnect(Type.EVENT_NOT_AUTHORIZED)) { + eventSource.open(); + assertThat(eventSource.isOpen(), equalTo(false)); + assertThat(errors.size(), equalTo(1)); + + // Allow the event processor to pull for events (150ms) + Thread.sleep(150L); + } + + assertThat(errors.size(), equalTo(2)); + assertThat(events.size(), equalTo(0)); + } + + @Test + public void testNoReconnectAndNotAuthorized() throws InterruptedException, IOException { + try (SseEventSource eventSource = withNoReconnect(Type.EVENT_NOT_AUTHORIZED)) { + eventSource.open(); + assertThat(eventSource.isOpen(), equalTo(false)); + assertThat(errors.size(), equalTo(1)); + + // Allow the event processor to pull for events (150ms) + Thread.sleep(150L); + } + + assertThat(errors.size(), equalTo(1)); + assertThat(events.size(), equalTo(0)); + } + + @Test public void testNoReconnectAndCloseTheStreamWhileEventIsBeingReceived() throws InterruptedException, IOException { try (SseEventSource eventSource = withNoReconnect(Type.BUSY)) { eventSource.open(); @@ -343,11 +376,8 @@ public class SseEventSourceImplTest { startServer(Type.NO_CONTENT, null); // Type.NO_SERVER - Type type = Type.BUSY; - JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); - sf.setAddress(LOCAL_ADDRESS + type.name()); - sf.setServiceBean(new BusyEventServer()); - SERVERS.put(type, sf.create()); + startBusyServer(Type.BUSY); + startNotAuthorizedServer(Type.EVENT_NOT_AUTHORIZED); startServer(Type.EVENT, EVENT); startServer(Type.EVENT_JUST_DATA, EVENT_JUST_DATA); @@ -358,6 +388,20 @@ public class SseEventSourceImplTest { startServer(Type.EVENT_BAD_NEW_LINES, EVENT_BAD_NEW_LINES); } + private static void startNotAuthorizedServer(Type type) { + JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); + sf.setAddress(LOCAL_ADDRESS + type.name()); + sf.setServiceBean(new ProtectedEventServer()); + SERVERS.put(type, sf.create()); + } + + private static void startBusyServer(Type type) { + JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); + sf.setAddress(LOCAL_ADDRESS + type.name()); + sf.setServiceBean(new BusyEventServer()); + SERVERS.put(type, sf.create()); + } + private static void startServer(Type type, String payload) { JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); sf.setAddress(LOCAL_ADDRESS + type.name()); @@ -401,4 +445,12 @@ public class SseEventSourceImplTest { } } + public static class ProtectedEventServer { + @GET + @Produces(MediaType.SERVER_SENT_EVENTS) + public Response event() { + return Response.status(Status.UNAUTHORIZED).build(); + } + } + }