带有附加Cookie的Server Sent Event Client

我正在尝试使用额外的cookie对Server Sent Event资源进行unit testing。 我已经在为客户端使用Jersey作为EventSource和JavaX。 以下代码工作正常:

WebTarget target = ClientBuilder.newBuilder() .register(SseFeature.class) .build() .target("http://localhost:8080/sse"); EventSource eventSource = EventSource.target(target).build(); EventListener listener = new EventListener() { @Override public void onEvent(InboundEvent inboundEvent) { LOG.info(inboundEvent.readData(String.class)); } }; eventSource.register(listener); eventSource.open(); serverEventManager.send("/sse", "foo"); eventSource.close(); 

Hoewever,对于额外的unit testing,我需要在请求中添加额外的cookie。 我已经尝试了以下内容

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

但是这会返回一个构建器,我无法从中为EventSource创建所需的WebTarget。

这是在EventSource中发生的事情,以建立给定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; } 

我们可以看到 – 没有机会在这里添加cookie。

因此, WebTarget.request(new MediaType[] { SseFeature.SERVER_SENT_EVENTS_TYPE })需要返回已添加了所需cookie的Builder。

考虑这个委托类将所需的cookie添加到所有request* type方法:

 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, 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, 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 paramMap) { return base.resolveTemplates(paramMap); } public WebTarget resolveTemplates(Map paramMap, boolean paramBoolean) { return base.resolveTemplates(paramMap, paramBoolean); } public WebTarget resolveTemplatesFromEncoded(Map paramMap) { return base.resolveTemplatesFromEncoded(paramMap); } } 

现在您应该可以将您的测试重新调整为:

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

并且应该插入cookie。

警告:我无法测试这个。 解决方案仅基于阅读jersey-media-sse-2.22.1的源代码。

祝好运。

您可以在ClientRequestFilter设置cookie。 虽然ClientRequestContext上的getCookies()是不可变的,但你应该记住,cookie在技术上只不过是一个标题。 并且请求上下文中的标头映射是可变的 。 所以你可以做点什么

 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()); } } 

只需使用客户端注册filter( client.register(new SseCookieFilter()) )。 这将是你想做的结果

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

以下是使用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(); } } 

这些是测试所需的唯一依赖项

  org.glassfish.jersey.test-framework.providers jersey-test-framework-provider-grizzly2 ${jersey2.version} test   org.glassfish.jersey.media jersey-media-sse ${jersey2.version} test  

以下是我在测试服务器上注册的LoggingFilter的服务器端结果

 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!!!