diff --git a/distribution/registry.go b/distribution/registry.go index da90c7b42e..4f1f0ff805 100644 --- a/distribution/registry.go +++ b/distribution/registry.go @@ -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