Skip to content

Commit dd60fdd

Browse files
committed
Always process SSE "data:" line
ServerSentEventHttpMessageReader now always processes "data:" lines, including empty lines, as per SSE spec. Closes gh-35412
1 parent 3702031 commit dd60fdd

File tree

2 files changed

+19
-5
lines changed

2 files changed

+19
-5
lines changed

spring-web/src/main/java/org/springframework/http/codec/ServerSentEventHttpMessageReader.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,15 @@ public Flux<Object> read(
145145

146146
for (String line : lines) {
147147
if (line.startsWith("data:")) {
148+
data = (data != null ? data : new StringBuilder());
148149
int length = line.length();
149150
if (length > 5) {
150151
int index = (line.charAt(5) != ' ' ? 5 : 6);
151152
if (length > index) {
152-
data = (data != null ? data : new StringBuilder());
153153
data.append(line, index, line.length());
154-
data.append('\n');
155154
}
156155
}
156+
data.append('\n');
157157
}
158158
else if (shouldWrap) {
159159
if (line.startsWith("id:")) {

spring-web/src/test/java/org/springframework/http/codec/ServerSentEventHttpMessageReaderTests.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ void readServerSentEvents() {
6767
MockServerHttpRequest request = MockServerHttpRequest.post("/")
6868
.body(Mono.just(stringBuffer(
6969
"id:c42\nevent:foo\nretry:123\n:bla\n:bla bla\n:bla bla bla\ndata:bar\n\n" +
70-
"id:c43\nevent:bar\nretry:456\ndata:baz\n\ndata:\n\ndata: \n\n")));
70+
"id:c43\nevent:bar\nretry:456\ndata:baz\n\n" +
71+
"data:\n\n" +
72+
"data: \n\n")));
7173

7274
Flux<ServerSentEvent> events = this.reader
7375
.read(ResolvableType.forClassWithGenerics(ServerSentEvent.class, String.class),
@@ -78,8 +80,8 @@ void readServerSentEvents() {
7880
.retry(Duration.ofMillis(123)).comment("bla\nbla bla\nbla bla bla").data("bar").build())
7981
.expectNext(ServerSentEvent.builder().id("c43").event("bar")
8082
.retry(Duration.ofMillis(456)).data("baz").build())
81-
.consumeNextWith(event -> assertThat(event.data()).isNull())
82-
.consumeNextWith(event -> assertThat(event.data()).isNull())
83+
.consumeNextWith(event -> assertThat(event.data()).isEqualTo(""))
84+
.consumeNextWith(event -> assertThat(event.data()).isEqualTo(""))
8385
.expectComplete()
8486
.verify();
8587
}
@@ -135,6 +137,18 @@ void trimWhitespace() {
135137
.verify();
136138
}
137139

140+
@Test // gh-35412
141+
void emptyLines() {
142+
MockServerHttpRequest request = MockServerHttpRequest.post("/")
143+
.body(Mono.just(stringBuffer("id:1\nevent:message\ndata:\ndata:\ndata:\n\n")));
144+
145+
Flux<String> data = new ServerSentEventHttpMessageReader()
146+
.read(ResolvableType.forClass(String.class), request, Collections.emptyMap())
147+
.cast(String.class);
148+
149+
StepVerifier.create(data).expectNext("\n\n").verifyComplete();
150+
}
151+
138152
@Test
139153
void readPojo() {
140154
MockServerHttpRequest request = MockServerHttpRequest.post("/")

0 commit comments

Comments
 (0)