Skip to content

Commit f9ae54d

Browse files
committed
Allow sending SSE events without data
Prior to this commit, the SseBuilder API in WebMvc.fn would only allow to send Server-Sent Events with `data:` items in them. The spec doesnn't disallow this and other specifications rely on such patterns to signal the completion of a stream, like: ``` event:next data:some data event:complete ``` This commit adds a new `send()` method without any argument that sends the buffered content (id, event comment and retry) without data. Fixes: gh-32270
1 parent 2284254 commit f9ae54d

File tree

3 files changed

+51
-16
lines changed

3 files changed

+51
-16
lines changed

spring-webmvc/src/main/java/org/springframework/web/servlet/function/ServerResponse.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -570,6 +570,15 @@ interface SseBuilder {
570570
*/
571571
void send(Object object) throws IOException;
572572

573+
/**
574+
* Sends the buffered content as a server-sent event, without data.
575+
* Only the {@link #event(String) events} and {@link #comment(String) comments}
576+
* will be sent.
577+
* @throws IOException in case of I/O errors
578+
* @since 6.1.4
579+
*/
580+
void send() throws IOException;
581+
573582
/**
574583
* Add an SSE "id" line.
575584
* @param id the event identifier

spring-webmvc/src/main/java/org/springframework/web/servlet/function/SseServerResponse.java

+18-14
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,23 @@ public void send(Object object) throws IOException {
135135
data(object);
136136
}
137137

138+
@Override
139+
public void send() throws IOException {
140+
this.builder.append('\n');
141+
try {
142+
OutputStream body = this.outputMessage.getBody();
143+
body.write(builderBytes());
144+
body.flush();
145+
}
146+
catch (IOException ex) {
147+
this.sendFailed = true;
148+
throw ex;
149+
}
150+
finally {
151+
this.builder.setLength(0);
152+
}
153+
}
154+
138155
@Override
139156
public SseBuilder id(String id) {
140157
Assert.hasLength(id, "Id must not be empty");
@@ -186,20 +203,7 @@ private void writeString(String string) throws IOException {
186203
for (String line : lines) {
187204
field("data", line);
188205
}
189-
this.builder.append('\n');
190-
191-
try {
192-
OutputStream body = this.outputMessage.getBody();
193-
body.write(builderBytes());
194-
body.flush();
195-
}
196-
catch (IOException ex) {
197-
this.sendFailed = true;
198-
throw ex;
199-
}
200-
finally {
201-
this.builder.setLength(0);
202-
}
206+
this.send();
203207
}
204208

205209
@SuppressWarnings("unchecked")

spring-webmvc/src/test/java/org/springframework/web/servlet/function/SseServerResponseTests.java

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -32,8 +32,10 @@
3232
import static org.assertj.core.api.Assertions.assertThat;
3333

3434
/**
35+
* Tests for {@link ServerResponse.SseBuilder}.
3536
* @author Arjen Poutsma
3637
* @author Sebastien Deleuze
38+
* @author Brian Clozel
3739
*/
3840
class SseServerResponseTests {
3941

@@ -151,6 +153,26 @@ void builder() throws Exception {
151153
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
152154
}
153155

156+
@Test
157+
void sendWithoutData() throws Exception {
158+
ServerResponse response = ServerResponse.sse(sse -> {
159+
try {
160+
sse.event("custom").send();
161+
}
162+
catch (IOException ex) {
163+
throw new UncheckedIOException(ex);
164+
}
165+
});
166+
167+
ServerResponse.Context context = Collections::emptyList;
168+
169+
ModelAndView mav = response.writeTo(this.mockRequest, this.mockResponse, context);
170+
assertThat(mav).isNull();
171+
172+
String expected = "event:custom\n\n";
173+
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
174+
}
175+
154176

155177
private static final class Person {
156178

0 commit comments

Comments
 (0)