Skip to content

Commit 82fba38

Browse files
use io.ErrUnexpectedEOF when input is too short (#73)
1 parent 3200fb9 commit 82fba38

File tree

3 files changed

+71
-28
lines changed

3 files changed

+71
-28
lines changed

decoder.go

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,6 @@ import (
88
"golang.org/x/net/http2/hpack"
99
)
1010

11-
// A decodingError represents a QPACK decoding error as defined by the specification
12-
type decodingError struct {
13-
err error
14-
}
15-
16-
func (de decodingError) Error() string {
17-
return fmt.Sprintf("decoding error: %v", de.err)
18-
}
19-
2011
// An invalidIndexError is returned when decoding encounters an invalid index
2112
// (e.g., an index that is out of bounds for the static table).
2213
type invalidIndexError int
@@ -25,7 +16,7 @@ func (e invalidIndexError) Error() string {
2516
return fmt.Sprintf("invalid indexed representation index %d", int(e))
2617
}
2718

28-
var errNoDynamicTable = decodingError{errors.New("no dynamic table")}
19+
var errNoDynamicTable = errors.New("no dynamic table")
2920

3021
// A Decoder decodes QPACK header blocks.
3122
// A Decoder can be reused to decode multiple header blocks on different streams
@@ -59,7 +50,7 @@ func (d *Decoder) Decode(p []byte) DecodeFunc {
5950
p = rest
6051
readRequiredInsertCount = true
6152
if requiredInsertCount != 0 {
62-
return HeaderField{}, decodingError{errors.New("expected Required Insert Count to be zero")}
53+
return HeaderField{}, errors.New("expected Required Insert Count to be zero")
6354
}
6455
}
6556

@@ -71,7 +62,7 @@ func (d *Decoder) Decode(p []byte) DecodeFunc {
7162
p = rest
7263
readDeltaBase = true
7364
if base != 0 {
74-
return HeaderField{}, decodingError{errors.New("expected Base to be zero")}
65+
return HeaderField{}, errors.New("expected Base to be zero")
7566
}
7667
}
7768

@@ -111,7 +102,7 @@ func (d *Decoder) parseIndexedHeaderField(buf []byte) (_ HeaderField, rest []byt
111102
}
112103
hf, ok := d.at(index)
113104
if !ok {
114-
return HeaderField{}, buf, decodingError{invalidIndexError(index)}
105+
return HeaderField{}, buf, invalidIndexError(index)
115106
}
116107
return hf, rest, nil
117108
}
@@ -130,11 +121,11 @@ func (d *Decoder) parseLiteralHeaderField(buf []byte) (_ HeaderField, rest []byt
130121
}
131122
hf, ok := d.at(index)
132123
if !ok {
133-
return HeaderField{}, buf, decodingError{invalidIndexError(index)}
124+
return HeaderField{}, buf, invalidIndexError(index)
134125
}
135126
buf = rest
136127
if len(buf) == 0 {
137-
return HeaderField{}, buf, decodingError{errors.New("truncated literal header field")}
128+
return HeaderField{}, buf, io.ErrUnexpectedEOF
138129
}
139130
usesHuffman := buf[0]&0x80 > 0
140131
val, rest, err := d.readString(rest, 7, usesHuffman)
@@ -153,7 +144,7 @@ func (d *Decoder) parseLiteralHeaderFieldWithoutNameReference(buf []byte) (_ Hea
153144
}
154145
buf = rest
155146
if len(buf) == 0 {
156-
return HeaderField{}, rest, decodingError{errors.New("truncated literal header field without name reference")}
147+
return HeaderField{}, rest, io.ErrUnexpectedEOF
157148
}
158149
usesHuffmanForVal := buf[0]&0x80 > 0
159150
val, rest, err := d.readString(buf, 7, usesHuffmanForVal)
@@ -169,7 +160,7 @@ func (d *Decoder) readString(buf []byte, n uint8, usesHuffman bool) (string, []b
169160
return "", nil, err
170161
}
171162
if uint64(len(buf)) < l {
172-
return "", nil, decodingError{errors.New("truncated string")}
163+
return "", nil, io.ErrUnexpectedEOF
173164
}
174165
var val string
175166
if usesHuffman {

decoder_test.go

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ func TestDecoderInvalidInputs(t *testing.T) {
2424
{
2525
name: "non-zero required insert count", // we don't support dynamic table updates
2626
input: append(appendVarInt(nil, 8, 1), appendVarInt(nil, 7, 0)...),
27-
expected: "decoding error: expected Required Insert Count to be zero",
27+
expected: "expected Required Insert Count to be zero",
2828
},
2929
{
3030
name: "non-zero delta base", // we don't support dynamic table updates
3131
input: append(appendVarInt(nil, 8, 0), appendVarInt(nil, 7, 1)...),
32-
expected: "decoding error: expected Base to be zero",
32+
expected: "expected Base to be zero",
3333
},
3434
{
3535
name: "unknown type byte",
@@ -174,7 +174,7 @@ func TestDecoderInvalidIndexedHeaderFields(t *testing.T) {
174174
data[0] ^= 0x80 | 0x40
175175
return insertPrefix(data)
176176
}(),
177-
expected: "decoding error: invalid indexed representation index 10000",
177+
expected: "invalid indexed representation index 10000",
178178
},
179179
{
180180
name: "rejects an indexed header field that references the dynamic table",
@@ -209,6 +209,58 @@ func TestDecoderLiteralHeaderFieldWithoutNameReference(t *testing.T) {
209209
require.Equal(t, literalFieldWithoutNameReference.Expected, decodeAll(t, decodeFn))
210210
}
211211

212+
func TestDecoderEOF(t *testing.T) {
213+
t.Run("literal field without name reference", func(t *testing.T) {
214+
testDecoderEOF(t,
215+
literalFieldWithoutNameReference.Data,
216+
len(literalFieldWithoutNameReference.Expected),
217+
)
218+
})
219+
220+
t.Run("literal field with name reference", func(t *testing.T) {
221+
testDecoderEOF(t,
222+
literalFieldWithNameReference.Data,
223+
len(literalFieldWithNameReference.Expected),
224+
)
225+
})
226+
227+
t.Run("literal field with Huffman encoding", func(t *testing.T) {
228+
testDecoderEOF(t,
229+
literalFieldWithHuffmanEncoding.Data,
230+
len(literalFieldWithHuffmanEncoding.Expected),
231+
)
232+
})
233+
234+
t.Run("indexed field", func(t *testing.T) {
235+
testDecoderEOF(t,
236+
indexedField.Data,
237+
len(indexedField.Expected),
238+
)
239+
})
240+
}
241+
242+
func testDecoderEOF(t *testing.T, data []byte, numExpected int) {
243+
for i := range data {
244+
dec := NewDecoder()
245+
decodeFn := dec.Decode(data[:i])
246+
var hfs []HeaderField
247+
for {
248+
hf, err := decodeFn()
249+
// the data might have been cut right after a header field,
250+
// which is a valid header
251+
if err == io.EOF {
252+
require.Less(t, len(hfs), numExpected)
253+
break
254+
}
255+
if err != nil {
256+
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
257+
break
258+
}
259+
hfs = append(hfs, hf)
260+
}
261+
}
262+
}
263+
212264
func BenchmarkDecoder(b *testing.B) {
213265
b.Run("literal field without name reference", func(b *testing.B) {
214266
benchmarkDecoder(b,

varint.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ package qpack
22

33
// copied from the Go standard library HPACK implementation
44

5-
import "errors"
6-
7-
var (
8-
errVarintOverflow = errors.New("varint integer overflow")
9-
errNeedMore = errors.New("need more data")
5+
import (
6+
"errors"
7+
"io"
108
)
119

10+
var errVarintOverflow = errors.New("varint integer overflow")
11+
1212
// appendVarInt appends i, as encoded in variable integer form using n
1313
// bit prefix, to dst and returns the extended buffer.
1414
//
@@ -34,13 +34,13 @@ func appendVarInt(dst []byte, n byte, i uint64) []byte {
3434
// n must always be between 1 and 8.
3535
//
3636
// The returned remain buffer is either a smaller suffix of p, or err != nil.
37-
// The error is errNeedMore if p doesn't contain a complete integer.
37+
// The error is io.ErrUnexpectedEOF if p doesn't contain a complete integer.
3838
func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) {
3939
if n < 1 || n > 8 {
4040
panic("bad n")
4141
}
4242
if len(p) == 0 {
43-
return 0, p, errNeedMore
43+
return 0, p, io.ErrUnexpectedEOF
4444
}
4545
i = uint64(p[0])
4646
if n < 8 {
@@ -65,5 +65,5 @@ func readVarInt(n byte, p []byte) (i uint64, remain []byte, err error) {
6565
return 0, origP, errVarintOverflow
6666
}
6767
}
68-
return 0, origP, errNeedMore
68+
return 0, origP, io.ErrUnexpectedEOF
6969
}

0 commit comments

Comments
 (0)