1616
1717package org .springframework .http .server .reactive ;
1818
19+ import java .nio .charset .StandardCharsets ;
1920import java .time .Duration ;
2021
2122import org .junit .Before ;
2627import reactor .test .StepVerifier ;
2728
2829import org .springframework .core .io .buffer .DataBuffer ;
30+ import org .springframework .core .io .buffer .DataBufferFactory ;
2931import org .springframework .http .client .reactive .ReactorClientHttpConnector ;
3032import org .springframework .http .codec .BodyExtractors ;
33+ import org .springframework .util .Assert ;
3134import org .springframework .web .client .reactive .ClientRequest ;
3235import org .springframework .web .client .reactive .WebClient ;
3336
@@ -45,11 +48,8 @@ public void setup() throws Exception {
4548 }
4649
4750 @ Test
48- public void testFlushing () throws Exception {
49-
50- ClientRequest <Void > request = ClientRequest .GET ("http://localhost:" + port ).build ();
51-
52-
51+ public void writeAndFlushWith () throws Exception {
52+ ClientRequest <Void > request = ClientRequest .GET ("http://localhost:" + port + "/write-and-flush" ).build ();
5353 Mono <String > result = this .webClient
5454 .exchange (request )
5555 .flatMap (response -> response .body (BodyExtractors .toFlux (String .class )))
@@ -62,6 +62,20 @@ public void testFlushing() throws Exception {
6262 .verify (Duration .ofSeconds (5L ));
6363 }
6464
65+ @ Test // SPR-14991
66+ public void writeAndAutoFlushOnComplete () {
67+ ClientRequest <Void > request = ClientRequest .GET ("http://localhost:" + port + "/write-and-complete" ).build ();
68+ Mono <String > result = this .webClient
69+ .exchange (request )
70+ .flatMap (response -> response .bodyToFlux (String .class ))
71+ .reduce ((s1 , s2 ) -> s1 + s2 );
72+
73+ StepVerifier .create (result )
74+ .consumeNextWith (value -> Assert .isTrue (value .length () == 200000 ))
75+ .expectComplete ()
76+ .verify ();
77+ }
78+
6579 @ Override
6680 protected HttpHandler createHttpHandler () {
6781 return new FlushingHandler ();
@@ -71,21 +85,33 @@ private static class FlushingHandler implements HttpHandler {
7185
7286 @ Override
7387 public Mono <Void > handle (ServerHttpRequest request , ServerHttpResponse response ) {
74- Flux <Publisher <DataBuffer >> responseBody = Flux
75- .intervalMillis (50 )
76- .map (l -> {
77- byte [] data = ("data" + l ).getBytes ();
78- DataBuffer buffer = response .bufferFactory ().allocateBuffer (data .length );
79- buffer .write (data );
80- return buffer ;
81- })
82- .take (2 )
83- .map (Flux ::just );
84-
85- responseBody = responseBody .concatWith (Flux .never ());
86-
87- return response .writeAndFlushWith (responseBody );
88+ String path = request .getURI ().getPath ();
89+ if (path .endsWith ("write-and-flush" )) {
90+ Flux <Publisher <DataBuffer >> responseBody = Flux
91+ .intervalMillis (50 )
92+ .map (l -> toDataBuffer ("data" + l , response .bufferFactory ()))
93+ .take (2 )
94+ .map (Flux ::just );
95+ responseBody = responseBody .concatWith (Flux .never ());
96+ return response .writeAndFlushWith (responseBody );
97+ }
98+ else if (path .endsWith ("write-and-complete" )){
99+ Flux <DataBuffer > responseBody = Flux
100+ .just ("0123456789" )
101+ .repeat (20000 )
102+ .map (value -> toDataBuffer (value , response .bufferFactory ()));
103+ return response .writeWith (responseBody );
104+ }
105+ return response .writeWith (Flux .empty ());
88106 }
107+
108+ private DataBuffer toDataBuffer (String value , DataBufferFactory factory ) {
109+ byte [] data = (value ).getBytes (StandardCharsets .UTF_8 );
110+ DataBuffer buffer = factory .allocateBuffer (data .length );
111+ buffer .write (data );
112+ return buffer ;
113+ }
114+
89115 }
90116
91117}
0 commit comments