Skip to content

Commit 0537a74

Browse files
Jorropogopherbot
authored andcommittedMay 3, 2022
io: NopCloser forward WriterTo implementations if the reader supports it
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 golang#51566 Change-Id: I1943ee2c20d0fe749f4d04177342ce6eca443efe GitHub-Last-Rev: a6b9af4 GitHub-Pull-Request: golang#52340 Reviewed-on: https://go-review.googlesource.com/c/go/+/400236 Run-TryBot: Ian Lance Taylor <[email protected]> Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Benny Siegert <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]>
1 parent d0cda4d commit 0537a74

File tree

3 files changed

+55
-4
lines changed

3 files changed

+55
-4
lines changed
 

‎src/io/io.go

+15
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,12 @@ func (discard) ReadFrom(r Reader) (n int64, err error) {
621621

622622
// NopCloser returns a ReadCloser with a no-op Close method wrapping
623623
// the provided Reader r.
624+
// If r implements WriterTo, the returned ReadCloser will implement WriterTo
625+
// by forwarding calls to r.
624626
func NopCloser(r Reader) ReadCloser {
627+
if _, ok := r.(WriterTo); ok {
628+
return nopCloserWriterTo{r}
629+
}
625630
return nopCloser{r}
626631
}
627632

@@ -631,6 +636,16 @@ type nopCloser struct {
631636

632637
func (nopCloser) Close() error { return nil }
633638

639+
type nopCloserWriterTo struct {
640+
Reader
641+
}
642+
643+
func (nopCloserWriterTo) Close() error { return nil }
644+
645+
func (c nopCloserWriterTo) WriteTo(w Writer) (n int64, err error) {
646+
return c.Reader.(WriterTo).WriteTo(w)
647+
}
648+
634649
// ReadAll reads from r until an error or EOF and returns the data it read.
635650
// A successful call returns err == nil, not err == EOF. Because ReadAll is
636651
// defined to read from src until EOF, it does not treat an EOF from Read

‎src/io/io_test.go

+21
Original file line numberDiff line numberDiff line change
@@ -471,3 +471,24 @@ func TestCopyLargeWriter(t *testing.T) {
471471
t.Errorf("Copy error: got %v, want %v", err, want)
472472
}
473473
}
474+
475+
func TestNopCloserWriterToForwarding(t *testing.T) {
476+
for _, tc := range [...]struct {
477+
Name string
478+
r Reader
479+
}{
480+
{"not a WriterTo", Reader(nil)},
481+
{"a WriterTo", struct {
482+
Reader
483+
WriterTo
484+
}{}},
485+
} {
486+
nc := NopCloser(tc.r)
487+
488+
_, expected := tc.r.(WriterTo)
489+
_, got := nc.(WriterTo)
490+
if expected != got {
491+
t.Errorf("NopCloser incorrectly forwards WriterTo for %s, got %t want %t", tc.Name, got, expected)
492+
}
493+
}
494+
}

‎src/net/http/transfer.go

+19-4
Original file line numberDiff line numberDiff line change
@@ -422,8 +422,8 @@ func (t *transferWriter) doBodyCopy(dst io.Writer, src io.Reader) (n int64, err
422422
//
423423
// This function is only intended for use in writeBody.
424424
func (t *transferWriter) unwrapBody() io.Reader {
425-
if reflect.TypeOf(t.Body) == nopCloserType {
426-
return reflect.ValueOf(t.Body).Field(0).Interface().(io.Reader)
425+
if r, ok := unwrapNopCloser(t.Body); ok {
426+
return r
427427
}
428428
if r, ok := t.Body.(*readTrackingBody); ok {
429429
r.didRead = true
@@ -1081,6 +1081,21 @@ func (fr finishAsyncByteRead) Read(p []byte) (n int, err error) {
10811081
}
10821082

10831083
var nopCloserType = reflect.TypeOf(io.NopCloser(nil))
1084+
var nopCloserWriterToType = reflect.TypeOf(io.NopCloser(struct {
1085+
io.Reader
1086+
io.WriterTo
1087+
}{}))
1088+
1089+
// unwrapNopCloser return the underlying reader and true if r is a NopCloser
1090+
// else it return false
1091+
func unwrapNopCloser(r io.Reader) (underlyingReader io.Reader, isNopCloser bool) {
1092+
switch reflect.TypeOf(r) {
1093+
case nopCloserType, nopCloserWriterToType:
1094+
return reflect.ValueOf(r).Field(0).Interface().(io.Reader), true
1095+
default:
1096+
return nil, false
1097+
}
1098+
}
10841099

10851100
// isKnownInMemoryReader reports whether r is a type known to not
10861101
// block on Read. Its caller uses this as an optional optimization to
@@ -1090,8 +1105,8 @@ func isKnownInMemoryReader(r io.Reader) bool {
10901105
case *bytes.Reader, *bytes.Buffer, *strings.Reader:
10911106
return true
10921107
}
1093-
if reflect.TypeOf(r) == nopCloserType {
1094-
return isKnownInMemoryReader(reflect.ValueOf(r).Field(0).Interface().(io.Reader))
1108+
if r, ok := unwrapNopCloser(r); ok {
1109+
return isKnownInMemoryReader(r)
10951110
}
10961111
if r, ok := r.(*readTrackingBody); ok {
10971112
return isKnownInMemoryReader(r.ReadCloser)

0 commit comments

Comments
 (0)
Please sign in to comment.