diff options
Diffstat (limited to 'libgo/go/net/http/transfer.go')
-rw-r--r-- | libgo/go/net/http/transfer.go | 73 |
1 files changed, 52 insertions, 21 deletions
diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go index e8a93e9137e..2e01a07f84f 100644 --- a/libgo/go/net/http/transfer.go +++ b/libgo/go/net/http/transfer.go @@ -21,7 +21,7 @@ import ( "sync" "time" - "internal/x/net/http/httpguts" + "golang.org/x/net/http/httpguts" ) // ErrLineTooLong is returned when reading request or response bodies @@ -53,19 +53,6 @@ func (br *byteReader) Read(p []byte) (n int, err error) { return 1, io.EOF } -// transferBodyReader is an io.Reader that reads from tw.Body -// and records any non-EOF error in tw.bodyReadError. -// It is exactly 1 pointer wide to avoid allocations into interfaces. -type transferBodyReader struct{ tw *transferWriter } - -func (br transferBodyReader) Read(p []byte) (n int, err error) { - n, err = br.tw.Body.Read(p) - if err != nil && err != io.EOF { - br.tw.bodyReadError = err - } - return -} - // transferWriter inspects the fields of a user-supplied Request or Response, // sanitizes them without changing the user object and provides methods for // writing the respective header, body and trailer in wire format. @@ -347,15 +334,18 @@ func (t *transferWriter) writeBody(w io.Writer) error { var err error var ncopy int64 - // Write body + // Write body. We "unwrap" the body first if it was wrapped in a + // nopCloser. This is to ensure that we can take advantage of + // OS-level optimizations in the event that the body is an + // *os.File. if t.Body != nil { - var body = transferBodyReader{t} + var body = t.unwrapBody() if chunked(t.TransferEncoding) { if bw, ok := w.(*bufio.Writer); ok && !t.IsResponse { w = &internal.FlushAfterChunkWriter{Writer: bw} } cw := internal.NewChunkedWriter(w) - _, err = io.Copy(cw, body) + _, err = t.doBodyCopy(cw, body) if err == nil { err = cw.Close() } @@ -364,14 +354,14 @@ func (t *transferWriter) writeBody(w io.Writer) error { if t.Method == "CONNECT" { dst = bufioFlushWriter{dst} } - ncopy, err = io.Copy(dst, body) + ncopy, err = t.doBodyCopy(dst, body) } else { - ncopy, err = io.Copy(w, io.LimitReader(body, t.ContentLength)) + ncopy, err = t.doBodyCopy(w, io.LimitReader(body, t.ContentLength)) if err != nil { return err } var nextra int64 - nextra, err = io.Copy(ioutil.Discard, body) + nextra, err = t.doBodyCopy(ioutil.Discard, body) ncopy += nextra } if err != nil { @@ -402,6 +392,31 @@ func (t *transferWriter) writeBody(w io.Writer) error { return err } +// doBodyCopy wraps a copy operation, with any resulting error also +// being saved in bodyReadError. +// +// This function is only intended for use in writeBody. +func (t *transferWriter) doBodyCopy(dst io.Writer, src io.Reader) (n int64, err error) { + n, err = io.Copy(dst, src) + if err != nil && err != io.EOF { + t.bodyReadError = err + } + return +} + +// unwrapBodyReader unwraps the body's inner reader if it's a +// nopCloser. This is to ensure that body writes sourced from local +// files (*os.File types) are properly optimized. +// +// This function is only intended for use in writeBody. +func (t *transferWriter) unwrapBody() io.Reader { + if reflect.TypeOf(t.Body) == nopCloserType { + return reflect.ValueOf(t.Body).Field(0).Interface().(io.Reader) + } + + return t.Body +} + type transferReader struct { // Input Header Header @@ -574,6 +589,22 @@ func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" } // Checks whether the encoding is explicitly "identity". func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" } +// unsupportedTEError reports unsupported transfer-encodings. +type unsupportedTEError struct { + err string +} + +func (uste *unsupportedTEError) Error() string { + return uste.err +} + +// isUnsupportedTEError checks if the error is of type +// unsupportedTEError. It is usually invoked with a non-nil err. +func isUnsupportedTEError(err error) bool { + _, ok := err.(*unsupportedTEError) + return ok +} + // fixTransferEncoding sanitizes t.TransferEncoding, if needed. func (t *transferReader) fixTransferEncoding() error { raw, present := t.Header["Transfer-Encoding"] @@ -600,7 +631,7 @@ func (t *transferReader) fixTransferEncoding() error { break } if encoding != "chunked" { - return &badStringError{"unsupported transfer encoding", encoding} + return &unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", encoding)} } te = te[0 : len(te)+1] te[len(te)-1] = encoding |