|
6 | 6 | import java.net.InetAddress;
|
7 | 7 | import java.net.InetSocketAddress;
|
8 | 8 | import java.net.SocketAddress;
|
| 9 | +import java.net.SocketTimeoutException; |
9 | 10 | import java.security.Security;
|
| 11 | +import java.util.concurrent.TimeUnit; |
10 | 12 |
|
11 | 13 | import javax.net.ssl.SSLEngine;
|
12 | 14 | import javax.net.ssl.SSLParameters;
|
|
23 | 25 | import io.netty.channel.Channel;
|
24 | 26 | import io.netty.channel.ChannelConfig;
|
25 | 27 | import io.netty.channel.ChannelFactory;
|
| 28 | +import io.netty.channel.ChannelHandlerContext; |
26 | 29 | import io.netty.channel.ChannelInitializer;
|
27 | 30 | import io.netty.channel.EventLoopGroup;
|
28 | 31 | import io.netty.channel.epoll.EpollDomainSocketChannel;
|
|
38 | 41 | import io.netty.handler.codec.http.HttpClientCodec;
|
39 | 42 | import io.netty.handler.logging.LoggingHandler;
|
40 | 43 | import io.netty.handler.ssl.SslHandler;
|
| 44 | +import io.netty.handler.timeout.IdleState; |
| 45 | +import io.netty.handler.timeout.IdleStateEvent; |
| 46 | +import io.netty.handler.timeout.IdleStateHandler; |
41 | 47 | import io.netty.util.concurrent.DefaultThreadFactory;
|
42 | 48 | import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
43 | 49 |
|
@@ -84,6 +90,8 @@ public DuplexChannel getChannel() {
|
84 | 90 |
|
85 | 91 | private Integer connectTimeout = null;
|
86 | 92 |
|
| 93 | + private Integer readTimeout = null; |
| 94 | + |
87 | 95 | @Override
|
88 | 96 | public void init(DockerClientConfig dockerClientConfig) {
|
89 | 97 | super.init(dockerClientConfig);
|
@@ -273,16 +281,54 @@ public NettyDockerCmdExecFactory withConnectTimeout(Integer connectTimeout) {
|
273 | 281 | return this;
|
274 | 282 | }
|
275 | 283 |
|
| 284 | + /** |
| 285 | + * Configure read timeout in milliseconds |
| 286 | + */ |
| 287 | + public NettyDockerCmdExecFactory withReadTimeout(Integer readTimeout) { |
| 288 | + this.readTimeout = readTimeout; |
| 289 | + return this; |
| 290 | + } |
| 291 | + |
276 | 292 | private <T extends Channel> T configure(T channel) {
|
277 | 293 | ChannelConfig channelConfig = channel.config();
|
278 | 294 |
|
279 | 295 | if (connectTimeout != null) {
|
280 | 296 | channelConfig.setConnectTimeoutMillis(connectTimeout);
|
281 | 297 | }
|
| 298 | + if (readTimeout != null) { |
| 299 | + channel.pipeline().addLast("readTimeoutHandler", new ReadTimeoutHandler()); |
| 300 | + } |
282 | 301 |
|
283 | 302 | return channel;
|
284 | 303 | }
|
285 | 304 |
|
| 305 | + private final class ReadTimeoutHandler extends IdleStateHandler { |
| 306 | + private boolean alreadyTimedOut; |
| 307 | + |
| 308 | + ReadTimeoutHandler() { |
| 309 | + super(readTimeout, 0, 0, TimeUnit.MILLISECONDS); |
| 310 | + } |
| 311 | + |
| 312 | + /** |
| 313 | + * Called when a read timeout was detected. |
| 314 | + */ |
| 315 | + @Override |
| 316 | + protected synchronized void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception { |
| 317 | + assert evt.state() == IdleState.READER_IDLE; |
| 318 | + final Channel channel = ctx.channel(); |
| 319 | + if (channel == null || !channel.isActive() || alreadyTimedOut) { |
| 320 | + return; |
| 321 | + } |
| 322 | + final Object dockerAPIEndpoint = dockerClientConfig.getDockerHost(); |
| 323 | + final String msg = "Read timed out: No data received within " + readTimeout |
| 324 | + + "ms. Perhaps the docker API (" + dockerAPIEndpoint |
| 325 | + + ") is not responding normally, or perhaps you need to increase the readTimeout value."; |
| 326 | + final Exception ex = new SocketTimeoutException(msg); |
| 327 | + ctx.fireExceptionCaught(ex); |
| 328 | + alreadyTimedOut = true; |
| 329 | + } |
| 330 | + } |
| 331 | + |
286 | 332 | protected WebTarget getBaseResource() {
|
287 | 333 | checkNotNull(baseResource, "Factory not initialized, baseResource not set. You probably forgot to call init()!");
|
288 | 334 | return baseResource;
|
|
0 commit comments