42
42
import okhttp3 .mockwebserver .MockResponse ;
43
43
import okhttp3 .mockwebserver .MockWebServer ;
44
44
import okhttp3 .mockwebserver .RecordedRequest ;
45
- import okhttp3 .mockwebserver .SocketPolicy ;
46
45
import org .junit .jupiter .api .AfterEach ;
47
46
import org .junit .jupiter .api .Test ;
48
47
import org .junit .jupiter .params .ParameterizedTest ;
49
- import org .junit .jupiter .params .provider .Arguments ;
50
48
import org .junit .jupiter .params .provider .MethodSource ;
51
- import org .springframework .util .SocketUtils ;
52
- import org .springframework .web .reactive .function .client .WebClient .ResponseSpec ;
53
49
import reactor .core .publisher .Flux ;
54
50
import reactor .core .publisher .Mono ;
55
51
import reactor .netty .http .client .HttpClient ;
73
69
import org .springframework .http .client .reactive .HttpComponentsClientHttpConnector ;
74
70
import org .springframework .http .client .reactive .JettyClientHttpConnector ;
75
71
import org .springframework .http .client .reactive .ReactorClientHttpConnector ;
72
+ import org .springframework .util .SocketUtils ;
76
73
import org .springframework .web .reactive .function .BodyExtractors ;
74
+ import org .springframework .web .reactive .function .client .WebClient .ResponseSpec ;
77
75
import org .springframework .web .testfixture .xml .Pojo ;
78
76
79
77
import static org .assertj .core .api .Assertions .assertThat ;
@@ -1220,63 +1218,9 @@ void invalidDomain(ClientHttpConnector connector) {
1220
1218
.verify ();
1221
1219
}
1222
1220
1223
- static Stream <Arguments > socketFaultArguments () {
1224
- Stream .Builder <Arguments > argumentsBuilder = Stream .builder ();
1225
- arguments ().forEach (arg -> {
1226
- argumentsBuilder .accept (Arguments .of (arg , SocketPolicy .DISCONNECT_AT_START ));
1227
- argumentsBuilder .accept (Arguments .of (arg , SocketPolicy .DISCONNECT_DURING_REQUEST_BODY ));
1228
- argumentsBuilder .accept (Arguments .of (arg , SocketPolicy .DISCONNECT_AFTER_REQUEST ));
1229
- });
1230
- return argumentsBuilder .build ();
1231
- }
1232
-
1233
- @ ParameterizedTest (name = "[{index}] {displayName} [{0}, {1}]" )
1234
- @ MethodSource ("socketFaultArguments" )
1235
- void prematureClosureFault (ClientHttpConnector connector , SocketPolicy socketPolicy ) {
1236
- startServer (connector );
1237
-
1238
- prepareResponse (response -> response
1239
- .setSocketPolicy (socketPolicy )
1240
- .setStatus ("HTTP/1.1 200 OK" )
1241
- .setHeader ("Response-Header-1" , "value 1" )
1242
- .setHeader ("Response-Header-2" , "value 2" )
1243
- .setBody ("{\" message\" : \" Hello, World!\" }" ));
1244
-
1245
- String uri = "/test" ;
1246
- Mono <String > result = this .webClient
1247
- .post ()
1248
- .uri (uri )
1249
- // Random non-empty body to allow us to interrupt.
1250
- .bodyValue ("{\" action\" : \" Say hello!\" }" )
1251
- .retrieve ()
1252
- .bodyToMono (String .class );
1253
-
1254
- StepVerifier .create (result )
1255
- .expectErrorSatisfies (throwable -> {
1256
- assertThat (throwable ).isInstanceOf (WebClientRequestException .class );
1257
- WebClientRequestException ex = (WebClientRequestException ) throwable ;
1258
- // Varies between connector providers.
1259
- assertThat (ex .getCause ()).isInstanceOf (IOException .class );
1260
- })
1261
- .verify ();
1262
- }
1263
-
1264
- static Stream <Arguments > malformedResponseChunkArguments () {
1265
- return Stream .of (
1266
- Arguments .of (new ReactorClientHttpConnector (), true ),
1267
- Arguments .of (new JettyClientHttpConnector (), true ),
1268
- // Apache injects the Transfer-Encoding header for us, and complains with an exception if we also
1269
- // add it. The other two connectors do not add the header at all. We need this header for the test
1270
- // case to work correctly.
1271
- Arguments .of (new HttpComponentsClientHttpConnector (), false )
1272
- );
1273
- }
1274
-
1275
- @ ParameterizedTest (name = "[{index}] {displayName} [{0}, {1}]" )
1276
- @ MethodSource ("malformedResponseChunkArguments" )
1277
- void malformedResponseChunksOnBodilessEntity (ClientHttpConnector connector , boolean addTransferEncodingHeader ) {
1278
- Mono <?> result = doMalformedResponseChunks (connector , addTransferEncodingHeader , ResponseSpec ::toBodilessEntity );
1279
-
1221
+ @ ParameterizedWebClientTest
1222
+ void malformedResponseChunksOnBodilessEntity (ClientHttpConnector connector ) {
1223
+ Mono <?> result = doMalformedChunkedResponseTest (connector , ResponseSpec ::toBodilessEntity );
1280
1224
StepVerifier .create (result )
1281
1225
.expectErrorSatisfies (throwable -> {
1282
1226
assertThat (throwable ).isInstanceOf (WebClientException .class );
@@ -1286,11 +1230,9 @@ void malformedResponseChunksOnBodilessEntity(ClientHttpConnector connector, bool
1286
1230
.verify ();
1287
1231
}
1288
1232
1289
- @ ParameterizedTest (name = "[{index}] {displayName} [{0}, {1}]" )
1290
- @ MethodSource ("malformedResponseChunkArguments" )
1291
- void malformedResponseChunksOnEntityWithBody (ClientHttpConnector connector , boolean addTransferEncodingHeader ) {
1292
- Mono <?> result = doMalformedResponseChunks (connector , addTransferEncodingHeader , spec -> spec .toEntity (String .class ));
1293
-
1233
+ @ ParameterizedWebClientTest
1234
+ void malformedResponseChunksOnEntityWithBody (ClientHttpConnector connector ) {
1235
+ Mono <?> result = doMalformedChunkedResponseTest (connector , spec -> spec .toEntity (String .class ));
1294
1236
StepVerifier .create (result )
1295
1237
.expectErrorSatisfies (throwable -> {
1296
1238
assertThat (throwable ).isInstanceOf (WebClientException .class );
@@ -1300,17 +1242,13 @@ void malformedResponseChunksOnEntityWithBody(ClientHttpConnector connector, bool
1300
1242
.verify ();
1301
1243
}
1302
1244
1303
- private <T > Mono <T > doMalformedResponseChunks (
1304
- ClientHttpConnector connector ,
1305
- boolean addTransferEncodingHeader ,
1306
- Function <ResponseSpec , Mono <T >> responseHandler
1307
- ) {
1245
+ private <T > Mono <T > doMalformedChunkedResponseTest (
1246
+ ClientHttpConnector connector , Function <ResponseSpec , Mono <T >> handler ) {
1247
+
1308
1248
int port = SocketUtils .findAvailableTcpPort ();
1309
1249
1310
1250
Thread serverThread = new Thread (() -> {
1311
- // This exists separately to the main mock server, as I had a really hard time getting that to send the
1312
- // chunked responses correctly, flushing the socket each time. This was the only way I was able to replicate
1313
- // the issue of the client not handling malformed response chunks correctly.
1251
+ // No way to simulate a malformed chunked response through MockWebServer.
1314
1252
try (ServerSocket serverSocket = new ServerSocket (port )) {
1315
1253
Socket socket = serverSocket .accept ();
1316
1254
InputStream is = socket .getInputStream ();
@@ -1324,30 +1262,20 @@ private <T> Mono<T> doMalformedResponseChunks(
1324
1262
os .write ("\r \n " .getBytes (StandardCharsets .UTF_8 ));
1325
1263
os .write ("lskdu018973t09sylgasjkfg1][]'./.sdlv" .getBytes (StandardCharsets .UTF_8 ));
1326
1264
socket .close ();
1327
- } catch (IOException ex ) {
1265
+ }
1266
+ catch (IOException ex ) {
1328
1267
throw new RuntimeException (ex );
1329
1268
}
1330
1269
});
1331
1270
1332
- serverThread .setDaemon (true );
1333
1271
serverThread .start ();
1334
1272
1335
- ResponseSpec spec = WebClient
1336
- .builder ()
1273
+ WebClient client = WebClient .builder ()
1337
1274
.clientConnector (connector )
1338
1275
.baseUrl ("http://localhost:" + port )
1339
- .build ()
1340
- .post ()
1341
- .headers (headers -> {
1342
- if (addTransferEncodingHeader ) {
1343
- headers .add (HttpHeaders .TRANSFER_ENCODING , "chunked" );
1344
- }
1345
- })
1346
- .retrieve ();
1276
+ .build ();
1347
1277
1348
- return responseHandler
1349
- .apply (spec )
1350
- .doFinally (signal -> serverThread .stop ());
1278
+ return handler .apply (client .post ().retrieve ());
1351
1279
}
1352
1280
1353
1281
private void prepareResponse (Consumer <MockResponse > consumer ) {
0 commit comments