Commit 13d0af4e authored by Brad Fitzpatrick's avatar Brad Fitzpatrick Committed by Benny Siegert

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: default avatarIngo Oeser <nightlyone@googlemail.com>
Reviewed-by: default avatarBenny Siegert <bsiegert@gmail.com>
parent 2417b0d0
...@@ -238,7 +238,7 @@ func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, d ...@@ -238,7 +238,7 @@ func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, d
username := u.Username() username := u.Username()
password, _ := u.Password() password, _ := u.Password()
forkReq() forkReq()
req.Header = ireq.Header.clone() req.Header = ireq.Header.Clone()
req.Header.Set("Authorization", "Basic "+basicAuth(username, password)) req.Header.Set("Authorization", "Basic "+basicAuth(username, password))
} }
...@@ -668,7 +668,7 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) { ...@@ -668,7 +668,7 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
// The headers to copy are from the very initial request. // The headers to copy are from the very initial request.
// We use a closured callback to keep a reference to these original headers. // We use a closured callback to keep a reference to these original headers.
var ( var (
ireqhdr = ireq.Header.clone() ireqhdr = ireq.Header.Clone()
icookies map[string][]*Cookie icookies map[string][]*Cookie
) )
if c.Jar != nil && ireq.Header.Get("Cookie") != "" { if c.Jar != nil && ireq.Header.Get("Cookie") != "" {
......
...@@ -78,12 +78,19 @@ func (h Header) write(w io.Writer, trace *httptrace.ClientTrace) error { ...@@ -78,12 +78,19 @@ func (h Header) write(w io.Writer, trace *httptrace.ClientTrace) error {
return h.writeSubset(w, nil, trace) 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)) h2 := make(Header, len(h))
for k, vv := range h { for k, vv := range h {
vv2 := make([]string, len(vv)) n := copy(sv, vv)
copy(vv2, vv) h2[k] = sv[:n:n]
h2[k] = vv2 sv = sv[n:]
} }
return h2 return h2
} }
......
...@@ -127,17 +127,7 @@ func (rw *ResponseRecorder) WriteHeader(code int) { ...@@ -127,17 +127,7 @@ func (rw *ResponseRecorder) WriteHeader(code int) {
if rw.HeaderMap == nil { if rw.HeaderMap == nil {
rw.HeaderMap = make(http.Header) rw.HeaderMap = make(http.Header)
} }
rw.snapHeader = cloneHeader(rw.HeaderMap) rw.snapHeader = rw.HeaderMap.Clone()
}
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
} }
// Flush sets rw.Flushed to true. // Flush sets rw.Flushed to true.
...@@ -168,7 +158,7 @@ func (rw *ResponseRecorder) Result() *http.Response { ...@@ -168,7 +158,7 @@ func (rw *ResponseRecorder) Result() *http.Response {
return rw.result return rw.result
} }
if rw.snapHeader == nil { if rw.snapHeader == nil {
rw.snapHeader = cloneHeader(rw.HeaderMap) rw.snapHeader = rw.HeaderMap.Clone()
} }
res := &http.Response{ res := &http.Response{
Proto: "HTTP/1.1", Proto: "HTTP/1.1",
......
...@@ -132,16 +132,6 @@ func copyHeader(dst, src http.Header) { ...@@ -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. // 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 // As of RFC 7230, hop-by-hop headers are required to appear in the
// Connection header field. These are the headers defined by 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) { ...@@ -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.Body = nil // Issue 16036: nil Body for http.Transport retries
} }
outreq.Header = cloneHeader(req.Header) outreq.Header = req.Header.Clone()
p.Director(outreq) p.Director(outreq)
outreq.Close = false outreq.Close = false
......
...@@ -1058,7 +1058,7 @@ func (w *response) Header() Header { ...@@ -1058,7 +1058,7 @@ func (w *response) Header() Header {
// Accessing the header between logically writing it // Accessing the header between logically writing it
// and physically writing it means we need to allocate // and physically writing it means we need to allocate
// a clone to snapshot the logically written state. // a clone to snapshot the logically written state.
w.cw.header = w.handlerHeader.clone() w.cw.header = w.handlerHeader.Clone()
} }
w.calledHeader = true w.calledHeader = true
return w.handlerHeader return w.handlerHeader
...@@ -1127,7 +1127,7 @@ func (w *response) WriteHeader(code int) { ...@@ -1127,7 +1127,7 @@ func (w *response) WriteHeader(code int) {
w.status = code w.status = code
if w.calledHeader && w.cw.header == nil { 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 != "" { if cl := w.handlerHeader.get("Content-Length"); cl != "" {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment