summaryrefslogtreecommitdiff
path: root/libgo/go/net/http/transfer.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/net/http/transfer.go')
-rw-r--r--libgo/go/net/http/transfer.go73
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