Enforce "data: " prefix for outgoing SSE data payloads

Prior to this commit, SSE support in Spring would write payloads with
the "data:" prefix (without space). While this is OK with the standard,
this makes it harder for implementations to support reading and writing
payloads with Spring (the round trip use case).

This commit introduces a breaking change and now enforces "data: " in
all variants. This has the potential of breaking some low level test
suites with text/plain or custom media types, but this should overall
make the situation better for developers.

Closes gh-37242
This commit is contained in:
Brian Clozel
2026-09-04 16:21:13 +02:00
parent 21bb726934
commit 60e5abff7f
22 changed files with 102 additions and 99 deletions
@@ -52,7 +52,7 @@ class ReactiveReturnTypeTests {
.andReturn();
mockMvc.perform(asyncDispatch(mvcResult))
.andExpect(content().string("data:event0\n\ndata:event1\n\ndata:event2\n\n"));
.andExpect(content().string("data: event0\n\ndata: event1\n\ndata: event2\n\n"));
}
@@ -96,7 +96,7 @@ public final class ServerSentEvent<T> {
/**
* Return a StringBuilder with the id, event, retry, and comment fields fully
* serialized, and also appending "data:" if there is data.
* serialized, and also appending "data: " if there is data.
* @since 6.2.1
*/
public String format() {
@@ -116,7 +116,7 @@ public final class ServerSentEvent<T> {
sb.append('\n');
}
if (this.data != null) {
sb.append("data:");
sb.append("data: ");
}
return sb.toString();
}
@@ -69,7 +69,7 @@ public class Jackson2JsonEncoder extends AbstractJackson2Encoder {
private static PrettyPrinter initSsePrettyPrinter() {
DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
printer.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
printer.indentObjectsWith(new DefaultIndenter(" ", "\ndata: "));
return printer;
}
@@ -117,7 +117,7 @@ public class JacksonJsonEncoder extends AbstractJacksonEncoder<JsonMapper> {
private static PrettyPrinter initSsePrettyPrinter() {
DefaultPrettyPrinter printer = new DefaultPrettyPrinter();
printer.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
printer.indentObjectsWith(new DefaultIndenter(" ", "\ndata: "));
return printer;
}
@@ -175,7 +175,7 @@ public abstract class AbstractJacksonHttpMessageConverter<T extends ObjectMapper
private PrettyPrinter initSsePrettyPrinter() {
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata: "));
return prettyPrinter;
}
@@ -108,7 +108,7 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper) {
this.defaultObjectMapper = objectMapper;
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));
prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata: "));
this.ssePrettyPrinter = prettyPrinter;
}
@@ -31,9 +31,9 @@ public abstract class SseUtils {
/**
* Append {@code value} to {@code output}, replacing each line separator
* ({@code "\n"}, {@code "\r"}, or {@code "\r\n"}) it contains with a new
* {@code field} line (that is, {@code "\n" + field + ":"}). This keeps a
* multi-line field value from breaking out of the current SSE field when
* written on the wire.
* {@code field} line (that is, {@code "\n" + field + ": "}, or
* {@code "\n:"} for a comment). This keeps a multi-line field value from
* breaking out of the current SSE field when written on the wire.
* @param field the name of the SSE field that {@code value} belongs to
* (for example, {@code "data"}), or an empty string for a comment
* @param value the field value to escape and append
@@ -44,7 +44,7 @@ public abstract class SseUtils {
output.append(value);
return;
}
String lineSeparatorReplacement = "\n" + field + ":";
String lineSeparatorReplacement = "\n" + field + (field.isEmpty() ? ":" : ": ");
int length = value.length();
for (int i = 0; i < length; i++) {
char c = value.charAt(i);
@@ -85,7 +85,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
StepVerifier.create(outputMessage.getBody())
.consumeNextWith(stringConsumer(
"id:c42\nevent:foo\nretry:123\n:bla\n:bla bla\n:bla bla bla\ndata:bar\n\n"))
"id:c42\nevent:foo\nretry:123\n:bla\n:bla bla\n:bla bla bla\ndata: bar\n\n"))
.expectComplete()
.verify();
}
@@ -99,8 +99,8 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
testWrite(source, outputMessage, String.class);
StepVerifier.create(outputMessage.getBody())
.consumeNextWith(stringConsumer("data:foo\n\n"))
.consumeNextWith(stringConsumer("data:bar\n\n"))
.consumeNextWith(stringConsumer("data: foo\n\n"))
.consumeNextWith(stringConsumer("data: bar\n\n"))
.expectComplete()
.verify();
}
@@ -114,9 +114,9 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
testWrite(source, outputMessage, String.class);
StepVerifier.create(outputMessage.getBody())
.consumeNextWith(stringConsumer("data:first\ndata:second\n\n"))
.consumeNextWith(stringConsumer("data:first\ndata:second\n\n"))
.consumeNextWith(stringConsumer("data:first\ndata:second\n\n"))
.consumeNextWith(stringConsumer("data: first\ndata: second\n\n"))
.consumeNextWith(stringConsumer("data: first\ndata: second\n\n"))
.consumeNextWith(stringConsumer("data: first\ndata: second\n\n"))
.expectComplete()
.verify();
}
@@ -136,7 +136,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
.consumeNextWith(dataBuffer -> {
String value = dataBuffer.toString(charset);
DataBufferUtils.release(dataBuffer);
assertThat(value).isEqualTo("data:\u00A3\n\n");
assertThat(value).isEqualTo("data: \u00A3\n\n");
})
.expectComplete()
.verify();
@@ -151,10 +151,10 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
testWrite(source, outputMessage, Pojo.class);
StepVerifier.create(outputMessage.getBody())
.consumeNextWith(stringConsumer("data:"))
.consumeNextWith(stringConsumer("data: "))
.consumeNextWith(stringConsumer("{\"foo\":\"foofoo\",\"bar\":\"barbar\"}"))
.consumeNextWith(stringConsumer("\n\n"))
.consumeNextWith(stringConsumer("data:"))
.consumeNextWith(stringConsumer("data: "))
.consumeNextWith(stringConsumer("{\"foo\":\"foofoofoo\",\"bar\":\"barbarbar\"}"))
.consumeNextWith(stringConsumer("\n\n"))
.expectComplete()
@@ -173,19 +173,19 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
testWrite(source, outputMessage, Pojo.class);
StepVerifier.create(outputMessage.getBody())
.consumeNextWith(stringConsumer("data:"))
.consumeNextWith(stringConsumer("data: "))
.consumeNextWith(stringConsumer("""
{
data: "foo" : "foofoo",
data: "bar" : "barbar"
data:}"""))
data: "foo" : "foofoo",
data: "bar" : "barbar"
data: }"""))
.consumeNextWith(stringConsumer("\n\n"))
.consumeNextWith(stringConsumer("data:"))
.consumeNextWith(stringConsumer("data: "))
.consumeNextWith(stringConsumer("""
{
data: "foo" : "foofoofoo",
data: "bar" : "barbarbar"
data:}"""))
data: "foo" : "foofoofoo",
data: "bar" : "barbarbar"
data: }"""))
.consumeNextWith(stringConsumer("\n\n"))
.expectComplete()
.verify();
@@ -203,7 +203,7 @@ class ServerSentEventHttpMessageWriterTests extends AbstractDataBufferAllocating
assertThat(outputMessage.getHeaders().getContentType()).isEqualTo(mediaType);
StepVerifier.create(outputMessage.getBody())
.consumeNextWith(stringConsumer("data:", charset))
.consumeNextWith(stringConsumer("data: ", charset))
.consumeNextWith(stringConsumer("{\"foo\":\"foo\uD834\uDD1E\",\"bar\":\"bar\uD834\uDD1E\"}", charset))
.consumeNextWith(stringConsumer("\n\n", charset))
.expectComplete()
@@ -51,7 +51,7 @@ class ServerSentEventTests {
void supportMultiLineComments(String newLine, String description) {
ServerSentEvent<String> event = ServerSentEvent.<String>builder()
.comment("foo" + newLine + "bar" + newLine + "baz").data("payload").build();
assertThat(event.format()).isEqualTo(":foo\n:bar\n:baz\ndata:");
assertThat(event.format()).isEqualTo(":foo\n:bar\n:baz\ndata: ");
}
private static Stream<Arguments> newLineCharacters() {
@@ -415,7 +415,7 @@ class JacksonJsonHttpMessageConverterTests {
MediaType.APPLICATION_JSON, outputMessage, null);
String result = outputMessage.getBodyAsString(StandardCharsets.UTF_8);
assertThat(result).isEqualTo("{\ndata: \"name\" : \"Jason\"\ndata:}");
assertThat(result).isEqualTo("{\ndata: \"name\" : \"Jason\"\ndata: }");
}
@Test
@@ -385,7 +385,7 @@ class MappingJackson2HttpMessageConverterTests {
this.converter.writeInternal(bean, null, outputMessage);
String result = outputMessage.getBodyAsString(StandardCharsets.UTF_8);
assertThat(result).isEqualTo("{\ndata: \"name\" : \"Jason\"\ndata:}");
assertThat(result).isEqualTo("{\ndata: \"name\" : \"Jason\"\ndata: }");
}
@Test
@@ -44,9 +44,9 @@ class SseUtilsTests {
@MethodSource("newLineCharacters")
void appendFieldValueReplacesLineSeparatorWithFieldPrefix(String newLine, String description) {
StringBuilder sb = new StringBuilder();
sb.append("data:");
sb.append("data: ");
SseUtils.appendFieldValue("data", "first" + newLine + "second", sb);
assertThat(sb).hasToString("data:first\ndata:second");
assertThat(sb).hasToString("data: first\ndata: second");
}
@ParameterizedTest(name = "{1}")
@@ -590,7 +590,7 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport imp
ServerSentEvent<?> sse = (ServerSentEvent<?>) hints;
CharSequence eventText = (sse != null ? sse.format() :
(fragment.viewName() != null ? "event:" + fragment.viewName() + "\n" : "") + "data:");
(fragment.viewName() != null ? "event:" + fragment.viewName() + "\n" : "") + "data: ");
DataBuffer prefix = encodeText(eventText.toString(), charset, bufferFactory);
DataBuffer suffix = encodeText("\n\n", charset, bufferFactory);
@@ -107,14 +107,14 @@ class FragmentViewResolutionResultHandlerTests {
on(Handler.class).resolveReturnType(Flux.class, Fragment.class),
"""
event:fragment1
data:<p>
data: Hello Foo
data:</p>
data: <p>
data: Hello Foo
data: </p>
event:fragment2
data:<p>
data: Hello Bar
data:</p>
data: <p>
data: Hello Bar
data: </p>
""");
}
@@ -126,10 +126,10 @@ class FragmentViewResolutionResultHandlerTests {
on(Handler.class).resolveReturnType(Flux.class, Fragment.class),
"""
event:fragment1
data:<p>
data: Hello Foo
data: and Bar
data:</p>
data: <p>
data: Hello Foo
data: and Bar
data: </p>
""");
}
@@ -147,15 +147,15 @@ class FragmentViewResolutionResultHandlerTests {
"""
id:id1
event:event1
data:<p>
data: Hello Foo
data:</p>
data: <p>
data: Hello Foo
data: </p>
id:id2
event:event2
data:<p>
data: Hello Bar
data:</p>
data: <p>
data: Hello Bar
data: </p>
""");
}
@@ -177,6 +177,9 @@ final class SseServerResponse extends AbstractServerResponse {
private SseBuilder field(String name, String value) {
this.builder.append(name).append(':');
if (!name.isEmpty()) {
this.builder.append(' ');
}
SseUtils.appendFieldValue(name, value, this.builder);
this.builder.append('\n');
return this;
@@ -200,7 +203,7 @@ final class SseServerResponse extends AbstractServerResponse {
@SuppressWarnings("unchecked")
private void writeObject(Object data) throws IOException {
this.builder.append("data:");
this.builder.append("data: ");
try {
this.outputMessage.getBody().write(builderBytes());
Class<?> dataClass = data.getClass();
@@ -240,7 +240,7 @@ public class SseEmitter extends ResponseBodyEmitter {
if (object instanceof ModelAndView mav && !this.hasName && mav.getViewName() != null) {
name(mav.getViewName());
}
append("data:");
append("data: ");
saveAppendedText(TEXT_PLAIN);
if (object instanceof String text) {
writeStringData(text, mediaType);
@@ -71,7 +71,7 @@ class SseServerResponseTests {
ModelAndView mav = response.writeTo(this.mockRequest, this.mockResponse, context);
assertThat(mav).isNull();
String expected = "data:" + body + "\n\n";
String expected = "data: " + body + "\n\n";
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
}
@@ -127,7 +127,7 @@ class SseServerResponseTests {
ModelAndView mav = response.writeTo(this.mockRequest, this.mockResponse, context);
assertThat(mav).isNull();
String expected = "data:{\"name\":\"John Doe\",\"age\":42}\n\n";
String expected = "data: {\"name\":\"John Doe\",\"age\":42}\n\n";
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
}
@@ -151,10 +151,10 @@ class SseServerResponseTests {
assertThat(mav).isNull();
String expected = """
data:{
data: "name" : "John Doe",
data: "age" : 42
data:}
data: {
data: "name" : "John Doe",
data: "age" : 42
data: }
""";
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
@@ -186,7 +186,7 @@ class SseServerResponseTests {
:comment line 1
:comment line 2
retry:1000
data:data
data: data
""";
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
@@ -229,7 +229,7 @@ class SseServerResponseTests {
ModelAndView mav = response.writeTo(this.mockRequest, this.mockResponse, context);
assertThat(mav).isNull();
String expected = "data:line1\ndata:line2\ndata:line3\n\n";
String expected = "data: line1\ndata: line2\ndata: line3\n\n";
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
}
@@ -104,14 +104,14 @@ class FragmentRenderingStreamTests {
assertThat(this.response.getContentType()).isEqualTo("text/event-stream");
assertThat(this.response.getContentAsString()).isEqualTo(("""
event:fragment1
data:<p>
data: Hello Foo
data:</p>
data: <p>
data: Hello Foo
data: </p>
event:fragment2
data:<p>
data: Hello Bar
data:</p>
data: <p>
data: Hello Bar
data: </p>
"""));
}
@@ -134,14 +134,14 @@ class FragmentRenderingStreamTests {
assertThat(this.response.getContentType()).isEqualTo("text/event-stream");
assertThat(this.response.getContentAsString()).isEqualTo(("""
event:fragment1
data:<p>
data: Hello Foo
data:</p>
data: <p>
data: Hello Foo
data: </p>
event:fragment2
data:<p>
data: Hello Bar
data:</p>
data: <p>
data: Hello Bar
data: </p>
"""));
}
@@ -163,10 +163,10 @@ class FragmentRenderingStreamTests {
assertThat(this.response.getContentType()).isEqualTo("text/event-stream");
assertThat(this.response.getContentAsString()).isEqualTo(("""
event:fragment1
data:<p>
data: Hello Foo
data: and Bar
data:</p>
data: <p>
data: Hello Foo
data: and Bar
data: </p>
"""));
}
@@ -267,7 +267,7 @@ class ReactiveTypeHandlerTests {
sink.tryEmitNext("baz");
sink.tryEmitComplete();
assertThat(emitterHandler.getValuesAsText()).isEqualTo("data:foo\n\ndata:bar\n\ndata:baz\n\n");
assertThat(emitterHandler.getValuesAsText()).isEqualTo("data: foo\n\ndata: bar\n\ndata: baz\n\n");
}
@Test
@@ -286,7 +286,7 @@ class ReactiveTypeHandlerTests {
sink.tryEmitNext(ServerSentEvent.builder("baz").id("3").build());
sink.tryEmitComplete();
assertThat(emitterHandler.getValuesAsText()).isEqualTo("id:1\ndata:foo\n\nid:2\ndata:bar\n\nid:3\ndata:baz\n\n");
assertThat(emitterHandler.getValuesAsText()).isEqualTo("id:1\ndata: foo\n\nid:2\ndata: bar\n\nid:3\ndata: baz\n\n");
}
@ParameterizedTest
@@ -453,7 +453,7 @@ class ReactiveTypeHandlerTests {
sink.tryEmitComplete();
assertThat(emitterHandler.getValuesAsText()).isEqualTo("data:emitted value\n\n");
assertThat(emitterHandler.getValuesAsText()).isEqualTo("data: emitted value\n\n");
assertThat(emitterHandler.getSavedRequest()).isSameAs(this.servletRequest);
}
finally {
@@ -218,7 +218,7 @@ class RequestMappingHandlerAdapterTests {
assertThat(this.response.getStatus()).isEqualTo(200);
assertThat(this.response.getHeader("Content-Type")).isEqualTo("text/event-stream");
assertThat(this.response.getContentAsString()).isEqualTo("data:event 1\n\ndata:event 2\n\n");
assertThat(this.response.getContentAsString()).isEqualTo("data: event 1\n\ndata: event 2\n\n");
}
@Test
@@ -218,8 +218,8 @@ class ResponseBodyEmitterReturnValueHandlerTests {
event:update
id:1
retry:5000
data:{"id":1,"name":"Joe"}
data:{"id":2,"name":"John"}
data: {"id":1,"name":"Joe"}
data: {"id":2,"name":"John"}
"""));
}
@@ -235,7 +235,7 @@ class ResponseBodyEmitterReturnValueHandlerTests {
assertThat(this.response.getStatus()).isEqualTo(200);
assertThat(this.response.getContentType()).isEqualTo("text/event-stream");
assertThat(this.response.getContentAsString()).isEqualTo("data:foo\n\ndata:bar\n\ndata:baz\n\n");
assertThat(this.response.getContentAsString()).isEqualTo("data: foo\n\ndata: bar\n\ndata: baz\n\n");
}
@Test
@@ -274,7 +274,7 @@ class ResponseBodyEmitterReturnValueHandlerTests {
assertThat(this.response.getStatus()).isEqualTo(200);
assertThat(this.response.getContentType()).isEqualTo("text/event-stream");
assertThat(this.response.getContentAsString()).isEqualTo("data:foo123\n\ndata:bar123\n\ndata:baz123\n\n");
assertThat(this.response.getContentAsString()).isEqualTo("data: foo123\n\ndata: bar123\n\ndata: baz123\n\n");
}
@Test // gh-21972
@@ -357,7 +357,7 @@ class ResponseBodyEmitterReturnValueHandlerTests {
assertThat(this.request.isAsyncStarted()).isTrue();
assertThat(this.response.getStatus()).isEqualTo(200);
assertThat(this.response.getContentType()).isEqualTo("text/event-stream");
assertThat(this.response.getContentAsString()).isEqualTo("data:foo\n\ndata:bar\n\n");
assertThat(this.response.getContentAsString()).isEqualTo("data: foo\n\ndata: bar\n\n");
}
@Test // gh-36357
@@ -65,7 +65,7 @@ class SseEmitterTests {
void send() throws Exception {
this.emitter.send("foo");
this.handler.assertSentObjectCount(3);
this.handler.assertObject(0, "data:", TEXT_PLAIN_UTF8);
this.handler.assertObject(0, "data: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "foo");
this.handler.assertObject(2, "\n\n", TEXT_PLAIN_UTF8);
this.handler.assertWriteCount(1);
@@ -75,7 +75,7 @@ class SseEmitterTests {
void sendWithMediaType() throws Exception {
this.emitter.send("foo", MediaType.TEXT_PLAIN);
this.handler.assertSentObjectCount(3);
this.handler.assertObject(0, "data:", TEXT_PLAIN_UTF8);
this.handler.assertObject(0, "data: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "foo", MediaType.TEXT_PLAIN);
this.handler.assertObject(2, "\n\n", TEXT_PLAIN_UTF8);
this.handler.assertWriteCount(1);
@@ -92,7 +92,7 @@ class SseEmitterTests {
void sendEventWithDataLine() throws Exception {
this.emitter.send(event().data("foo"));
this.handler.assertSentObjectCount(3);
this.handler.assertObject(0, "data:", TEXT_PLAIN_UTF8);
this.handler.assertObject(0, "data: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "foo");
this.handler.assertObject(2, "\n\n", TEXT_PLAIN_UTF8);
this.handler.assertWriteCount(1);
@@ -102,9 +102,9 @@ class SseEmitterTests {
void sendEventWithTwoDataLines() throws Exception {
this.emitter.send(event().data("foo").data("bar"));
this.handler.assertSentObjectCount(5);
this.handler.assertObject(0, "data:", TEXT_PLAIN_UTF8);
this.handler.assertObject(0, "data: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "foo");
this.handler.assertObject(2, "\ndata:", TEXT_PLAIN_UTF8);
this.handler.assertObject(2, "\ndata: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(3, "bar");
this.handler.assertObject(4, "\n\n", TEXT_PLAIN_UTF8);
this.handler.assertWriteCount(1);
@@ -115,8 +115,8 @@ class SseEmitterTests {
void sendEventWithMultiline(String newLineChars, String description) throws Exception {
this.emitter.send(event().data("foo" + newLineChars + "bar" + newLineChars + "baz"));
this.handler.assertSentObjectCount(3);
this.handler.assertObject(0, "data:", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "foo\ndata:bar\ndata:baz");
this.handler.assertObject(0, "data: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "foo\ndata: bar\ndata: baz");
this.handler.assertObject(2, "\n\n", TEXT_PLAIN_UTF8);
this.handler.assertWriteCount(1);
}
@@ -126,8 +126,8 @@ class SseEmitterTests {
void sendEventWithMultilineWithMediaType(String newLineChars, String description) throws Exception {
this.emitter.send(event().data("foo" + newLineChars + "bar" + newLineChars + "baz", MediaType.TEXT_PLAIN));
this.handler.assertSentObjectCount(3);
this.handler.assertObject(0, "data:", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "foo\ndata:bar\ndata:baz", MediaType.TEXT_PLAIN);
this.handler.assertObject(0, "data: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "foo\ndata: bar\ndata: baz", MediaType.TEXT_PLAIN);
this.handler.assertObject(2, "\n\n", TEXT_PLAIN_UTF8);
this.handler.assertWriteCount(1);
}
@@ -136,7 +136,7 @@ class SseEmitterTests {
void sendEventFull() throws Exception {
this.emitter.send(event().comment("blah").name("test").reconnectTime(5000L).id("1").data("foo"));
this.handler.assertSentObjectCount(3);
this.handler.assertObject(0, ":blah\nevent:test\nretry:5000\nid:1\ndata:", TEXT_PLAIN_UTF8);
this.handler.assertObject(0, ":blah\nevent:test\nretry:5000\nid:1\ndata: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "foo");
this.handler.assertObject(2, "\n\n", TEXT_PLAIN_UTF8);
this.handler.assertWriteCount(1);
@@ -146,9 +146,9 @@ class SseEmitterTests {
void sendEventFullWithTwoDataLinesInTheMiddle() throws Exception {
this.emitter.send(event().comment("blah").data("foo").data("bar").name("test").reconnectTime(5000L).id("1"));
this.handler.assertSentObjectCount(5);
this.handler.assertObject(0, ":blah\ndata:", TEXT_PLAIN_UTF8);
this.handler.assertObject(0, ":blah\ndata: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "foo");
this.handler.assertObject(2, "\ndata:", TEXT_PLAIN_UTF8);
this.handler.assertObject(2, "\ndata: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(3, "bar");
this.handler.assertObject(4, "\nevent:test\nretry:5000\nid:1\n\n", TEXT_PLAIN_UTF8);
this.handler.assertWriteCount(1);
@@ -173,7 +173,7 @@ class SseEmitterTests {
void supportMultiLineComments(String newLineChars, String description) throws Exception {
this.emitter.send(event().comment("foo" + newLineChars + "bar" + newLineChars + "baz").data("payload"));
this.handler.assertSentObjectCount(3);
this.handler.assertObject(0, ":foo\n:bar\n:baz\ndata:", TEXT_PLAIN_UTF8);
this.handler.assertObject(0, ":foo\n:bar\n:baz\ndata: ", TEXT_PLAIN_UTF8);
this.handler.assertObject(1, "payload");
this.handler.assertObject(2, "\n\n", TEXT_PLAIN_UTF8);
this.handler.assertWriteCount(1);