26
26
import java .util .Collection ;
27
27
import java .util .List ;
28
28
import java .util .Random ;
29
+ import java .util .concurrent .ConcurrentHashMap ;
30
+ import java .util .concurrent .ConcurrentMap ;
31
+ import java .util .concurrent .ExecutionException ;
32
+ import java .util .concurrent .TimeUnit ;
33
+ import java .util .concurrent .TimeoutException ;
34
+ import java .util .concurrent .atomic .AtomicInteger ;
35
+ import java .util .logging .Logger ;
29
36
30
37
import jakarta .ws .rs .client .Entity ;
31
38
import jakarta .ws .rs .core .MediaType ;
32
39
import jakarta .ws .rs .core .MultivaluedMap ;
33
40
import jakarta .ws .rs .core .Response ;
34
41
import org .apache .cxf .interceptor .LoggingInInterceptor ;
42
+ import org .apache .cxf .interceptor .LoggingMessage ;
35
43
import org .apache .cxf .interceptor .LoggingOutInterceptor ;
36
44
import org .apache .cxf .jaxrs .client .ClientConfiguration ;
37
45
import org .apache .cxf .jaxrs .client .WebClient ;
40
48
import org .apache .cxf .jaxrs .impl .MetadataMap ;
41
49
import org .apache .cxf .jaxrs .model .AbstractResourceInfo ;
42
50
import org .apache .cxf .jaxrs .provider .MultipartProvider ;
51
+ import org .apache .cxf .message .Message ;
43
52
import org .apache .cxf .testutil .common .AbstractBusClientServerTestBase ;
44
53
import org .apache .cxf .transport .http .asyncclient .hc5 .AsyncHTTPConduit ;
45
54
50
59
51
60
import static org .hamcrest .CoreMatchers .equalTo ;
52
61
import static org .hamcrest .CoreMatchers .not ;
62
+ import static org .hamcrest .CoreMatchers .startsWith ;
53
63
import static org .junit .Assert .assertThat ;
54
64
import static org .junit .Assert .assertTrue ;
55
65
56
66
@ RunWith (value = org .junit .runners .Parameterized .class )
57
67
public class JAXRSAsyncClientChunkingTest extends AbstractBusClientServerTestBase {
58
68
private static final String PORT = allocatePort (FileStoreServer .class );
59
69
private final Boolean chunked ;
70
+ private final Boolean autoRedirect ;
71
+ private final ConcurrentMap <String , AtomicInteger > ids = new ConcurrentHashMap <>();
60
72
61
- public JAXRSAsyncClientChunkingTest (Boolean chunked ) {
73
+ public JAXRSAsyncClientChunkingTest (Boolean chunked , Boolean autoRedirect ) {
62
74
this .chunked = chunked ;
75
+ this .autoRedirect = autoRedirect ;
63
76
}
64
77
65
78
@ BeforeClass
@@ -69,9 +82,14 @@ public static void startServers() throws Exception {
69
82
createStaticBus ();
70
83
}
71
84
72
- @ Parameters (name = "{0}" )
73
- public static Collection <Boolean > data () {
74
- return Arrays .asList (new Boolean [] {Boolean .FALSE , Boolean .TRUE });
85
+ @ Parameters (name = "chunked {0}, auto-redirect {1}" )
86
+ public static Collection <Boolean []> data () {
87
+ return Arrays .asList (new Boolean [][] {
88
+ {Boolean .FALSE /* chunked */ , Boolean .FALSE /* autoredirect */ },
89
+ {Boolean .FALSE /* chunked */ , Boolean .TRUE /* autoredirect */ },
90
+ {Boolean .TRUE /* chunked */ , Boolean .FALSE /* autoredirect */ },
91
+ {Boolean .TRUE /* chunked */ , Boolean .TRUE /* autoredirect */ },
92
+ });
75
93
}
76
94
77
95
@ Test
@@ -82,24 +100,61 @@ public void testMultipartChunking() {
82
100
final ClientConfiguration config = WebClient .getConfig (webClient );
83
101
config .getBus ().setProperty (AsyncHTTPConduit .USE_ASYNC , true );
84
102
config .getHttpConduit ().getClient ().setAllowChunking (chunked );
103
+ config .getHttpConduit ().getClient ().setAutoRedirect (autoRedirect );
85
104
configureLogging (config );
86
105
106
+ final String filename = "keymanagers.jks" ;
87
107
try {
88
- final String filename = "keymanagers.jks" ;
89
108
final MultivaluedMap <String , String > headers = new MetadataMap <>();
90
109
headers .add ("Content-ID" , filename );
91
110
headers .add ("Content-Type" , "application/binary" );
92
- headers .add ("Content-Disposition" , "attachment; filename=" + chunked + "_" + filename );
111
+ headers .add ("Content-Disposition" , "attachment; filename=" + chunked + "_" + autoRedirect + "_" + filename );
93
112
final Attachment att = new Attachment (getClass ().getResourceAsStream ("/" + filename ), headers );
94
113
final MultipartBody entity = new MultipartBody (att );
95
- try (Response response = webClient .header ("Content-Type" , "multipart/form-data" ).post (entity )) {
114
+ try (Response response = webClient .header ("Content-Type" , MediaType . MULTIPART_FORM_DATA ).post (entity )) {
96
115
assertThat (response .getStatus (), equalTo (201 ));
97
116
assertThat (response .getHeaderString ("Transfer-Encoding" ), equalTo (chunked ? "chunked" : null ));
98
117
assertThat (response .getEntity (), not (equalTo (null )));
99
118
}
100
119
} finally {
101
120
webClient .close ();
102
121
}
122
+
123
+ assertRedirect (chunked + "_" + autoRedirect + "_" + filename );
124
+ }
125
+
126
+ @ Test
127
+ public void testMultipartChunkingAsync () throws InterruptedException , ExecutionException , TimeoutException {
128
+ final String url = "http://localhost:" + PORT + "/file-store" ;
129
+ final WebClient webClient = WebClient .create (url , List .of (new MultipartProvider ())).query ("chunked" , chunked );
130
+
131
+ final ClientConfiguration config = WebClient .getConfig (webClient );
132
+ config .getBus ().setProperty (AsyncHTTPConduit .USE_ASYNC , true );
133
+ config .getHttpConduit ().getClient ().setAllowChunking (chunked );
134
+ config .getHttpConduit ().getClient ().setAutoRedirect (autoRedirect );
135
+ configureLogging (config );
136
+
137
+ final String filename = "keymanagers.jks" ;
138
+ try {
139
+ final MultivaluedMap <String , String > headers = new MetadataMap <>();
140
+ headers .add ("Content-ID" , filename );
141
+ headers .add ("Content-Type" , "application/binary" );
142
+ headers .add ("Content-Disposition" , "attachment; filename=" + chunked
143
+ + "_" + autoRedirect + "_async_" + filename );
144
+ final Attachment att = new Attachment (getClass ().getResourceAsStream ("/" + filename ), headers );
145
+ final Entity <MultipartBody > entity = Entity .entity (new MultipartBody (att ),
146
+ MediaType .MULTIPART_FORM_DATA_TYPE );
147
+ try (Response response = webClient .header ("Content-Type" , MediaType .MULTIPART_FORM_DATA ).async ()
148
+ .post (entity ).get (10 , TimeUnit .SECONDS )) {
149
+ assertThat (response .getStatus (), equalTo (201 ));
150
+ assertThat (response .getHeaderString ("Transfer-Encoding" ), equalTo (chunked ? "chunked" : null ));
151
+ assertThat (response .getEntity (), not (equalTo (null )));
152
+ }
153
+ } finally {
154
+ webClient .close ();
155
+ }
156
+
157
+ assertRedirect (chunked + "_" + autoRedirect + "_" + filename );
103
158
}
104
159
105
160
@ Test
@@ -110,6 +165,7 @@ public void testStreamChunking() throws IOException {
110
165
final ClientConfiguration config = WebClient .getConfig (webClient );
111
166
config .getBus ().setProperty (AsyncHTTPConduit .USE_ASYNC , true );
112
167
config .getHttpConduit ().getClient ().setAllowChunking (chunked );
168
+ config .getHttpConduit ().getClient ().setAutoRedirect (autoRedirect );
113
169
configureLogging (config );
114
170
115
171
final byte [] bytes = new byte [32 * 1024 ];
@@ -126,13 +182,89 @@ public void testStreamChunking() throws IOException {
126
182
} finally {
127
183
webClient .close ();
128
184
}
185
+
186
+ assertNoDuplicateLogging ();
129
187
}
130
-
188
+
189
+ @ Test
190
+ public void testStreamChunkingAsync () throws IOException , InterruptedException ,
191
+ ExecutionException , TimeoutException {
192
+ final String url = "http://localhost:" + PORT + "/file-store/stream" ;
193
+ final WebClient webClient = WebClient .create (url ).query ("chunked" , chunked );
194
+
195
+ final ClientConfiguration config = WebClient .getConfig (webClient );
196
+ config .getBus ().setProperty (AsyncHTTPConduit .USE_ASYNC , true );
197
+ config .getHttpConduit ().getClient ().setAllowChunking (chunked );
198
+ config .getHttpConduit ().getClient ().setAutoRedirect (autoRedirect );
199
+ configureLogging (config );
200
+
201
+ final byte [] bytes = new byte [32 * 1024 ];
202
+ final Random random = new Random ();
203
+ random .nextBytes (bytes );
204
+
205
+ try (InputStream in = new ByteArrayInputStream (bytes )) {
206
+ final Entity <InputStream > entity = Entity .entity (in , MediaType .APPLICATION_OCTET_STREAM );
207
+ try (Response response = webClient .async ().post (entity ).get (10 , TimeUnit .SECONDS )) {
208
+ assertThat (response .getStatus (), equalTo (200 ));
209
+ assertThat (response .getHeaderString ("Transfer-Encoding" ), equalTo (chunked ? "chunked" : null ));
210
+ assertThat (response .getEntity (), not (equalTo (null )));
211
+ }
212
+ } finally {
213
+ webClient .close ();
214
+ }
215
+
216
+ assertNoDuplicateLogging ();
217
+ }
218
+
219
+ private void assertRedirect (String filename ) {
220
+ final String url = "http://localhost:" + PORT + "/file-store/redirect" ;
221
+
222
+ final WebClient webClient = WebClient .create (url , List .of (new MultipartProvider ()))
223
+ .query ("chunked" , chunked )
224
+ .query ("filename" , filename );
225
+
226
+ final ClientConfiguration config = WebClient .getConfig (webClient );
227
+ config .getBus ().setProperty (AsyncHTTPConduit .USE_ASYNC , true );
228
+ config .getHttpConduit ().getClient ().setAllowChunking (chunked );
229
+ config .getHttpConduit ().getClient ().setAutoRedirect (autoRedirect );
230
+ configureLogging (config );
231
+
232
+ try {
233
+ try (Response response = webClient .get ()) {
234
+ if (autoRedirect ) {
235
+ assertThat (response .getStatus (), equalTo (200 ));
236
+ assertThat (response .getHeaderString ("Transfer-Encoding" ), equalTo (chunked ? "chunked" : null ));
237
+ assertThat (response .getEntity (), not (equalTo (null )));
238
+ } else {
239
+ assertThat (response .getStatus (), equalTo (303 ));
240
+ assertThat (response .getHeaderString ("Location" ),
241
+ startsWith ("http://localhost:" + PORT + "/file-store" ));
242
+ }
243
+ }
244
+ } finally {
245
+ webClient .close ();
246
+ }
247
+
248
+ assertNoDuplicateLogging ();
249
+ }
250
+
251
+ private void assertNoDuplicateLogging () {
252
+ ids .forEach ((id , counter ) -> assertThat ("Duplicate client logging for message " + id ,
253
+ counter .get (), equalTo (1 )));
254
+ }
255
+
131
256
private void configureLogging (final ClientConfiguration config ) {
132
257
final LoggingOutInterceptor out = new LoggingOutInterceptor ();
133
258
out .setShowMultipartContent (false );
134
259
135
- final LoggingInInterceptor in = new LoggingInInterceptor ();
260
+ final LoggingInInterceptor in = new LoggingInInterceptor () {
261
+ @ Override
262
+ protected void logging (Logger logger , Message message ) {
263
+ super .logging (logger , message );
264
+ final String id = (String ) message .get (LoggingMessage .ID_KEY );
265
+ ids .computeIfAbsent (id , key -> new AtomicInteger ()).incrementAndGet ();
266
+ }
267
+ };
136
268
in .setShowBinaryContent (false );
137
269
138
270
config .getInInterceptors ().add (in );
0 commit comments