Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging server-sent events stream from Spring in Chrome and Postman

According to the Spring documentation, when returning a Flux, Spring should emit a server-sent event for each element returned by the subscription.

Here's an exemplaric REST controller:

package myapp.controller;

import myapp.MyOutput;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/api/v1/test")
@Api(tags = "Test API")
public class TestController {

    @ApiOperation(
        value = "Test",
        response = MyOutput.class,
        produces = MediaType.TEXT_EVENT_STREAM_VALUE
    )
    @RequestMapping(
        value = "",
        method = RequestMethod.PATCH,
        produces = MediaType.TEXT_EVENT_STREAM_VALUE
    )
    @ApiResponses(
        value = {
            @ApiResponse(code = 200, message = "Service execution successful"),
            @ApiResponse(code = 400, message = "Bad input data"),
            @ApiResponse(code = 500, message = "An internal server error occurred."),
            @ApiResponse(code = 503, message = "The service is not available.")
        }
    )
    public ResponseEntity<Flux<MyOutput>> test() {
        return ResponseEntity.ok().header("Connection", "Keep-Alive")
            .body(Flux.range(0, 1000)
                .delayElements(Duration.ofSeconds(1))
                .map(MyOutput::new)
            );
    }
}

Example response, using wget:

data:{"recordCount":0}

data:{"recordCount":148}

data:{"recordCount":226}

data:{"recordCount":266}

data:{"recordCount":272}

data:{"recordCount":286}

data:{"recordCount":287}

data:{"recordCount":293}

data:{"recordCount":294}

When debugging the endpoint using Chrome or Postman, the clients seem to interpret the events as parts of a chunked response - not as a stream of events. I have confirmed that the response data is the same, and requires the expected amount of time. See screenshots from Chrome Network tab below:

Chrome Developer Networking Tab -> Headers

The EventStream - tab is empty:

Chrome Developer Networking Tab -> EventStream Chrome Developer Networking Tab -> Timing


Compare this to the headers of a website like http://www.emojitracker.com/:

Chrome Developer Networking Tab -> Headers

Where the EventStream tab displays the events correctly:

Chrome Developer Networking Tab -> EventStream


The important fact is, when consuming the Spring endpoint with a WebClient, I can successfully receive each event at the expected time using .bodyToFlux. So it seems like the events are missing some form of configuration that Chrome expects from a server-sent event stream - but which?

like image 510
Markus Appel Avatar asked Sep 30 '19 07:09

Markus Appel


1 Answers

First of all in the browser when using EventSource will perform a GET request so using a PATCH is not really compatible in the browser.

If we takes your code and change the PATCH in GET and we create a simple page page:

<html lang="fr">
    <head>
        <title>Test SSE</title>
        <script>
            const evtSource = new EventSource("/api/v1/test");
            evtSource.onmessage = function(event) {
                const newElement = document.createElement("li");
                const eventList = document.getElementById("results");
                newElement.textContent = "message: " + event.data;
                eventList.appendChild(newElement);
            }
        </script>
    </head>
    <body>
        <ul id="results">

        </ul>
    </body>
</html>

Make your application serve this static file and open it in chrome. You will correctly see the events in the Event tab. However if you directly request /api/v1/test you will this the event in the page but not in the Event tab. I suppose that the event tab intercept EventSource object and won't be used if no EventSource is created.

like image 153
JEY Avatar answered Nov 08 '22 12:11

JEY