]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: export Header.Clone, reduce its allocations, use it everywhere
authorBrad Fitzpatrick <bradfitz@golang.org>
Wed, 24 Apr 2019 14:59:18 +0000 (14:59 +0000)
committerBenny Siegert <bsiegert@gmail.com>
Wed, 24 Apr 2019 18:43:11 +0000 (18:43 +0000)
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>
src/net/http/client.go
src/net/http/header.go
src/net/http/httptest/recorder.go
src/net/http/httputil/reverseproxy.go
src/net/http/server.go

index aa54806c45a02ff6c9fc99c1f01716ae6afcbb28..6de1b48531826d3464e71c8abf3f7fcc41475d42 100644 (file)
@@ -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") != "" {
index b699e7ef8ffb13515d87665d1ea19ac875c9bdbd..1e1ed981ecc1961b626e2c0441f38ebbfdde07ca 100644 (file)
@@ -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
 }
index 59c98adfe84acec68e2c522bbb844a9894e96479..f2350f0a8d21691bbe31148d6bac6f9b6f0f7c55 100644 (file)
@@ -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",
index 0e0731b08f08c74b8b827bc0624d418496bbe9f6..3c522b2af4fde1c8754b071e30872cc85a5a2cdc 100644 (file)
@@ -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
index bc6d93bce096a3807d1be56f58f4b3fab11d3d54..722b709e85b301a39e320c4cd630514fa04fa96c 100644 (file)
@@ -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 != "" {