Server Sent Event Client with additional Cookie

前端 未结 2 1705
梦毁少年i
梦毁少年i 2020-12-17 18:04

I am trying to unit test a Server Sent Event resource with an additional cookie. I am already using Jersey for the EventSource and JavaX for the client. The following code w

相关标签:
2条回答
  • 2020-12-17 18:39

    Here's what's happens within EventSource to establish a conection to the given WebTarget:

           private Invocation.Builder prepareHandshakeRequest() {
                Invocation.Builder request = EventSource.this.target
                        .request(new MediaType[] { SseFeature.SERVER_SENT_EVENTS_TYPE });
                if ((this.lastEventId != null) && (!(this.lastEventId.isEmpty()))) {
                    request.header("Last-Event-ID", this.lastEventId);
                }
                if (EventSource.this.disableKeepAlive) {
                    request.header("Connection", "close");
                }
                return request;
              }
    

    As we can see - no chance to add a cookie here.

    So WebTarget.request(new MediaType[] { SseFeature.SERVER_SENT_EVENTS_TYPE }) needs to return a Builder that already has that desired cookie added.

    Consider this delegating class adding the desired cookie to all request* type methods:

    public class CookieAddedWebTarget implements WebTarget {
    
        private WebTarget base;
    
        private Cookie cookie;
    
        public CookieAddedWebTarget(WebTarget base, Cookie cookie) {
            this.base = base;
            this.cookie = cookie;
        }
    
        // Inject that cookie whenever someone requests a Builder (like EventSource does):
        public Builder request() {
            return base.request().cookie(cookie);
        }
    
        public Builder request(String... paramArrayOfString) {
            return base.request(paramArrayOfString).cookie(cookie);
        }
    
        public Builder request(MediaType... paramArrayOfMediaType) {
            return base.request(paramArrayOfMediaType).cookie(cookie);
        }
    
        public Configuration getConfiguration() {
            return base.getConfiguration();
        }
    
        //All other methods from WebTarget are delegated as-is:
    
        public URI getUri() {
            return base.getUri();
        }
    
        public UriBuilder getUriBuilder() {
            return base.getUriBuilder();
        }
    
        public WebTarget path(String paramString) {
            return base.path(paramString);
        }
    
        public WebTarget matrixParam(String paramString, Object... paramArrayOfObject) {
            return base.matrixParam(paramString, paramArrayOfObject);
        }
    
        public WebTarget property(String paramString, Object paramObject) {
            return base.property(paramString, paramObject);
        }
    
        public WebTarget queryParam(String paramString, Object... paramArrayOfObject) {
            return base.queryParam(paramString, paramArrayOfObject);
        }
    
        public WebTarget register(Class<?> paramClass, Class<?>... paramArrayOfClass) {
            return base.register(paramClass, paramArrayOfClass);
        }
    
        public WebTarget register(Class<?> paramClass, int paramInt) {
            return base.register(paramClass, paramInt);
        }
    
        public WebTarget register(Class<?> paramClass, Map<Class<?>, Integer> paramMap) {
            return base.register(paramClass, paramMap);
        }
    
        public WebTarget register(Class<?> paramClass) {
            return base.register(paramClass);
        }
    
        public WebTarget register(Object paramObject, Class<?>... paramArrayOfClass) {
            return base.register(paramObject, paramArrayOfClass);
        }
    
        public WebTarget register(Object paramObject, int paramInt) {
            return base.register(paramObject, paramInt);
        }
    
        public WebTarget register(Object paramObject, Map<Class<?>, Integer> paramMap) {
            return base.register(paramObject, paramMap);
        }
    
        public WebTarget register(Object paramObject) {
            return base.register(paramObject);
        }
    
        public WebTarget resolveTemplate(String paramString, Object paramObject) {
            return base.resolveTemplate(paramString, paramObject);
        }
    
        public WebTarget resolveTemplate(String paramString, Object paramObject, boolean paramBoolean) {
            return base.resolveTemplate(paramString, paramObject, paramBoolean);
        }
    
        public WebTarget resolveTemplateFromEncoded(String paramString, Object paramObject) {
            return base.resolveTemplateFromEncoded(paramString, paramObject);
        }
    
        public WebTarget resolveTemplates(Map<String, Object> paramMap) {
            return base.resolveTemplates(paramMap);
        }
    
        public WebTarget resolveTemplates(Map<String, Object> paramMap, boolean paramBoolean) {
            return base.resolveTemplates(paramMap, paramBoolean);
        }
    
        public WebTarget resolveTemplatesFromEncoded(Map<String, Object> paramMap) {
            return base.resolveTemplatesFromEncoded(paramMap);
        }
    
    }
    

    Now you should be able to reerite your test to:

    EventSource eventSource = EventSource.target(new CookieAddedWebTarget(target, 
                                new Cookie("name", "value"))).build();
    

    And the cookie should be inserted.

    Caveat: I have no way of testing this. Solution based only on reading source-code of jersey-media-sse-2.22.1.

    Good Luck.

    0 讨论(0)
  • 2020-12-17 18:55

    You could set the cookie in a ClientRequestFilter. Though the getCookies() on the ClientRequestContext is immutable, you should remember that a cookie is technically nothing more than a header. And the headers map on the request context is mutable. So you could do something like

    public static class SseCookieFilter implements ClientRequestFilter {
    
        @Override
        public void filter(ClientRequestContext requestContext) throws IOException {
            Cookie cookie = new Cookie("foo", "bar");
            requestContext.getHeaders().add("Cookie", cookie.toString());
        }
    }
    

    Just register the filter with the client (client.register(new SseCookieFilter())). It would be the same result as if you were to do

    target.(...).request().cookie("foo", "bar");
    

    Here is a complete example using Jersey Test Framework

    public class SseCookieFilterTest extends JerseyTest {
    
        @Path("events")
        public static class SseResource {
    
            @GET
            @Produces(SseFeature.SERVER_SENT_EVENTS)
            public EventOutput getServerSentEvents(@CookieParam("foo") String foo) {
                final EventOutput eventOutput = new EventOutput();
                new Thread(() -> {
                    try {
                        final OutboundEvent.Builder eventBuilder
                                = new OutboundEvent.Builder();
                        eventBuilder.name("message");
                        eventBuilder.data(String.class, "Blah " + foo + "!!!");
                        final OutboundEvent event = eventBuilder.build();
                        eventOutput.write(event);
    
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    } finally {
                        try {
                            eventOutput.close();
                        } catch (IOException ioClose) {
                            throw new RuntimeException(ioClose);
                        }
                    }
                }).start();
                return eventOutput;
            }
        }
    
        public static class SseCookieFilter implements ClientRequestFilter {
    
            @Override
            public void filter(ClientRequestContext requestContext) throws IOException {
                Cookie cookie = new Cookie("foo", "bar");
                requestContext.getHeaders().add("Cookie", cookie.toString());
            }
        }
    
        @Override
        public ResourceConfig configure() {
            return new ResourceConfig(SseResource.class)
                    .register(new LoggingFilter());
        }
    
        @Test
        public void doit() throws Exception {
            Client client = ClientBuilder.newBuilder()
                    .register(SseFeature.class).build();
            client.register(new SseCookieFilter());
            WebTarget target = client.target("http://localhost:9998/events");
            EventSource eventSource = EventSource.target(target).build();
            EventListener listener = (InboundEvent inboundEvent) -> {
                System.out.println("From server ---====++++>  " 
                        + inboundEvent.readData(String.class));
            };
            eventSource.register(listener, "message");
            eventSource.open();
            Thread.sleep(100);
            eventSource.close();
        }
    }
    

    These are the only dependencies needed to test

    <dependency>
        <groupId>org.glassfish.jersey.test-framework.providers</groupId>
        <artifactId>jersey-test-framework-provider-grizzly2</artifactId>
        <version>${jersey2.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-sse</artifactId>
        <version>${jersey2.version}</version>
        <scope>test</scope>
    </dependency>
    

    Here is the server side result from the LoggingFilter I registered on the server in the test

    INFO: 1 * Server has received a request on thread grizzly-http-server-2
    1 > GET http://localhost:9998/events
    1 > accept: text/event-stream
    1 > connection: close
    1 > cookie: $Version=1;foo=bar
    1 > host: localhost:9998
    1 > user-agent: Jersey/2.19 (HttpUrlConnection 1.8.0_31)
    
    INFO: 1 * Server responded with a response on thread grizzly-http-server-2
    1 < 200
    1 < Content-Type: text/event-stream
    
    From server ---====++++>  Blah bar!!!
    
    0 讨论(0)
提交回复
热议问题