net/http: export Header.Clone, reduce its allocations, use it everywhere

Fixes #29915

Change-Id: I6e6edf4f9a0e062211f74d120ae1a242bce1b274
Reviewed-on: https://go-review.googlesource.com/c/go/+/173658
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ingo Oeser <nightlyone@googlemail.com>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
diff --git a/src/net/http/client.go b/src/net/http/client.go
index aa54806..6de1b48 100644
--- a/src/net/http/client.go
+++ b/src/net/http/client.go
@@ -238,7 +238,7 @@
 		username := u.Username()
 		password, _ := u.Password()
 		forkReq()
-		req.Header = ireq.Header.clone()
+		req.Header = ireq.Header.Clone()
 		req.Header.Set("Authorization", "Basic "+basicAuth(username, password))
 	}
 
@@ -668,7 +668,7 @@
 	// The headers to copy are from the very initial request.
 	// We use a closured callback to keep a reference to these original headers.
 	var (
-		ireqhdr  = ireq.Header.clone()
+		ireqhdr  = ireq.Header.Clone()
 		icookies map[string][]*Cookie
 	)
 	if c.Jar != nil && ireq.Header.Get("Cookie") != "" {
diff --git a/src/net/http/header.go b/src/net/http/header.go
index b699e7e..1e1ed98 100644
--- a/src/net/http/header.go
+++ b/src/net/http/header.go
@@ -78,12 +78,19 @@
 	return h.writeSubset(w, nil, trace)
 }
 
-func (h Header) clone() Header {
+// Clone returns a copy of h.
+func (h Header) Clone() Header {
+	// Find total number of values.
+	nv := 0
+	for _, vv := range h {
+		nv += len(vv)
+	}
+	sv := make([]string, nv) // shared backing array for headers' values
 	h2 := make(Header, len(h))
 	for k, vv := range h {
-		vv2 := make([]string, len(vv))
-		copy(vv2, vv)
-		h2[k] = vv2
+		n := copy(sv, vv)
+		h2[k] = sv[:n:n]
+		sv = sv[n:]
 	}
 	return h2
 }
diff --git a/src/net/http/httptest/recorder.go b/src/net/http/httptest/recorder.go
index 59c98ad..f2350f0 100644
--- a/src/net/http/httptest/recorder.go
+++ b/src/net/http/httptest/recorder.go
@@ -127,17 +127,7 @@
 	if rw.HeaderMap == nil {
 		rw.HeaderMap = make(http.Header)
 	}
-	rw.snapHeader = cloneHeader(rw.HeaderMap)
-}
-
-func cloneHeader(h http.Header) http.Header {
-	h2 := make(http.Header, len(h))
-	for k, vv := range h {
-		vv2 := make([]string, len(vv))
-		copy(vv2, vv)
-		h2[k] = vv2
-	}
-	return h2
+	rw.snapHeader = rw.HeaderMap.Clone()
 }
 
 // Flush sets rw.Flushed to true.
@@ -168,7 +158,7 @@
 		return rw.result
 	}
 	if rw.snapHeader == nil {
-		rw.snapHeader = cloneHeader(rw.HeaderMap)
+		rw.snapHeader = rw.HeaderMap.Clone()
 	}
 	res := &http.Response{
 		Proto:      "HTTP/1.1",
diff --git a/src/net/http/httputil/reverseproxy.go b/src/net/http/httputil/reverseproxy.go
index 0e0731b..3c522b2 100644
--- a/src/net/http/httputil/reverseproxy.go
+++ b/src/net/http/httputil/reverseproxy.go
@@ -132,16 +132,6 @@
 	}
 }
 
-func cloneHeader(h http.Header) http.Header {
-	h2 := make(http.Header, len(h))
-	for k, vv := range h {
-		vv2 := make([]string, len(vv))
-		copy(vv2, vv)
-		h2[k] = vv2
-	}
-	return h2
-}
-
 // Hop-by-hop headers. These are removed when sent to the backend.
 // As of RFC 7230, hop-by-hop headers are required to appear in the
 // Connection header field. These are the headers defined by the
@@ -211,7 +201,7 @@
 		outreq.Body = nil // Issue 16036: nil Body for http.Transport retries
 	}
 
-	outreq.Header = cloneHeader(req.Header)
+	outreq.Header = req.Header.Clone()
 
 	p.Director(outreq)
 	outreq.Close = false
diff --git a/src/net/http/server.go b/src/net/http/server.go
index bc6d93b..722b709 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -1058,7 +1058,7 @@
 		// Accessing the header between logically writing it
 		// and physically writing it means we need to allocate
 		// a clone to snapshot the logically written state.
-		w.cw.header = w.handlerHeader.clone()
+		w.cw.header = w.handlerHeader.Clone()
 	}
 	w.calledHeader = true
 	return w.handlerHeader
@@ -1127,7 +1127,7 @@
 	w.status = code
 
 	if w.calledHeader && w.cw.header == nil {
-		w.cw.header = w.handlerHeader.clone()
+		w.cw.header = w.handlerHeader.Clone()
 	}
 
 	if cl := w.handlerHeader.get("Content-Length"); cl != "" {