aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2019-04-24 14:59:18 +0000
committerBenny Siegert <bsiegert@gmail.com>2019-04-24 18:43:11 +0000
commit13d0af4e704bee164f873701e326048bdaf23933 (patch)
treeb17ff70eae9526b1a8b8a6bdfda88e885057531f
parent2417b0d0067b192e7cca05c00d9874617607fa81 (diff)
downloadgo-13d0af4e704bee164f873701e326048bdaf23933.tar.gz
go-13d0af4e704bee164f873701e326048bdaf23933.zip
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>
-rw-r--r--src/net/http/client.go4
-rw-r--r--src/net/http/header.go15
-rw-r--r--src/net/http/httptest/recorder.go14
-rw-r--r--src/net/http/httputil/reverseproxy.go12
-rw-r--r--src/net/http/server.go4
5 files changed, 18 insertions, 31 deletions
diff --git a/src/net/http/client.go b/src/net/http/client.go
index aa54806c45..6de1b48531 100644
--- a/src/net/http/client.go
+++ b/src/net/http/client.go
@@ -238,7 +238,7 @@ func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, d
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 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
// 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 b699e7ef8f..1e1ed981ec 100644
--- a/src/net/http/header.go
+++ b/src/net/http/header.go
@@ -78,12 +78,19 @@ func (h Header) write(w io.Writer, trace *httptrace.ClientTrace) error {
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 59c98adfe8..f2350f0a8d 100644
--- a/src/net/http/httptest/recorder.go
+++ b/src/net/http/httptest/recorder.go
@@ -127,17 +127,7 @@ func (rw *ResponseRecorder) WriteHeader(code int) {
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 @@ func (rw *ResponseRecorder) Result() *http.Response {
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 0e0731b08f..3c522b2af4 100644
--- a/src/net/http/httputil/reverseproxy.go
+++ b/src/net/http/httputil/reverseproxy.go
@@ -132,16 +132,6 @@ func copyHeader(dst, src http.Header) {
}
}
-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 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
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 bc6d93bce0..722b709e85 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -1058,7 +1058,7 @@ func (w *response) Header() Header {
// 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 @@ func (w *response) WriteHeader(code int) {
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 != "" {