]> Cypherpunks repositories - gostls13.git/commitdiff
io: NopCloser forward WriterTo implementations if the reader supports it
authorJorropo <jorropo.pgm@gmail.com>
Mon, 2 May 2022 11:50:36 +0000 (11:50 +0000)
committerGopher Robot <gobot@golang.org>
Tue, 3 May 2022 14:37:48 +0000 (14:37 +0000)
This patch also include related fixes to net/http.

io_test.go don't test reading or WritingTo of the because the logic is simple.
NopCloser didn't even had direct tests before.

Fixes #51566

Change-Id: I1943ee2c20d0fe749f4d04177342ce6eca443efe
GitHub-Last-Rev: a6b9af4e945a6903735a74aa185e2d1c4c2e2cef
GitHub-Pull-Request: golang/go#52340
Reviewed-on: https://go-review.googlesource.com/c/go/+/400236
Run-TryBot: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Benny Siegert <bsiegert@gmail.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>

src/io/io.go
src/io/io_test.go
src/net/http/transfer.go

index 1ea01d5d633df3d8f006c6bb3e961372058b979f..db88125f507799dafbb619e638ec846480784bf4 100644 (file)
@@ -621,7 +621,12 @@ func (discard) ReadFrom(r Reader) (n int64, err error) {
 
 // NopCloser returns a ReadCloser with a no-op Close method wrapping
 // the provided Reader r.
+// If r implements WriterTo, the returned ReadCloser will implement WriterTo
+// by forwarding calls to r.
 func NopCloser(r Reader) ReadCloser {
+       if _, ok := r.(WriterTo); ok {
+               return nopCloserWriterTo{r}
+       }
        return nopCloser{r}
 }
 
@@ -631,6 +636,16 @@ type nopCloser struct {
 
 func (nopCloser) Close() error { return nil }
 
+type nopCloserWriterTo struct {
+       Reader
+}
+
+func (nopCloserWriterTo) Close() error { return nil }
+
+func (c nopCloserWriterTo) WriteTo(w Writer) (n int64, err error) {
+       return c.Reader.(WriterTo).WriteTo(w)
+}
+
 // ReadAll reads from r until an error or EOF and returns the data it read.
 // A successful call returns err == nil, not err == EOF. Because ReadAll is
 // defined to read from src until EOF, it does not treat an EOF from Read
index 308846048095652c8974517dbac250a6278a2b88..a51a1fa160da3ec390a462914348578fb10112bf 100644 (file)
@@ -471,3 +471,24 @@ func TestCopyLargeWriter(t *testing.T) {
                t.Errorf("Copy error: got %v, want %v", err, want)
        }
 }
+
+func TestNopCloserWriterToForwarding(t *testing.T) {
+       for _, tc := range [...]struct {
+               Name string
+               r    Reader
+       }{
+               {"not a WriterTo", Reader(nil)},
+               {"a WriterTo", struct {
+                       Reader
+                       WriterTo
+               }{}},
+       } {
+               nc := NopCloser(tc.r)
+
+               _, expected := tc.r.(WriterTo)
+               _, got := nc.(WriterTo)
+               if expected != got {
+                       t.Errorf("NopCloser incorrectly forwards WriterTo for %s, got %t want %t", tc.Name, got, expected)
+               }
+       }
+}
index d9edf8c725947be3a24138c3c10b6c72366f7fe0..7bea5866f71c535e2c1ad446eff5c3288076d8cd 100644 (file)
@@ -422,8 +422,8 @@ func (t *transferWriter) doBodyCopy(dst io.Writer, src io.Reader) (n int64, err
 //
 // 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)
+       if r, ok := unwrapNopCloser(t.Body); ok {
+               return r
        }
        if r, ok := t.Body.(*readTrackingBody); ok {
                r.didRead = true
@@ -1081,6 +1081,21 @@ func (fr finishAsyncByteRead) Read(p []byte) (n int, err error) {
 }
 
 var nopCloserType = reflect.TypeOf(io.NopCloser(nil))
+var nopCloserWriterToType = reflect.TypeOf(io.NopCloser(struct {
+       io.Reader
+       io.WriterTo
+}{}))
+
+// unwrapNopCloser return the underlying reader and true if r is a NopCloser
+// else it return false
+func unwrapNopCloser(r io.Reader) (underlyingReader io.Reader, isNopCloser bool) {
+       switch reflect.TypeOf(r) {
+       case nopCloserType, nopCloserWriterToType:
+               return reflect.ValueOf(r).Field(0).Interface().(io.Reader), true
+       default:
+               return nil, false
+       }
+}
 
 // isKnownInMemoryReader reports whether r is a type known to not
 // block on Read. Its caller uses this as an optional optimization to
@@ -1090,8 +1105,8 @@ func isKnownInMemoryReader(r io.Reader) bool {
        case *bytes.Reader, *bytes.Buffer, *strings.Reader:
                return true
        }
-       if reflect.TypeOf(r) == nopCloserType {
-               return isKnownInMemoryReader(reflect.ValueOf(r).Field(0).Interface().(io.Reader))
+       if r, ok := unwrapNopCloser(r); ok {
+               return isKnownInMemoryReader(r)
        }
        if r, ok := r.(*readTrackingBody); ok {
                return isKnownInMemoryReader(r.ReadCloser)