eclipse-ee4j / eclipse-ee4j/jersey
Combination of ContainerResponseFilter and SSE EventOutput/OutboundEvent
- Dominant language
- Java
- Stars
- 730
- Forks
- 382
- PR merge metrics
- No merged PRs in 30d
Description
Here's the scenario that will illustrate the general problem:
We want to allow a user to "link" his website account with a mobile device. To do so, user logs in to website account and requests QR code. User scans QR code on mobile device. On doing so, the Jersey server is notified thusly:
```
@POST
@Path("/notify-linked")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response broadcastAccountLinked(ObjectNode accountLinkedJson)
{
// if QR code matches ...
OutboundEvent event = new OutboundEvent.Builder().name("message")
.mediaType(MediaType.APPLICATION_JSON_TYPE)
.data(ObjectNode.class, new ObjectNode(JsonNodeFactory.instance).put("userId", user.getId())).build();
broadcaster.broadcast(event);
return Response.ok(new ObjectNode(JsonNodeFactory.instance).put("success", true)).build();
}
```
This informs the Jersey server of a successful linkage, which is then broadcasted via server sent events (SSE) thusly:
```
@GET
@CORS
@Path("/link-events")
@Produces(SseFeature.SERVER_SENT_EVENTS)
public EventOutput listenToAccountLinkedBroadcasts()
{
final EventOutput eventOutput = new EventOutput();
broadcaster.add(eventOutput);
return eventOutput;
}
```
The website which provided the initial QR code is listening to these events (via a javascript EventSource pointed at the /link-events resource). You will notice the above method is annotated with a @CORS annotation. This is a ContainerResponseFilter that adds an "Access-Control-Allow-Origin" header so that the above scenario is possible (as the Jersey server runs on say port 4633 but the website runs on port 80\. Without this header, the browser will report an error and the EventSource can't be constructed.
All of this would be fine if the @CORS annotated method actually added the header to the /link-events resource, but so far I have had no luck in getting it to do so.
Here is the response filter:
```
@CORS
public class CORSResponseFilter implements ContainerResponseFilter
{
public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException
{
response.getHeaders().add("Access-Control-Allow-Origin", "*");
response.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
```
The CORS interface:
```
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface CORS
{
}
```
I have also tried using a dynamic binding instead of a static one but no luck. I should mention that in the same project I have a working filter that uses the above method (e.g. static binding).
tl;dr: How can one add a ContainerResponseFIlter on an SSE resource?
Thanks!
#### Affected Versions
[2.22.1]
Contributor guide
Research direction
Start with CORSResponseFilter.filter and the @CORS annotation on listenToAccountLinkedBroadcasts(), then reproduce the EventSource request against the /link-events resource. Compare the SSE response headers with a response from the project's other working statically bound filter; done means the expected CORS headers are present for the SSE response.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, javascript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100