49
49
import org .springframework .web .method .support .ModelAndViewContainer ;
50
50
51
51
/**
52
- * Handler for return values of type {@link ResponseBodyEmitter} and subclasses
53
- * such as {@link SseEmitter} including the same types wrapped with
54
- * {@link ResponseEntity}.
52
+ * Handler for return values of type:
53
+ * <ul>
54
+ * <li>{@link ResponseBodyEmitter} including sub-class {@link SseEmitter} and others.
55
+ * <li>Reactive return types known to {@link ReactiveAdapterRegistry}.
56
+ * <li>Any of the above wrapped with {@link ResponseEntity}.
57
+ * </ul>
55
58
*
56
- * <p>As of 5.0 also supports reactive return value types for any reactive
57
- * library with registered adapters in {@link ReactiveAdapterRegistry}.
59
+ * <p>Single-value reactive types are adapted to {@link DeferredResult}.
60
+ * Multi-value reactive types are adapted to {@link ResponseBodyEmitter} or
61
+ * {@link SseEmitter} as follows:
62
+ * <ul>
63
+ * <li>SSE stream, if the element type is
64
+ * {@link org.springframework.http.codec.ServerSentEvent} or if negotiated by
65
+ * content type.
66
+ * <li>Text stream for a {@link org.reactivestreams.Publisher} of
67
+ * {@link CharSequence}.
68
+ * <li>A JSON stream if negotiated by content type to
69
+ * {@link MediaType#APPLICATION_NDJSON}.
70
+ * </ul>
58
71
*
59
72
* @author Rossen Stoyanchev
60
73
* @since 4.2
@@ -153,7 +166,7 @@ public void handleReturnValue(@Nullable Object returnValue, MethodParameter retu
153
166
else {
154
167
emitter = this .reactiveHandler .handleValue (returnValue , returnType , mavContainer , webRequest );
155
168
if (emitter == null ) {
156
- // Not streaming: write headers without committing response..
169
+ // We're not streaming; write headers without committing response
157
170
outputMessage .getHeaders ().forEach ((headerName , headerValues ) -> {
158
171
for (String headerValue : headerValues ) {
159
172
response .addHeader (headerName , headerValue );
@@ -164,18 +177,17 @@ public void handleReturnValue(@Nullable Object returnValue, MethodParameter retu
164
177
}
165
178
emitter .extendResponse (outputMessage );
166
179
167
- // At this point we know we're streaming..
180
+ // We are streaming
168
181
ShallowEtagHeaderFilter .disableContentCaching (request );
169
182
170
- // Wrap the response to ignore further header changes
171
- // Headers will be flushed at the first write
183
+ // Ignore further header changes; response is committed after first event
172
184
outputMessage = new StreamingServletServerHttpResponse (outputMessage );
173
185
174
186
HttpMessageConvertingHandler handler ;
175
187
try {
176
- DeferredResult <?> deferredResult = new DeferredResult <>(emitter .getTimeout ());
177
- WebAsyncUtils .getAsyncManager (webRequest ).startDeferredResultProcessing (deferredResult , mavContainer );
178
- handler = new HttpMessageConvertingHandler (outputMessage , deferredResult );
188
+ DeferredResult <?> result = new DeferredResult <>(emitter .getTimeout ());
189
+ WebAsyncUtils .getAsyncManager (webRequest ).startDeferredResultProcessing (result , mavContainer );
190
+ handler = new HttpMessageConvertingHandler (outputMessage , result );
179
191
}
180
192
catch (Throwable ex ) {
181
193
emitter .initializeWithError (ex );
@@ -186,6 +198,26 @@ public void handleReturnValue(@Nullable Object returnValue, MethodParameter retu
186
198
}
187
199
188
200
201
+ /**
202
+ * Wrap to silently ignore header changes HttpMessageConverter's that would
203
+ * otherwise cause HttpHeaders to raise exceptions.
204
+ */
205
+ private static class StreamingServletServerHttpResponse extends DelegatingServerHttpResponse {
206
+
207
+ private final HttpHeaders mutableHeaders = new HttpHeaders ();
208
+
209
+ public StreamingServletServerHttpResponse (ServerHttpResponse delegate ) {
210
+ super (delegate );
211
+ this .mutableHeaders .putAll (delegate .getHeaders ());
212
+ }
213
+
214
+ @ Override
215
+ public HttpHeaders getHeaders () {
216
+ return this .mutableHeaders ;
217
+ }
218
+ }
219
+
220
+
189
221
/**
190
222
* ResponseBodyEmitter.Handler that writes with HttpMessageConverter's.
191
223
*/
@@ -257,25 +289,4 @@ public void onCompletion(Runnable callback) {
257
289
}
258
290
}
259
291
260
-
261
- /**
262
- * Wrap to silently ignore header changes HttpMessageConverter's that would
263
- * otherwise cause HttpHeaders to raise exceptions.
264
- */
265
- private static class StreamingServletServerHttpResponse extends DelegatingServerHttpResponse {
266
-
267
- private final HttpHeaders mutableHeaders = new HttpHeaders ();
268
-
269
- public StreamingServletServerHttpResponse (ServerHttpResponse delegate ) {
270
- super (delegate );
271
- this .mutableHeaders .putAll (delegate .getHeaders ());
272
- }
273
-
274
- @ Override
275
- public HttpHeaders getHeaders () {
276
- return this .mutableHeaders ;
277
- }
278
-
279
- }
280
-
281
292
}
0 commit comments