Skip to content

Commit 6be607c

Browse files
committed
Add withReadTimeout(Integer) method to NettyDockerCmdExecFactory.
This enhances our Netty support so that, like JerseyDockerCmdExecFactory, we have a withReadTimeout(milliseconds) method that configures the factory to implement a read timeout. This allows clients to cope with recalcitrant docker daemons - if the daemon stops responding then the client code will get a SocketTimeoutException (much like it would if it was using Jersey instead of Netty) instead of just hanging forever.
1 parent 3a77f43 commit 6be607c

File tree

1 file changed

+46
-0
lines changed

1 file changed

+46
-0
lines changed

src/main/java/com/github/dockerjava/netty/NettyDockerCmdExecFactory.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import java.net.InetAddress;
77
import java.net.InetSocketAddress;
88
import java.net.SocketAddress;
9+
import java.net.SocketTimeoutException;
910
import java.security.Security;
11+
import java.util.concurrent.TimeUnit;
1012

1113
import javax.net.ssl.SSLEngine;
1214
import javax.net.ssl.SSLParameters;
@@ -153,6 +155,7 @@
153155
import io.netty.channel.Channel;
154156
import io.netty.channel.ChannelConfig;
155157
import io.netty.channel.ChannelFactory;
158+
import io.netty.channel.ChannelHandlerContext;
156159
import io.netty.channel.ChannelInitializer;
157160
import io.netty.channel.EventLoopGroup;
158161
import io.netty.channel.epoll.EpollDomainSocketChannel;
@@ -168,6 +171,9 @@
168171
import io.netty.handler.codec.http.HttpClientCodec;
169172
import io.netty.handler.logging.LoggingHandler;
170173
import io.netty.handler.ssl.SslHandler;
174+
import io.netty.handler.timeout.IdleState;
175+
import io.netty.handler.timeout.IdleStateEvent;
176+
import io.netty.handler.timeout.IdleStateHandler;
171177
import io.netty.util.concurrent.DefaultThreadFactory;
172178
import org.bouncycastle.jce.provider.BouncyCastleProvider;
173179

@@ -216,6 +222,8 @@ public DuplexChannel getChannel() {
216222

217223
private Integer connectTimeout = null;
218224

225+
private Integer readTimeout = null;
226+
219227
@Override
220228
public void init(DockerClientConfig dockerClientConfig) {
221229
checkNotNull(dockerClientConfig, "config was not specified");
@@ -744,16 +752,54 @@ public NettyDockerCmdExecFactory withConnectTimeout(Integer connectTimeout) {
744752
return this;
745753
}
746754

755+
/**
756+
* Configure read timeout in milliseconds
757+
*/
758+
public NettyDockerCmdExecFactory withReadTimeout(Integer readTimeout) {
759+
this.readTimeout = readTimeout;
760+
return this;
761+
}
762+
747763
private <T extends Channel> T configure(T channel) {
748764
ChannelConfig channelConfig = channel.config();
749765

750766
if (connectTimeout != null) {
751767
channelConfig.setConnectTimeoutMillis(connectTimeout);
752768
}
769+
if (readTimeout != null) {
770+
channel.pipeline().addLast("readTimeoutHandler", new ReadTimeoutHandler());
771+
}
753772

754773
return channel;
755774
}
756775

776+
private final class ReadTimeoutHandler extends IdleStateHandler {
777+
private boolean alreadyTimedOut;
778+
779+
ReadTimeoutHandler() {
780+
super(readTimeout, 0, 0, TimeUnit.MILLISECONDS);
781+
}
782+
783+
/**
784+
* Called when a read timeout was detected.
785+
*/
786+
@Override
787+
protected synchronized void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
788+
assert evt.state() == IdleState.READER_IDLE;
789+
final Channel channel = ctx.channel();
790+
if (channel == null || !channel.isActive() || alreadyTimedOut) {
791+
return;
792+
}
793+
final Object dockerAPIEndpoint = dockerClientConfig.getDockerHost();
794+
final String msg = "Read timed out: No data received within " + readTimeout
795+
+ "ms. Perhaps the docker API (" + dockerAPIEndpoint
796+
+ ") is not responding normally, or perhaps you need to increase the readTimeout value.";
797+
final Exception ex = new SocketTimeoutException(msg);
798+
ctx.fireExceptionCaught(ex);
799+
alreadyTimedOut = true;
800+
}
801+
}
802+
757803
protected WebTarget getBaseResource() {
758804
checkNotNull(baseResource, "Factory not initialized, baseResource not set. You probably forgot to call init()!");
759805
return baseResource;

0 commit comments

Comments
 (0)