mirror of
https://github.com/golang/go
synced 2024-11-24 09:50:17 -07:00
encoding/gob: follow documented io.EOF semantics
The docs say: If the input is at EOF, Decode returns io.EOF and does not modify e. However, the added test fails: --- FAIL: TestDecodePartial (0.00s) encoder_test.go:1263: 31/81: expected io.ErrUnexpectedEOF: EOF encoder_test.go:1263: 51/81: expected io.ErrUnexpectedEOF: EOF In particular, the decoder would return io.EOF after reading a valid message for a type specification, and then hit EOF before reading a data item message. Fix that by only allowing a Decode call to return io.EOF if the reader hits EOF immediately, without successfully reading any message. Otherwise, hitting EOF is an ErrUnexpectedEOF, like in other cases. Also fix a net/rpc test that, coincidentally, expected an io.EOF as an error when feeding bad non-zero data to a gob decoder. An io.ErrUnexpectedEOF is clearly better in that scenario. Fixes #48905. Change-Id: Ied6a0d8ac8377f89646319a18c0380c4f2b09b85 Reviewed-on: https://go-review.googlesource.com/c/go/+/354972 Trust: Daniel Martí <mvdan@mvdan.cc> Run-TryBot: Daniel Martí <mvdan@mvdan.cc> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
parent
7023535126
commit
d973bb107e
@ -138,9 +138,17 @@ func (dec *Decoder) nextUint() uint64 {
|
||||
// decoded. If this is an interface value, it can be ignored by
|
||||
// resetting that buffer.
|
||||
func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId {
|
||||
firstMessage := true
|
||||
for dec.err == nil {
|
||||
if dec.buf.Len() == 0 {
|
||||
if !dec.recvMessage() {
|
||||
// We can only return io.EOF if the input was empty.
|
||||
// If we read one or more type spec messages,
|
||||
// require a data item message to follow.
|
||||
// If we hit an EOF before that, then give ErrUnexpectedEOF.
|
||||
if !firstMessage && dec.err == io.EOF {
|
||||
dec.err = io.ErrUnexpectedEOF
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -166,6 +174,7 @@ func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId {
|
||||
}
|
||||
dec.nextUint()
|
||||
}
|
||||
firstMessage = false
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
@ -1202,3 +1202,66 @@ func TestMarshalFloatMap(t *testing.T) {
|
||||
t.Fatalf("\nEncode: %v\nDecode: %v", want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodePartial(t *testing.T) {
|
||||
type T struct {
|
||||
X []int
|
||||
Y string
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
t1 := T{X: []int{1, 2, 3}, Y: "foo"}
|
||||
t2 := T{X: []int{4, 5, 6}, Y: "bar"}
|
||||
enc := NewEncoder(&buf)
|
||||
|
||||
t1start := 0
|
||||
if err := enc.Encode(&t1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t2start := buf.Len()
|
||||
if err := enc.Encode(&t2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
data := buf.Bytes()
|
||||
for i := 0; i <= len(data); i++ {
|
||||
bufr := bytes.NewReader(data[:i])
|
||||
|
||||
// Decode both values, stopping at the first error.
|
||||
var t1b, t2b T
|
||||
dec := NewDecoder(bufr)
|
||||
var err error
|
||||
err = dec.Decode(&t1b)
|
||||
if err == nil {
|
||||
err = dec.Decode(&t2b)
|
||||
}
|
||||
|
||||
switch i {
|
||||
case t1start, t2start:
|
||||
// Either the first or the second Decode calls had zero input.
|
||||
if err != io.EOF {
|
||||
t.Errorf("%d/%d: expected io.EOF: %v", i, len(data), err)
|
||||
}
|
||||
case len(data):
|
||||
// We reached the end of the entire input.
|
||||
if err != nil {
|
||||
t.Errorf("%d/%d: unexpected error: %v", i, len(data), err)
|
||||
}
|
||||
if !reflect.DeepEqual(t1b, t1) {
|
||||
t.Fatalf("t1 value mismatch: got %v, want %v", t1b, t1)
|
||||
}
|
||||
if !reflect.DeepEqual(t2b, t2) {
|
||||
t.Fatalf("t2 value mismatch: got %v, want %v", t2b, t2)
|
||||
}
|
||||
default:
|
||||
// In between, we must see io.ErrUnexpectedEOF.
|
||||
// The decoder used to erroneously return io.EOF in some cases here,
|
||||
// such as if the input was cut off right after some type specs,
|
||||
// but before any value was actually transmitted.
|
||||
if err != io.ErrUnexpectedEOF {
|
||||
t.Errorf("%d/%d: expected io.ErrUnexpectedEOF: %v", i, len(data), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,8 +57,8 @@ func TestGobError(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("no error")
|
||||
}
|
||||
if !strings.Contains(err.(error).Error(), "reading body EOF") {
|
||||
t.Fatal("expected `reading body EOF', got", err)
|
||||
if !strings.Contains(err.(error).Error(), "reading body unexpected EOF") {
|
||||
t.Fatal("expected `reading body unexpected EOF', got", err)
|
||||
}
|
||||
}()
|
||||
Register(new(S))
|
||||
|
Loading…
Reference in New Issue
Block a user