lib/readers: add NoCloser to stop upgrades from io.Reader to io.ReadCloser

This commit is contained in:
Nick Craig-Wood 2019-08-26 12:17:53 +01:00
parent 73e010aff9
commit ce3340621f
2 changed files with 73 additions and 0 deletions

29
lib/readers/noclose.go Normal file
View file

@ -0,0 +1,29 @@
package readers
import "io"
// noClose is used to wrap an io.Reader to stop it being upgraded
type noClose struct {
in io.Reader
}
// Read implements io.Closer by passing it straight on
func (nc noClose) Read(p []byte) (n int, err error) {
return nc.in.Read(p)
}
// NoCloser makes sure that the io.Reader passed in can't upgraded to
// an io.Closer.
//
// This is for use with http.NewRequest to make sure the body doesn't
// get upgraded to an io.Closer and the body closed unexpectedly.
func NoCloser(in io.Reader) io.Reader {
if in == nil {
return in
}
// if in doesn't implement io.Closer, just return it
if _, canClose := in.(io.Closer); !canClose {
return in
}
return noClose{in: in}
}

View file

@ -0,0 +1,44 @@
package readers
import (
"io"
"testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
var errRead = errors.New("read error")
type readOnly struct{}
func (readOnly) Read(p []byte) (n int, err error) {
return 0, io.EOF
}
type readClose struct{}
func (readClose) Read(p []byte) (n int, err error) {
return 0, errRead
}
func (readClose) Close() (err error) {
return io.EOF
}
func TestNoCloser(t *testing.T) {
assert.Equal(t, nil, NoCloser(nil))
ro := readOnly{}
assert.Equal(t, ro, NoCloser(ro))
rc := readClose{}
nc := NoCloser(rc)
assert.NotEqual(t, nc, rc)
_, hasClose := nc.(io.Closer)
assert.False(t, hasClose)
_, err := nc.Read(nil)
assert.Equal(t, errRead, err)
}