Spring version: 6.2.10
We utilize Spring's SSE implementation to receive events from a third-party source and stream them to other components. We noticed that ServerSentEventHttpMessageWriter and ServerSentEventHttpMessageReader are not consistent: data that is serialized cannot be deserialized back to the original value.
reproducible test case:
@Test
void shouldSerializeAndDeserializeSse() {
// given
String originalData = "\n\n";
String expectedSerialized =
"id:1\n" +
"event:message\n" +
"data:\n" +
"data:\n" +
"data:\n\n";
Flux<ServerSentEvent<String>> events = Flux.just(
ServerSentEvent.builder(originalData)
.id("1")
.event("message")
.build()
);
// when – serialize
MockServerHttpResponse response = new MockServerHttpResponse();
ServerSentEventHttpMessageWriter writer = new ServerSentEventHttpMessageWriter();
ResolvableType eventType = ResolvableType.forClassWithGenerics(ServerSentEvent.class, String.class);
writer.write(events, eventType, MediaType.TEXT_EVENT_STREAM, response, null).block();
String actualSerialized = response.getBodyAsString().block();
// then – check serialization
assertThat(actualSerialized).isEqualTo(expectedSerialized); // passes
// when – deserialize back
ServerSentEventHttpMessageReader reader = new ServerSentEventHttpMessageReader();
List<ServerSentEvent<String>> deserialized = reader
.read(eventType, new MockInputMessage(actualSerialized), Collections.emptyMap())
.cast((Class<ServerSentEvent<String>>)(Class<?>)ServerSentEvent.class)
.collectList()
.block();
// then – check deserialization
assertThat(deserialized).hasSize(1);
assertThat(deserialized.get(0).data()).isEqualTo(originalData); // fails
}
Observed behavior: Serialization produces the expected output. After deserialization, data() is null instead of the original value "\n\n".
Expected behavior: Round-trip serialization and deserialization should be consistent.
Comment From: rstoyanchev
It looks on the reading side we treat empty String as null. We can make an improvement as suggested, but better not to introduce that going forward given the possibility for impact on existing applications.
Comment From: rstoyanchev
The spec also confirms that an empty data line should be included regardless of whether it is empty or not:
If the field name is "data" Append the field value to the data buffer, then append a single U+000A LINE FEED (LF) character to the data buffer.