@@ -141,7 +141,7 @@ private final boolean validateWebSocketRequest(Request request, AsyncHandler<?>
141
141
142
142
private Channel getCachedChannel (NettyResponseFuture <?> future , URI uri , ConnectionPoolKeyStrategy poolKeyGen , ProxyServer proxyServer ) {
143
143
144
- if (future != null && future .reuseChannel () && future .channel () != null ) {
144
+ if (future != null && future .reuseChannel () && isChannelValid ( future .channel ()) ) {
145
145
return future .channel ();
146
146
} else {
147
147
URI connectionKeyUri = proxyServer != null ? proxyServer .getURI () : uri ;
@@ -235,8 +235,8 @@ private <T> ListenableFuture<T> sendRequestWithNewChannel(Request request, URI u
235
235
return connectListener .future ();
236
236
}
237
237
238
- private <T > NettyResponseFuture <T > newFuture (final Request request , final AsyncHandler <T > asyncHandler , NettyResponseFuture <T > originalFuture , URI uri , ProxyServer proxy ,
239
- boolean forceConnect ) throws IOException {
238
+ private <T > NettyResponseFuture <T > newNettyRequestAndResponseFuture (final Request request , final AsyncHandler <T > asyncHandler , NettyResponseFuture <T > originalFuture , URI uri ,
239
+ ProxyServer proxy , boolean forceConnect ) throws IOException {
240
240
241
241
NettyRequest nettyRequest = NettyRequests .newNettyRequest (config , request , uri , forceConnect , proxy );
242
242
@@ -249,6 +249,51 @@ private <T> NettyResponseFuture<T> newFuture(final Request request, final AsyncH
249
249
}
250
250
}
251
251
252
+ private boolean isChannelValid (Channel channel ) {
253
+ return channel != null && channel .isOpen () && channel .isActive ();
254
+ }
255
+
256
+ private <T > ListenableFuture <T > sendRequestThroughSslProxy (Request request , AsyncHandler <T > asyncHandler , NettyResponseFuture <T > future , boolean reclaimCache , URI uri ,
257
+ ProxyServer proxyServer ) throws IOException {
258
+
259
+ // Using CONNECT depends on wither we can fetch a valid channel or not
260
+
261
+ // Loop until we get a valid channel from the pool and it's still valid once the request is built
262
+ NettyResponseFuture <T > newFuture = null ;
263
+ for (int i = 0 ; i < 3 ; i ++) {
264
+ Channel channel = getCachedChannel (future , uri , request .getConnectionPoolKeyStrategy (), proxyServer );
265
+ if (isChannelValid (channel )) {
266
+ if (newFuture == null )
267
+ newFuture = newNettyRequestAndResponseFuture (request , asyncHandler , future , uri , proxyServer , false );
268
+ else
269
+ // no need to recreate fully the future, just need to re-attach the new channel
270
+ newFuture .attachChannel (channel );
271
+ if (isChannelValid (channel ))
272
+ // if the channel is still active, we can use it, otherwise try gain
273
+ return sendRequestWithCachedChannel (channel , request , uri , proxyServer , newFuture , asyncHandler );
274
+ } else
275
+ // pool is empty
276
+ break ;
277
+ }
278
+
279
+ newFuture = newNettyRequestAndResponseFuture (request , asyncHandler , future , uri , proxyServer , true );
280
+ return sendRequestWithNewChannel (request , uri , proxyServer , newFuture , asyncHandler , reclaimCache );
281
+ }
282
+
283
+ private <T > ListenableFuture <T > sendRequestWithCertainForceConnect (Request request , AsyncHandler <T > asyncHandler , NettyResponseFuture <T > future , boolean reclaimCache , URI uri ,
284
+ ProxyServer proxyServer , boolean forceConnect ) throws IOException {
285
+ // We know for sure if we have to force to connect or not, so we can build the HttpRequest right away
286
+ // This reduces the probability of having a pooled channel closed by the server by the time we build the request
287
+ NettyResponseFuture <T > newFuture = newNettyRequestAndResponseFuture (request , asyncHandler , future , uri , proxyServer , forceConnect );
288
+
289
+ Channel channel = getCachedChannel (future , uri , request .getConnectionPoolKeyStrategy (), proxyServer );
290
+
291
+ if (isChannelValid (channel ))
292
+ return sendRequestWithCachedChannel (channel , request , uri , proxyServer , newFuture , asyncHandler );
293
+ else
294
+ return sendRequestWithNewChannel (request , uri , proxyServer , newFuture , asyncHandler , reclaimCache );
295
+ }
296
+
252
297
public <T > ListenableFuture <T > sendRequest (final Request request , final AsyncHandler <T > asyncHandler , NettyResponseFuture <T > future , boolean reclaimCache ) throws IOException {
253
298
254
299
if (closed .get ()) {
@@ -262,36 +307,16 @@ public <T> ListenableFuture<T> sendRequest(final Request request, final AsyncHan
262
307
263
308
URI uri = config .isUseRawUrl () ? request .getRawURI () : request .getURI ();
264
309
ProxyServer proxyServer = ProxyUtils .getProxyServer (config , request );
265
-
266
- boolean sslProxy = proxyServer != null && isSecure (uri );
267
-
268
- if (!sslProxy ) {
269
- // won't be forcing to CONNECT whatever how we get a connection, so we can build the HttpRequest right away
270
-
271
- // We first build the request, then try to get a connection from the pool.
272
- // This reduces the probability of having a pooled connection closed by the server by the time we build the request
273
- NettyResponseFuture <T > newFuture = newFuture (request , asyncHandler , future , uri , proxyServer , false );
274
-
275
- Channel channel = getCachedChannel (future , uri , request .getConnectionPoolKeyStrategy (), proxyServer );
276
-
277
- if (channel != null && channel .isOpen () && channel .isActive ())
278
- return sendRequestWithCachedChannel (channel , request , uri , proxyServer , newFuture , asyncHandler );
279
- else
280
- return sendRequestWithNewChannel (request , uri , proxyServer , newFuture , asyncHandler , reclaimCache );
281
-
282
- } else {
283
- // we have to determine wither we have to open a new connection or not before being able to build the HttpRequest
284
- Channel channel = getCachedChannel (future , uri , request .getConnectionPoolKeyStrategy (), proxyServer );
285
-
286
- if (channel != null && channel .isOpen () && channel .isActive ()) {
287
- NettyResponseFuture <T > newFuture = newFuture (request , asyncHandler , future , uri , proxyServer , future != null ? future .isConnectAllowed () : false );
288
- return sendRequestWithCachedChannel (channel , request , uri , proxyServer , newFuture , asyncHandler );
289
310
290
- } else {
291
- NettyResponseFuture <T > newFuture = newFuture (request , asyncHandler , future , uri , proxyServer , true );
292
- return sendRequestWithNewChannel (request , uri , proxyServer , newFuture , asyncHandler , reclaimCache );
293
- }
294
- }
311
+ if (proxyServer != null && isSecure (uri )) {
312
+ // SSL proxy, have to handle CONNECT
313
+ if (future != null && future .isConnectAllowed ())
314
+ // CONNECT forced
315
+ return sendRequestWithCertainForceConnect (request , asyncHandler , future , reclaimCache , uri , proxyServer , true );
316
+ else
317
+ return sendRequestThroughSslProxy (request , asyncHandler , future , reclaimCache , uri , proxyServer );
318
+ } else
319
+ return sendRequestWithCertainForceConnect (request , asyncHandler , future , reclaimCache , uri , proxyServer , false );
295
320
}
296
321
297
322
private void configureTransferAdapter (AsyncHandler <?> handler , HttpRequest httpRequest ) {
0 commit comments