javaspring-bootsslhttpsserver-sent-events

Server-Sent-Events (SSE) and HTTPS


I have a spring-boot backend and wanted to create a SSE endpoint, sample:

@CrossOrigin(origins = "http://localhost:4200") // Angular Dev-Server
@GetMapping(path = "/stream-flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamFlux() {
    return Flux.interval(Duration.ofSeconds(1))
            .map(sequence -> "data: Flux - " + LocalTime.now().toString() + "\n\n")
            .doOnSubscribe(subscription -> {
                System.out.println("SSE-Stream started.");
            })
            .doOnNext(value -> {
                System.out.println("SSE-Value sent: " + value);
            })
            .doOnError(error -> {
                System.out.println("SSE-Error: " + error.getMessage());
            })
            .doOnComplete(() -> {
                System.out.println("SSE-Stream complete.");
            });                
}

Now, this example works perfect with an http-call (and the respective spring-server settings), tested with CURL.

curl -v http://localhost:8080/stream-flux

But when I wanted to switch to https (of course with the right spring-boot server settings)

tested with the following call:

curl -k -v https://localhost:<ssl-port>/stream-flux

The values from the event doesn't come at all with https. I am using a self-signed certificate.

And the CURL-answer is confusing to me:

* schannel: remote party requests renegotiation
* schannel: renegotiating SSL/TLS connection
* schannel: SSL/TLS connection renegotiated
* Request completely sent off

Any approach to tackling this issue is highly appreciated.


Solution

  • Spring Boot Logging Filter Blocks SSE (Server-Sent Events)

    My Spring Boot application has a additional Filter that logs incoming REST requests and responses. It uses ContentCachingRequestWrapper and ContentCachingResponseWrapper to capture requests and response bodies.

    Original Problem: Standard REST endpoints work fine, but SSE (text/event-stream) endpoints never send data to the client.

    Cause: ContentCachingResponseWrapper buffers the entire response before passing it downstream. Since SSE is a continuous stream, it never completes, so the client never receives data.

    Solution: Exclude SSE from logging To prevent this issue, I modified my filter to bypass SSE requests:

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
    
        // Skip logging for SSE responses
        if (httpRequest.getContentType() == null || httpRequest.getContentType().equals(MediaType.TEXT_EVENT_STREAM_VALUE)) {
            chain.doFilter(request, response);
            return;
        }
    
        ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(httpRequest);
        ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(httpResponse);
    
        chain.doFilter(wrappedRequest, wrappedResponse);
    
        logRequestAndResponse(wrappedRequest, wrappedResponse);
    
        wrappedResponse.copyBodyToResponse();
    }
    

    Now, SSE responses are streamed directly without being cached, issue ist fixed.

    Hope this helps someone facing the same problem! 😊