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>
// 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}
}
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
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)
+ }
+ }
+}
//
// 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
}
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
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)