Commit 27f74279 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

net/http: simplify transfer body; reduces allocations too

benchmark                                   old ns/op    new ns/op    delta
BenchmarkServerFakeConnNoKeepAlive              14431        14247   -1.28%
BenchmarkServerFakeConnWithKeepAlive            11618        11357   -2.25%
BenchmarkServerFakeConnWithKeepAliveLite         6735         6427   -4.57%
BenchmarkServerHandlerTypeLen                    8842         8740   -1.15%
BenchmarkServerHandlerNoLen                      8001         7828   -2.16%
BenchmarkServerHandlerNoType                     8270         8227   -0.52%
BenchmarkServerHandlerNoHeader                   6148         5920   -3.71%

benchmark                                  old allocs   new allocs    delta
BenchmarkServerFakeConnNoKeepAlive                 30           29   -3.33%
BenchmarkServerFakeConnWithKeepAlive               25           24   -4.00%
BenchmarkServerFakeConnWithKeepAliveLite           10            9  -10.00%
BenchmarkServerHandlerTypeLen                      18           17   -5.56%
BenchmarkServerHandlerNoLen                        15           14   -6.67%
BenchmarkServerHandlerNoType                       16           15   -6.25%
BenchmarkServerHandlerNoHeader                     10            9  -10.00%

benchmark                                   old bytes    new bytes    delta
BenchmarkServerFakeConnNoKeepAlive               2557         2492   -2.54%
BenchmarkServerFakeConnWithKeepAlive             2260         2194   -2.92%
BenchmarkServerFakeConnWithKeepAliveLite         1092         1026   -6.04%
BenchmarkServerHandlerTypeLen                    1941         1875   -3.40%
BenchmarkServerHandlerNoLen                      1898         1832   -3.48%
BenchmarkServerHandlerNoType                     1906         1840   -3.46%
BenchmarkServerHandlerNoHeader                   1092         1026   -6.04%

Update #5195

R=golang-dev, daniel.morsing
CC=golang-dev
https://golang.org/cl/9492044
parent b98a4d1a
...@@ -1814,7 +1814,15 @@ func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) { ...@@ -1814,7 +1814,15 @@ func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) {
} }
// eofReader is a non-nil io.ReadCloser that always returns EOF. // eofReader is a non-nil io.ReadCloser that always returns EOF.
var eofReader = ioutil.NopCloser(strings.NewReader("")) // It embeds a *strings.Reader so it still has a WriteTo method
// and io.Copy won't need a buffer.
var eofReader = &struct {
*strings.Reader
io.Closer
}{
strings.NewReader(""),
ioutil.NopCloser(nil),
}
// initNPNRequest is an HTTP handler that initializes certain // initNPNRequest is an HTTP handler that initializes certain
// uninitialized fields in its *Request. Such partially-initialized // uninitialized fields in its *Request. Such partially-initialized
......
...@@ -328,12 +328,12 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { ...@@ -328,12 +328,12 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
switch { switch {
case chunked(t.TransferEncoding): case chunked(t.TransferEncoding):
if noBodyExpected(t.RequestMethod) { if noBodyExpected(t.RequestMethod) {
t.Body = &body{Reader: eofReader, closing: t.Close} t.Body = eofReader
} else { } else {
t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close} t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
} }
case realLength == 0: case realLength == 0:
t.Body = &body{Reader: eofReader, closing: t.Close} t.Body = eofReader
case realLength > 0: case realLength > 0:
t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close} t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close}
default: default:
...@@ -343,7 +343,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { ...@@ -343,7 +343,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
t.Body = &body{Reader: r, closing: t.Close} t.Body = &body{Reader: r, closing: t.Close}
} else { } else {
// Persistent connection (i.e. HTTP/1.1) // Persistent connection (i.e. HTTP/1.1)
t.Body = &body{Reader: eofReader, closing: t.Close} t.Body = eofReader
} }
} }
...@@ -518,8 +518,6 @@ type body struct { ...@@ -518,8 +518,6 @@ type body struct {
r *bufio.Reader // underlying wire-format reader for the trailer r *bufio.Reader // underlying wire-format reader for the trailer
closing bool // is the connection to be closed after reading body? closing bool // is the connection to be closed after reading body?
closed bool closed bool
res *response // response writer for server requests, else nil
} }
// ErrBodyReadAfterClose is returned when reading a Request or Response // ErrBodyReadAfterClose is returned when reading a Request or Response
...@@ -618,14 +616,6 @@ func (b *body) Close() error { ...@@ -618,14 +616,6 @@ func (b *body) Close() error {
case b.hdr == nil && b.closing: case b.hdr == nil && b.closing:
// no trailer and closing the connection next. // no trailer and closing the connection next.
// no point in reading to EOF. // no point in reading to EOF.
case b.res != nil && b.res.requestBodyLimitHit:
// In a server request, don't continue reading from the client
// if we've already hit the maximum body size set by the
// handler. If this is set, that also means the TCP connection
// is about to be closed, so getting to the next HTTP request
// in the stream is not necessary.
case b.Reader == eofReader:
// Nothing to read. No need to io.Copy from it.
default: default:
// Fully consume the body, which will also lead to us reading // Fully consume the body, which will also lead to us reading
// the trailer headers after the body, if present. // the trailer headers after the body, if present.
......
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