Skip to content

Commit 13d0af4

Browse files
bradfitzbsiegert
authored andcommitted
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ingo Oeser <[email protected]> Reviewed-by: Benny Siegert <[email protected]>
1 parent 2417b0d commit 13d0af4

File tree

5 files changed

+18
-31
lines changed

5 files changed

+18
-31
lines changed

src/net/http/client.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, d
238238
username := u.Username()
239239
password, _ := u.Password()
240240
forkReq()
241-
req.Header = ireq.Header.clone()
241+
req.Header = ireq.Header.Clone()
242242
req.Header.Set("Authorization", "Basic "+basicAuth(username, password))
243243
}
244244

@@ -668,7 +668,7 @@ func (c *Client) makeHeadersCopier(ireq *Request) func(*Request) {
668668
// The headers to copy are from the very initial request.
669669
// We use a closured callback to keep a reference to these original headers.
670670
var (
671-
ireqhdr = ireq.Header.clone()
671+
ireqhdr = ireq.Header.Clone()
672672
icookies map[string][]*Cookie
673673
)
674674
if c.Jar != nil && ireq.Header.Get("Cookie") != "" {

src/net/http/header.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,19 @@ func (h Header) write(w io.Writer, trace *httptrace.ClientTrace) error {
7878
return h.writeSubset(w, nil, trace)
7979
}
8080

81-
func (h Header) clone() Header {
81+
// Clone returns a copy of h.
82+
func (h Header) Clone() Header {
83+
// Find total number of values.
84+
nv := 0
85+
for _, vv := range h {
86+
nv += len(vv)
87+
}
88+
sv := make([]string, nv) // shared backing array for headers' values
8289
h2 := make(Header, len(h))
8390
for k, vv := range h {
84-
vv2 := make([]string, len(vv))
85-
copy(vv2, vv)
86-
h2[k] = vv2
91+
n := copy(sv, vv)
92+
h2[k] = sv[:n:n]
93+
sv = sv[n:]
8794
}
8895
return h2
8996
}

src/net/http/httptest/recorder.go

+2-12
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,7 @@ func (rw *ResponseRecorder) WriteHeader(code int) {
127127
if rw.HeaderMap == nil {
128128
rw.HeaderMap = make(http.Header)
129129
}
130-
rw.snapHeader = cloneHeader(rw.HeaderMap)
131-
}
132-
133-
func cloneHeader(h http.Header) http.Header {
134-
h2 := make(http.Header, len(h))
135-
for k, vv := range h {
136-
vv2 := make([]string, len(vv))
137-
copy(vv2, vv)
138-
h2[k] = vv2
139-
}
140-
return h2
130+
rw.snapHeader = rw.HeaderMap.Clone()
141131
}
142132

143133
// Flush sets rw.Flushed to true.
@@ -168,7 +158,7 @@ func (rw *ResponseRecorder) Result() *http.Response {
168158
return rw.result
169159
}
170160
if rw.snapHeader == nil {
171-
rw.snapHeader = cloneHeader(rw.HeaderMap)
161+
rw.snapHeader = rw.HeaderMap.Clone()
172162
}
173163
res := &http.Response{
174164
Proto: "HTTP/1.1",

src/net/http/httputil/reverseproxy.go

+1-11
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,6 @@ func copyHeader(dst, src http.Header) {
132132
}
133133
}
134134

135-
func cloneHeader(h http.Header) http.Header {
136-
h2 := make(http.Header, len(h))
137-
for k, vv := range h {
138-
vv2 := make([]string, len(vv))
139-
copy(vv2, vv)
140-
h2[k] = vv2
141-
}
142-
return h2
143-
}
144-
145135
// Hop-by-hop headers. These are removed when sent to the backend.
146136
// As of RFC 7230, hop-by-hop headers are required to appear in the
147137
// Connection header field. These are the headers defined by the
@@ -211,7 +201,7 @@ func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
211201
outreq.Body = nil // Issue 16036: nil Body for http.Transport retries
212202
}
213203

214-
outreq.Header = cloneHeader(req.Header)
204+
outreq.Header = req.Header.Clone()
215205

216206
p.Director(outreq)
217207
outreq.Close = false

src/net/http/server.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ func (w *response) Header() Header {
10581058
// Accessing the header between logically writing it
10591059
// and physically writing it means we need to allocate
10601060
// a clone to snapshot the logically written state.
1061-
w.cw.header = w.handlerHeader.clone()
1061+
w.cw.header = w.handlerHeader.Clone()
10621062
}
10631063
w.calledHeader = true
10641064
return w.handlerHeader
@@ -1127,7 +1127,7 @@ func (w *response) WriteHeader(code int) {
11271127
w.status = code
11281128

11291129
if w.calledHeader && w.cw.header == nil {
1130-
w.cw.header = w.handlerHeader.clone()
1130+
w.cw.header = w.handlerHeader.Clone()
11311131
}
11321132

11331133
if cl := w.handlerHeader.get("Content-Length"); cl != "" {

0 commit comments

Comments
 (0)