1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
18 func (d *Decoder) mustAt(idx int) HeaderField {
19 if hf, ok := d.at(uint64(idx)); !ok {
20 panic(fmt.Sprintf("bogus index %d", idx))
26 func TestDynamicTableAt(t *testing.T) {
27 d := NewDecoder(4096, nil)
29 if got, want := at(2), (pair(":method", "GET")); got != want {
30 t.Errorf("at(2) = %v; want %v", got, want)
32 d.dynTab.add(pair("foo", "bar"))
33 d.dynTab.add(pair("blake", "miz"))
34 if got, want := at(staticTable.len()+1), (pair("blake", "miz")); got != want {
35 t.Errorf("at(dyn 1) = %v; want %v", got, want)
37 if got, want := at(staticTable.len()+2), (pair("foo", "bar")); got != want {
38 t.Errorf("at(dyn 2) = %v; want %v", got, want)
40 if got, want := at(3), (pair(":method", "POST")); got != want {
41 t.Errorf("at(3) = %v; want %v", got, want)
45 func TestDynamicTableSizeEvict(t *testing.T) {
46 d := NewDecoder(4096, nil)
47 if want := uint32(0); d.dynTab.size != want {
48 t.Fatalf("size = %d; want %d", d.dynTab.size, want)
51 add(pair("blake", "eats pizza"))
52 if want := uint32(15 + 32); d.dynTab.size != want {
53 t.Fatalf("after pizza, size = %d; want %d", d.dynTab.size, want)
55 add(pair("foo", "bar"))
56 if want := uint32(15 + 32 + 6 + 32); d.dynTab.size != want {
57 t.Fatalf("after foo bar, size = %d; want %d", d.dynTab.size, want)
59 d.dynTab.setMaxSize(15 + 32 + 1 /* slop */)
60 if want := uint32(6 + 32); d.dynTab.size != want {
61 t.Fatalf("after setMaxSize, size = %d; want %d", d.dynTab.size, want)
63 if got, want := d.mustAt(staticTable.len()+1), (pair("foo", "bar")); got != want {
64 t.Errorf("at(dyn 1) = %v; want %v", got, want)
66 add(pair("long", strings.Repeat("x", 500)))
67 if want := uint32(0); d.dynTab.size != want {
68 t.Fatalf("after big one, size = %d; want %d", d.dynTab.size, want)
72 func TestDecoderDecode(t *testing.T) {
77 wantDynTab []HeaderField // newest entry first
79 // C.2.1 Literal Header Field with Indexing
80 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.1
81 {"C.2.1", dehex("400a 6375 7374 6f6d 2d6b 6579 0d63 7573 746f 6d2d 6865 6164 6572"),
82 []HeaderField{pair("custom-key", "custom-header")},
83 []HeaderField{pair("custom-key", "custom-header")},
86 // C.2.2 Literal Header Field without Indexing
87 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.2
88 {"C.2.2", dehex("040c 2f73 616d 706c 652f 7061 7468"),
89 []HeaderField{pair(":path", "/sample/path")},
92 // C.2.3 Literal Header Field never Indexed
93 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.3
94 {"C.2.3", dehex("1008 7061 7373 776f 7264 0673 6563 7265 74"),
95 []HeaderField{{"password", "secret", true}},
98 // C.2.4 Indexed Header Field
99 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.2.4
100 {"C.2.4", []byte("\x82"),
101 []HeaderField{pair(":method", "GET")},
104 for _, tt := range tests {
105 d := NewDecoder(4096, nil)
106 hf, err := d.DecodeFull(tt.in)
108 t.Errorf("%s: %v", tt.name, err)
111 if !reflect.DeepEqual(hf, tt.want) {
112 t.Errorf("%s: Got %v; want %v", tt.name, hf, tt.want)
114 gotDynTab := d.dynTab.reverseCopy()
115 if !reflect.DeepEqual(gotDynTab, tt.wantDynTab) {
116 t.Errorf("%s: dynamic table after = %v; want %v", tt.name, gotDynTab, tt.wantDynTab)
121 func (dt *dynamicTable) reverseCopy() (hf []HeaderField) {
122 hf = make([]HeaderField, len(dt.table.ents))
124 hf[i] = dt.table.ents[len(dt.table.ents)-1-i]
129 type encAndWant struct {
132 wantDynTab []HeaderField
136 // C.3 Request Examples without Huffman Coding
137 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.3
138 func TestDecodeC3_NoHuffman(t *testing.T) {
139 testDecodeSeries(t, 4096, []encAndWant{
140 {dehex("8286 8441 0f77 7777 2e65 7861 6d70 6c65 2e63 6f6d"),
142 pair(":method", "GET"),
143 pair(":scheme", "http"),
145 pair(":authority", "www.example.com"),
148 pair(":authority", "www.example.com"),
152 {dehex("8286 84be 5808 6e6f 2d63 6163 6865"),
154 pair(":method", "GET"),
155 pair(":scheme", "http"),
157 pair(":authority", "www.example.com"),
158 pair("cache-control", "no-cache"),
161 pair("cache-control", "no-cache"),
162 pair(":authority", "www.example.com"),
166 {dehex("8287 85bf 400a 6375 7374 6f6d 2d6b 6579 0c63 7573 746f 6d2d 7661 6c75 65"),
168 pair(":method", "GET"),
169 pair(":scheme", "https"),
170 pair(":path", "/index.html"),
171 pair(":authority", "www.example.com"),
172 pair("custom-key", "custom-value"),
175 pair("custom-key", "custom-value"),
176 pair("cache-control", "no-cache"),
177 pair(":authority", "www.example.com"),
184 // C.4 Request Examples with Huffman Coding
185 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.4
186 func TestDecodeC4_Huffman(t *testing.T) {
187 testDecodeSeries(t, 4096, []encAndWant{
188 {dehex("8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff"),
190 pair(":method", "GET"),
191 pair(":scheme", "http"),
193 pair(":authority", "www.example.com"),
196 pair(":authority", "www.example.com"),
200 {dehex("8286 84be 5886 a8eb 1064 9cbf"),
202 pair(":method", "GET"),
203 pair(":scheme", "http"),
205 pair(":authority", "www.example.com"),
206 pair("cache-control", "no-cache"),
209 pair("cache-control", "no-cache"),
210 pair(":authority", "www.example.com"),
214 {dehex("8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925 a849 e95b b8e8 b4bf"),
216 pair(":method", "GET"),
217 pair(":scheme", "https"),
218 pair(":path", "/index.html"),
219 pair(":authority", "www.example.com"),
220 pair("custom-key", "custom-value"),
223 pair("custom-key", "custom-value"),
224 pair("cache-control", "no-cache"),
225 pair(":authority", "www.example.com"),
232 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.5
233 // "This section shows several consecutive header lists, corresponding
234 // to HTTP responses, on the same connection. The HTTP/2 setting
235 // parameter SETTINGS_HEADER_TABLE_SIZE is set to the value of 256
236 // octets, causing some evictions to occur."
237 func TestDecodeC5_ResponsesNoHuff(t *testing.T) {
238 testDecodeSeries(t, 256, []encAndWant{
240 4803 3330 3258 0770 7269 7661 7465 611d
241 4d6f 6e2c 2032 3120 4f63 7420 3230 3133
242 2032 303a 3133 3a32 3120 474d 546e 1768
243 7474 7073 3a2f 2f77 7777 2e65 7861 6d70
247 pair(":status", "302"),
248 pair("cache-control", "private"),
249 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
250 pair("location", "https://www.example.com"),
253 pair("location", "https://www.example.com"),
254 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
255 pair("cache-control", "private"),
256 pair(":status", "302"),
260 {dehex("4803 3330 37c1 c0bf"),
262 pair(":status", "307"),
263 pair("cache-control", "private"),
264 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
265 pair("location", "https://www.example.com"),
268 pair(":status", "307"),
269 pair("location", "https://www.example.com"),
270 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
271 pair("cache-control", "private"),
276 88c1 611d 4d6f 6e2c 2032 3120 4f63 7420
277 3230 3133 2032 303a 3133 3a32 3220 474d
278 54c0 5a04 677a 6970 7738 666f 6f3d 4153
279 444a 4b48 514b 425a 584f 5157 454f 5049
280 5541 5851 5745 4f49 553b 206d 6178 2d61
281 6765 3d33 3630 303b 2076 6572 7369 6f6e
285 pair(":status", "200"),
286 pair("cache-control", "private"),
287 pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
288 pair("location", "https://www.example.com"),
289 pair("content-encoding", "gzip"),
290 pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
293 pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
294 pair("content-encoding", "gzip"),
295 pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
302 // http://http2.github.io/http2-spec/compression.html#rfc.section.C.6
303 // "This section shows the same examples as the previous section, but
304 // using Huffman encoding for the literal values. The HTTP/2 setting
305 // parameter SETTINGS_HEADER_TABLE_SIZE is set to the value of 256
306 // octets, causing some evictions to occur. The eviction mechanism
307 // uses the length of the decoded literal values, so the same
308 // evictions occurs as in the previous section."
309 func TestDecodeC6_ResponsesHuffman(t *testing.T) {
310 testDecodeSeries(t, 256, []encAndWant{
312 4882 6402 5885 aec3 771a 4b61 96d0 7abe
313 9410 54d4 44a8 2005 9504 0b81 66e0 82a6
314 2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8
318 pair(":status", "302"),
319 pair("cache-control", "private"),
320 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
321 pair("location", "https://www.example.com"),
324 pair("location", "https://www.example.com"),
325 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
326 pair("cache-control", "private"),
327 pair(":status", "302"),
331 {dehex("4883 640e ffc1 c0bf"),
333 pair(":status", "307"),
334 pair("cache-control", "private"),
335 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
336 pair("location", "https://www.example.com"),
339 pair(":status", "307"),
340 pair("location", "https://www.example.com"),
341 pair("date", "Mon, 21 Oct 2013 20:13:21 GMT"),
342 pair("cache-control", "private"),
347 88c1 6196 d07a be94 1054 d444 a820 0595
348 040b 8166 e084 a62d 1bff c05a 839b d9ab
349 77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b
350 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f
351 9587 3160 65c0 03ed 4ee5 b106 3d50 07
354 pair(":status", "200"),
355 pair("cache-control", "private"),
356 pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
357 pair("location", "https://www.example.com"),
358 pair("content-encoding", "gzip"),
359 pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
362 pair("set-cookie", "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"),
363 pair("content-encoding", "gzip"),
364 pair("date", "Mon, 21 Oct 2013 20:13:22 GMT"),
371 func testDecodeSeries(t *testing.T, size uint32, steps []encAndWant) {
372 d := NewDecoder(size, nil)
373 for i, step := range steps {
374 hf, err := d.DecodeFull(step.enc)
376 t.Fatalf("Error at step index %d: %v", i, err)
378 if !reflect.DeepEqual(hf, step.want) {
379 t.Fatalf("At step index %d: Got headers %v; want %v", i, hf, step.want)
381 gotDynTab := d.dynTab.reverseCopy()
382 if !reflect.DeepEqual(gotDynTab, step.wantDynTab) {
383 t.Errorf("After step index %d, dynamic table = %v; want %v", i, gotDynTab, step.wantDynTab)
385 if d.dynTab.size != step.wantDynSize {
386 t.Errorf("After step index %d, dynamic table size = %v; want %v", i, d.dynTab.size, step.wantDynSize)
391 func TestHuffmanDecodeExcessPadding(t *testing.T) {
393 {0xff}, // Padding Exceeds 7 bits
394 {0x1f, 0xff}, // {"a", 1 byte excess padding}
395 {0x1f, 0xff, 0xff}, // {"a", 2 byte excess padding}
396 {0x1f, 0xff, 0xff, 0xff}, // {"a", 3 byte excess padding}
397 {0xff, 0x9f, 0xff, 0xff, 0xff}, // {"a", 29 bit excess padding}
398 {'R', 0xbc, '0', 0xff, 0xff, 0xff, 0xff}, // Padding ends on partial symbol.
400 for i, in := range tests {
402 if _, err := HuffmanDecode(&buf, in); err != ErrInvalidHuffman {
403 t.Errorf("test-%d: decode(%q) = %v; want ErrInvalidHuffman", i, in, err)
408 func TestHuffmanDecodeEOS(t *testing.T) {
409 in := []byte{0xff, 0xff, 0xff, 0xff, 0xfc} // {EOS, "?"}
411 if _, err := HuffmanDecode(&buf, in); err != ErrInvalidHuffman {
412 t.Errorf("error = %v; want ErrInvalidHuffman", err)
416 func TestHuffmanDecodeMaxLengthOnTrailingByte(t *testing.T) {
417 in := []byte{0x00, 0x01} // {"0", "0", "0"}
419 if err := huffmanDecode(&buf, 2, in); err != ErrStringLength {
420 t.Errorf("error = %v; want ErrStringLength", err)
424 func TestHuffmanDecodeCorruptPadding(t *testing.T) {
427 if _, err := HuffmanDecode(&buf, in); err != ErrInvalidHuffman {
428 t.Errorf("error = %v; want ErrInvalidHuffman", err)
432 func TestHuffmanDecode(t *testing.T) {
436 {"f1e3 c2e5 f23a 6ba0 ab90 f4ff", "www.example.com"},
437 {"a8eb 1064 9cbf", "no-cache"},
438 {"25a8 49e9 5ba9 7d7f", "custom-key"},
439 {"25a8 49e9 5bb8 e8b4 bf", "custom-value"},
441 {"aec3 771a 4b", "private"},
442 {"d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff", "Mon, 21 Oct 2013 20:13:21 GMT"},
443 {"9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3", "https://www.example.com"},
445 {"94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07",
446 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
448 for i, tt := range tests {
450 in, err := hex.DecodeString(strings.Replace(tt.inHex, " ", "", -1))
452 t.Errorf("%d. hex input error: %v", i, err)
455 if _, err := HuffmanDecode(&buf, in); err != nil {
456 t.Errorf("%d. decode error: %v", i, err)
459 if got := buf.String(); tt.want != got {
460 t.Errorf("%d. decode = %q; want %q", i, got, tt.want)
465 func TestAppendHuffmanString(t *testing.T) {
469 {"www.example.com", "f1e3 c2e5 f23a 6ba0 ab90 f4ff"},
470 {"no-cache", "a8eb 1064 9cbf"},
471 {"custom-key", "25a8 49e9 5ba9 7d7f"},
472 {"custom-value", "25a8 49e9 5bb8 e8b4 bf"},
474 {"private", "aec3 771a 4b"},
475 {"Mon, 21 Oct 2013 20:13:21 GMT", "d07a be94 1054 d444 a820 0595 040b 8166 e082 a62d 1bff"},
476 {"https://www.example.com", "9d29 ad17 1863 c78f 0b97 c8e9 ae82 ae43 d3"},
478 {"foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
479 "94e7 821d d7f2 e6c7 b335 dfdf cd5b 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f 9587 3160 65c0 03ed 4ee5 b106 3d50 07"},
481 for i, tt := range tests {
483 want := strings.Replace(tt.want, " ", "", -1)
484 buf = AppendHuffmanString(buf, tt.in)
485 if got := hex.EncodeToString(buf); want != got {
486 t.Errorf("%d. encode = %q; want %q", i, got, want)
491 func TestHuffmanMaxStrLen(t *testing.T) {
492 const msg = "Some string"
493 huff := AppendHuffmanString(nil, msg)
495 testGood := func(max int) {
497 if err := huffmanDecode(&out, max, huff); err != nil {
498 t.Errorf("For maxLen=%d, unexpected error: %v", max, err)
500 if out.String() != msg {
501 t.Errorf("For maxLen=%d, out = %q; want %q", max, out.String(), msg)
506 testGood(len(msg) + 1)
509 if err := huffmanDecode(&out, len(msg)-1, huff); err != ErrStringLength {
510 t.Errorf("err = %v; want ErrStringLength", err)
514 func TestHuffmanRoundtripStress(t *testing.T) {
515 const Len = 50 // of uncompressed string
516 input := make([]byte, Len)
517 var output bytes.Buffer
524 seed := time.Now().UnixNano()
525 t.Logf("Seed = %v", seed)
526 src := rand.New(rand.NewSource(seed))
528 for i := 0; i < n; i++ {
529 for l := range input {
530 input[l] = byte(src.Intn(256))
532 huff = AppendHuffmanString(huff[:0], string(input))
533 encSize += int64(len(huff))
535 if err := huffmanDecode(&output, 0, huff); err != nil {
536 t.Errorf("Failed to decode %q -> %q -> error %v", input, huff, err)
539 if !bytes.Equal(output.Bytes(), input) {
540 t.Errorf("Roundtrip failure on %q -> %q -> %q", input, huff, output.Bytes())
543 t.Logf("Compressed size of original: %0.02f%% (%v -> %v)", 100*(float64(encSize)/(Len*float64(n))), Len*n, encSize)
546 func TestHuffmanDecodeFuzz(t *testing.T) {
547 const Len = 50 // of compressed
548 var buf, zbuf bytes.Buffer
554 seed := time.Now().UnixNano()
555 t.Logf("Seed = %v", seed)
556 src := rand.New(rand.NewSource(seed))
558 for i := 0; i < n; i++ {
561 // Start with at least one invalid one.
562 zbuf.WriteString("00\x91\xff\xff\xff\xff\xc8")
564 for l := 0; l < Len; l++ {
565 zbuf.WriteByte(byte(src.Intn(256)))
570 if err := huffmanDecode(&buf, 0, zbuf.Bytes()); err != nil {
571 if err == ErrInvalidHuffman {
575 t.Errorf("Failed to decode %q: %v", zbuf.Bytes(), err)
579 t.Logf("%0.02f%% are invalid (%d / %d)", 100*float64(numFail)/float64(n), numFail, n)
581 t.Error("expected at least one invalid huffman encoding (test starts with one)")
585 func TestReadVarInt(t *testing.T) {
597 {1, []byte{0}, res{0, 1, nil}},
598 {2, []byte{2}, res{2, 1, nil}},
599 {3, []byte{6}, res{6, 1, nil}},
600 {4, []byte{14}, res{14, 1, nil}},
601 {5, []byte{30}, res{30, 1, nil}},
602 {6, []byte{62}, res{62, 1, nil}},
603 {7, []byte{126}, res{126, 1, nil}},
604 {8, []byte{254}, res{254, 1, nil}},
606 // Doesn't fit in a byte:
607 {1, []byte{1}, res{0, 0, errNeedMore}},
608 {2, []byte{3}, res{0, 0, errNeedMore}},
609 {3, []byte{7}, res{0, 0, errNeedMore}},
610 {4, []byte{15}, res{0, 0, errNeedMore}},
611 {5, []byte{31}, res{0, 0, errNeedMore}},
612 {6, []byte{63}, res{0, 0, errNeedMore}},
613 {7, []byte{127}, res{0, 0, errNeedMore}},
614 {8, []byte{255}, res{0, 0, errNeedMore}},
616 // Ignoring top bits:
617 {5, []byte{255, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 111
618 {5, []byte{159, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 100
619 {5, []byte{191, 154, 10}, res{1337, 3, nil}}, // high dummy three bits: 101
622 {5, []byte{191, 154, 10, 2}, res{1337, 3, nil}}, // extra byte
625 {5, []byte{191, 154}, res{0, 0, errNeedMore}},
628 {1, []byte{255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128}, res{0, 0, errVarintOverflow}},
630 for _, tt := range tests {
631 i, remain, err := readVarInt(tt.n, tt.p)
632 consumed := len(tt.p) - len(remain)
633 got := res{i, consumed, err}
635 t.Errorf("readVarInt(%d, %v ~ %x) = %+v; want %+v", tt.n, tt.p, tt.p, got, tt.want)
640 // Fuzz crash, originally reported at https://github.com/bradfitz/http2/issues/56
641 func TestHuffmanFuzzCrash(t *testing.T) {
642 got, err := HuffmanDecodeToString([]byte("00\x91\xff\xff\xff\xff\xc8"))
644 t.Errorf("Got %q; want empty string", got)
646 if err != ErrInvalidHuffman {
647 t.Errorf("Err = %v; want ErrInvalidHuffman", err)
651 func pair(name, value string) HeaderField {
652 return HeaderField{Name: name, Value: value}
655 func dehex(s string) []byte {
656 s = strings.Replace(s, " ", "", -1)
657 s = strings.Replace(s, "\n", "", -1)
658 b, err := hex.DecodeString(s)
665 func TestEmitEnabled(t *testing.T) {
667 enc := NewEncoder(&buf)
668 enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
669 enc.WriteField(HeaderField{Name: "foo", Value: "bar"})
673 dec = NewDecoder(8<<20, func(HeaderField) {
675 dec.SetEmitEnabled(false)
677 if !dec.EmitEnabled() {
678 t.Errorf("initial emit enabled = false; want true")
680 if _, err := dec.Write(buf.Bytes()); err != nil {
683 if numCallback != 1 {
684 t.Errorf("num callbacks = %d; want 1", numCallback)
686 if dec.EmitEnabled() {
687 t.Errorf("emit enabled = true; want false")
691 func TestSaveBufLimit(t *testing.T) {
692 const maxStr = 1 << 10
693 var got []HeaderField
694 dec := NewDecoder(initialHeaderTableSize, func(hf HeaderField) {
695 got = append(got, hf)
697 dec.SetMaxStringLength(maxStr)
699 frag = append(frag[:0], encodeTypeByte(false, false))
700 frag = appendVarInt(frag, 7, 3)
701 frag = append(frag, "foo"...)
702 frag = appendVarInt(frag, 7, 3)
703 frag = append(frag, "bar"...)
705 if _, err := dec.Write(frag); err != nil {
709 want := []HeaderField{{Name: "foo", Value: "bar"}}
710 if !reflect.DeepEqual(got, want) {
711 t.Errorf("After small writes, got %v; want %v", got, want)
714 frag = append(frag[:0], encodeTypeByte(false, false))
715 frag = appendVarInt(frag, 7, maxStr*3)
716 frag = append(frag, make([]byte, maxStr*3)...)
718 _, err := dec.Write(frag)
719 if err != ErrStringLength {
720 t.Fatalf("Write error = %v; want ErrStringLength", err)