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>
diff --git a/distribution/registry.go b/distribution/registry.go
index 1c2b4f3..fd3d552 100644
--- a/distribution/registry.go
+++ b/distribution/registry.go
@@ -44,6 +44,30 @@
 	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.
@@ -57,11 +81,22 @@
 	// 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