Set idle timeouts for HTTP reads and writes in communications with the registry

Otherwise, some operations can get stuck indefinitely when the remote
side is unresponsive.

Fixes #12823

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>
This commit is contained in:
Aaron Lehmann 2016-01-26 12:33:10 -08:00
parent b5420be597
commit 84b2162c1a
1 changed files with 40 additions and 5 deletions

View File

@ -45,6 +45,30 @@ func (dcs dumbCredentialStore) Basic(*url.URL) (string, string) {
return dcs.auth.Username, dcs.auth.Password
}
// conn wraps a net.Conn, and sets a deadline for every read
// and write operation.
type conn struct {
net.Conn
readTimeout time.Duration
writeTimeout time.Duration
}
func (c *conn) Read(b []byte) (int, error) {
err := c.Conn.SetReadDeadline(time.Now().Add(c.readTimeout))
if err != nil {
return 0, err
}
return c.Conn.Read(b)
}
func (c *conn) Write(b []byte) (int, error) {
err := c.Conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
if err != nil {
return 0, err
}
return c.Conn.Write(b)
}
// NewV2Repository returns a repository (v2 only). It creates a HTTP transport
// providing timeout settings and authentication support, and also verifies the
// remote API version.
@ -58,11 +82,22 @@ func NewV2Repository(ctx context.Context, repoInfo *registry.RepositoryInfo, end
// TODO(dmcgowan): Call close idle connections when complete, use keep alive
base := &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).Dial,
Dial: func(network, address string) (net.Conn, error) {
dialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}
netConn, err := dialer.Dial(network, address)
if err != nil {
return netConn, err
}
return &conn{
Conn: netConn,
readTimeout: time.Minute,
writeTimeout: time.Minute,
}, nil
},
TLSHandshakeTimeout: 10 * time.Second,
TLSClientConfig: endpoint.TLSConfig,
// TODO(dmcgowan): Call close idle connections when complete and use keep alive