Skip to content

Commit c4b100a

Browse files
committedDec 11, 2024
Minor refactoring in ServerSentEvent
Extract re-usable method to serialize SSE fields. See gh-33975
1 parent fd88238 commit c4b100a

File tree

2 files changed

+36
-31
lines changed

2 files changed

+36
-31
lines changed
 

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

+29
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.springframework.lang.Nullable;
2222
import org.springframework.util.ObjectUtils;
23+
import org.springframework.util.StringUtils;
2324

2425
/**
2526
* Representation for a Server-Sent Event for use with Spring's reactive Web support.
@@ -102,6 +103,34 @@ public T data() {
102103
return this.data;
103104
}
104105

106+
/**
107+
* Return a StringBuilder with the id, event, retry, and comment fields fully
108+
* serialized, and also appending "data:" if there is data.
109+
* @since 6.2.1
110+
*/
111+
public String format() {
112+
StringBuilder sb = new StringBuilder();
113+
if (this.id != null) {
114+
appendAttribute("id", this.id, sb);
115+
}
116+
if (this.event != null) {
117+
appendAttribute("event", this.event, sb);
118+
}
119+
if (this.retry != null) {
120+
appendAttribute("retry", this.retry.toMillis(), sb);
121+
}
122+
if (this.comment != null) {
123+
sb.append(':').append(StringUtils.replace(this.comment, "\n", "\n:")).append('\n');
124+
}
125+
if (this.data != null) {
126+
sb.append("data:");
127+
}
128+
return sb.toString();
129+
}
130+
131+
private void appendAttribute(String fieldName, Object fieldValue, StringBuilder sb) {
132+
sb.append(fieldName).append(':').append(fieldValue).append('\n');
133+
}
105134

106135
@Override
107136
public boolean equals(@Nullable Object other) {

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

+7-31
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 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.
@@ -17,7 +17,6 @@
1717
package org.springframework.http.codec;
1818

1919
import java.nio.charset.StandardCharsets;
20-
import java.time.Duration;
2120
import java.util.Collections;
2221
import java.util.List;
2322
import java.util.Map;
@@ -124,65 +123,42 @@ private Flux<Publisher<DataBuffer>> encode(Publisher<?> input, ResolvableType el
124123
ServerSentEvent<?> sse = (element instanceof ServerSentEvent<?> serverSentEvent ?
125124
serverSentEvent : ServerSentEvent.builder().data(element).build());
126125

127-
StringBuilder sb = new StringBuilder();
128-
String id = sse.id();
129-
String event = sse.event();
130-
Duration retry = sse.retry();
131-
String comment = sse.comment();
126+
String sseText = sse.format();
132127
Object data = sse.data();
133-
if (id != null) {
134-
writeField("id", id, sb);
135-
}
136-
if (event != null) {
137-
writeField("event", event, sb);
138-
}
139-
if (retry != null) {
140-
writeField("retry", retry.toMillis(), sb);
141-
}
142-
if (comment != null) {
143-
sb.append(':').append(StringUtils.replace(comment, "\n", "\n:")).append('\n');
144-
}
145-
if (data != null) {
146-
sb.append("data:");
147-
}
148128

149129
Flux<DataBuffer> result;
150130
if (data == null) {
151-
result = Flux.just(encodeText(sb + "\n", mediaType, factory));
131+
result = Flux.just(encodeText(sseText + "\n", mediaType, factory));
152132
}
153133
else if (data instanceof String text) {
154134
text = StringUtils.replace(text, "\n", "\ndata:");
155-
result = Flux.just(encodeText(sb + text + "\n\n", mediaType, factory));
135+
result = Flux.just(encodeText(sseText + text + "\n\n", mediaType, factory));
156136
}
157137
else {
158-
result = encodeEvent(sb, data, dataType, mediaType, factory, hints);
138+
result = encodeEvent(sseText, data, dataType, mediaType, factory, hints);
159139
}
160140

161141
return result.doOnDiscard(DataBuffer.class, DataBufferUtils::release);
162142
});
163143
}
164144

165145
@SuppressWarnings("unchecked")
166-
private <T> Flux<DataBuffer> encodeEvent(StringBuilder eventContent, T data, ResolvableType dataType,
146+
private <T> Flux<DataBuffer> encodeEvent(CharSequence sseText, T data, ResolvableType dataType,
167147
MediaType mediaType, DataBufferFactory factory, Map<String, Object> hints) {
168148

169149
if (this.encoder == null) {
170150
throw new CodecException("No SSE encoder configured and the data is not String.");
171151
}
172152

173153
return Flux.defer(() -> {
174-
DataBuffer startBuffer = encodeText(eventContent, mediaType, factory);
154+
DataBuffer startBuffer = encodeText(sseText, mediaType, factory);
175155
DataBuffer endBuffer = encodeText("\n\n", mediaType, factory);
176156
DataBuffer dataBuffer = ((Encoder<T>) this.encoder).encodeValue(data, factory, dataType, mediaType, hints);
177157
Hints.touchDataBuffer(dataBuffer, hints, logger);
178158
return Flux.just(startBuffer, dataBuffer, endBuffer);
179159
});
180160
}
181161

182-
private void writeField(String fieldName, Object fieldValue, StringBuilder sb) {
183-
sb.append(fieldName).append(':').append(fieldValue).append('\n');
184-
}
185-
186162
private DataBuffer encodeText(CharSequence text, MediaType mediaType, DataBufferFactory bufferFactory) {
187163
Assert.notNull(mediaType.getCharset(), "Expected MediaType with charset");
188164
byte[] bytes = text.toString().getBytes(mediaType.getCharset());

0 commit comments

Comments
 (0)
Please sign in to comment.