1
1
/*
2
- * Copyright 2002-2016 the original author or authors.
2
+ * Copyright 2002-2019 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package org .springframework .http .server .reactive ;
18
18
19
+ import java .nio .charset .StandardCharsets ;
19
20
import java .util .ArrayList ;
20
21
import java .util .Arrays ;
21
22
import java .util .List ;
22
23
import java .util .concurrent .Executors ;
23
24
import java .util .concurrent .TimeUnit ;
24
25
26
+ import io .netty .buffer .ByteBufAllocator ;
25
27
import org .junit .Before ;
26
28
import org .junit .Test ;
27
29
import org .reactivestreams .Publisher ;
28
30
import org .reactivestreams .Subscriber ;
29
31
import org .reactivestreams .Subscription ;
32
+ import reactor .core .publisher .BaseSubscriber ;
30
33
import reactor .core .publisher .Flux ;
31
34
import reactor .core .publisher .Mono ;
32
35
import reactor .core .publisher .Signal ;
33
36
34
- import static org .junit .Assert .assertEquals ;
35
- import static org .junit .Assert .assertNotNull ;
36
- import static org .junit .Assert .assertSame ;
37
- import static org .junit .Assert .assertTrue ;
37
+ import org .springframework .core .io .buffer .DataBuffer ;
38
+ import org .springframework .core .io .buffer .LeakAwareDataBufferFactory ;
39
+ import org .springframework .core .io .buffer .NettyDataBufferFactory ;
40
+
41
+ import static org .junit .Assert .*;
38
42
39
43
/**
40
44
* @author Rossen Stoyanchev
@@ -50,9 +54,6 @@ public void setUp() throws Exception {
50
54
this .writer = new OneByOneAsyncWriter ();
51
55
}
52
56
53
- private <T > Mono <Void > sendOperator (Publisher <String > source ){
54
- return new ChannelSendOperator <>(source , writer ::send );
55
- }
56
57
57
58
@ Test
58
59
public void errorBeforeFirstItem () throws Exception {
@@ -130,6 +131,66 @@ public void errorAfterMultipleItems() throws Exception {
130
131
assertSame (error , this .writer .error );
131
132
}
132
133
134
+ @ Test // gh-22720
135
+ public void cancelWhileItemCached () {
136
+ NettyDataBufferFactory delegate = new NettyDataBufferFactory (ByteBufAllocator .DEFAULT );
137
+ LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory (delegate );
138
+
139
+ ChannelSendOperator <DataBuffer > operator = new ChannelSendOperator <>(
140
+ Mono .fromCallable (() -> {
141
+ DataBuffer dataBuffer = bufferFactory .allocateBuffer ();
142
+ dataBuffer .write ("foo" , StandardCharsets .UTF_8 );
143
+ return dataBuffer ;
144
+ }),
145
+ publisher -> {
146
+ ZeroDemandSubscriber subscriber = new ZeroDemandSubscriber ();
147
+ publisher .subscribe (subscriber );
148
+ return Mono .never ();
149
+ });
150
+
151
+ BaseSubscriber <Void > subscriber = new BaseSubscriber <Void >() {};
152
+ operator .subscribe (subscriber );
153
+ subscriber .cancel ();
154
+
155
+ bufferFactory .checkForLeaks ();
156
+ }
157
+
158
+ @ Test // gh-22720
159
+ public void errorWhileItemCached () {
160
+ NettyDataBufferFactory delegate = new NettyDataBufferFactory (ByteBufAllocator .DEFAULT );
161
+ LeakAwareDataBufferFactory bufferFactory = new LeakAwareDataBufferFactory (delegate );
162
+ ZeroDemandSubscriber writeSubscriber = new ZeroDemandSubscriber ();
163
+
164
+ ChannelSendOperator <DataBuffer > operator = new ChannelSendOperator <>(
165
+ Flux .create (sink -> {
166
+ DataBuffer dataBuffer = bufferFactory .allocateBuffer ();
167
+ dataBuffer .write ("foo" , StandardCharsets .UTF_8 );
168
+ sink .next (dataBuffer );
169
+ sink .error (new IllegalStateException ("err" ));
170
+ }),
171
+ publisher -> {
172
+ publisher .subscribe (writeSubscriber );
173
+ return Mono .never ();
174
+ });
175
+
176
+
177
+ operator .subscribe (new BaseSubscriber <Void >() {});
178
+ try {
179
+ writeSubscriber .signalDemand (1 ); // Let cached signals ("foo" and error) be published..
180
+ }
181
+ catch (Throwable ex ) {
182
+ assertNotNull (ex .getCause ());
183
+ assertEquals ("err" , ex .getCause ().getMessage ());
184
+ }
185
+
186
+ bufferFactory .checkForLeaks ();
187
+ }
188
+
189
+
190
+ private <T > Mono <Void > sendOperator (Publisher <String > source ){
191
+ return new ChannelSendOperator <>(source , writer ::send );
192
+ }
193
+
133
194
134
195
private static class OneByOneAsyncWriter {
135
196
@@ -182,4 +243,18 @@ public void onComplete() {
182
243
}
183
244
}
184
245
246
+
247
+ private static class ZeroDemandSubscriber extends BaseSubscriber <DataBuffer > {
248
+
249
+
250
+ @ Override
251
+ protected void hookOnSubscribe (Subscription subscription ) {
252
+ // Just subscribe without requesting
253
+ }
254
+
255
+ public void signalDemand (long demand ) {
256
+ upstream ().request (demand );
257
+ }
258
+ }
259
+
185
260
}
0 commit comments