blob: 6e16511fc64af2ffdc7d46b0b542fc8cf25b38ca [file] [log] [blame] [edit]
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build go1.24
package http3
import (
"bytes"
"testing"
)
func TestPrefixedInt(t *testing.T) {
st1, st2 := newStreamPair(t)
for _, test := range []struct {
value int64
prefixLen uint8
encoded []byte
}{
// https://www.rfc-editor.org/rfc/rfc7541#appendix-C.1.1
{
value: 10,
prefixLen: 5,
encoded: []byte{
0b_0000_1010,
},
},
// https://www.rfc-editor.org/rfc/rfc7541#appendix-C.1.2
{
value: 1337,
prefixLen: 5,
encoded: []byte{
0b0001_1111,
0b1001_1010,
0b0000_1010,
},
},
// https://www.rfc-editor.org/rfc/rfc7541#appendix-C.1.3
{
value: 42,
prefixLen: 8,
encoded: []byte{
0b0010_1010,
},
},
} {
highBitMask := ^((byte(1) << test.prefixLen) - 1)
for _, highBits := range []byte{
0, highBitMask, 0b1010_1010 & highBitMask,
} {
gotEnc := appendPrefixedInt(nil, highBits, test.prefixLen, test.value)
wantEnc := append([]byte{}, test.encoded...)
wantEnc[0] |= highBits
if !bytes.Equal(gotEnc, wantEnc) {
t.Errorf("appendPrefixedInt(nil, 0b%08b, %v, %v) = {%x}, want {%x}",
highBits, test.prefixLen, test.value, gotEnc, wantEnc)
}
st1.Write(gotEnc)
if err := st1.Flush(); err != nil {
t.Fatal(err)
}
gotFirstByte, v, err := st2.readPrefixedInt(test.prefixLen)
if err != nil || gotFirstByte&highBitMask != highBits || v != test.value {
t.Errorf("st.readPrefixedInt(%v) = 0b%08b, %v, %v; want 0b%08b, %v, nil", test.prefixLen, gotFirstByte, v, err, highBits, test.value)
}
}
}
}
func TestPrefixedString(t *testing.T) {
st1, st2 := newStreamPair(t)
for _, test := range []struct {
value string
prefixLen uint8
encoded []byte
}{
// https://www.rfc-editor.org/rfc/rfc7541#appendix-C.6.1
{
value: "302",
prefixLen: 7,
encoded: []byte{
0x82, // H bit + length 2
0x64, 0x02,
},
},
{
value: "private",
prefixLen: 5,
encoded: []byte{
0x25, // H bit + length 5
0xae, 0xc3, 0x77, 0x1a, 0x4b,
},
},
{
value: "Mon, 21 Oct 2013 20:13:21 GMT",
prefixLen: 7,
encoded: []byte{
0x96, // H bit + length 22
0xd0, 0x7a, 0xbe, 0x94, 0x10, 0x54, 0xd4, 0x44,
0xa8, 0x20, 0x05, 0x95, 0x04, 0x0b, 0x81, 0x66,
0xe0, 0x82, 0xa6, 0x2d, 0x1b, 0xff,
},
},
{
value: "https://www.example.com",
prefixLen: 7,
encoded: []byte{
0x91, // H bit + length 17
0x9d, 0x29, 0xad, 0x17, 0x18, 0x63, 0xc7, 0x8f,
0x0b, 0x97, 0xc8, 0xe9, 0xae, 0x82, 0xae, 0x43,
0xd3,
},
},
// Not Huffman encoded (encoded size == unencoded size).
{
value: "a",
prefixLen: 7,
encoded: []byte{
0x01, // length 1
0x61,
},
},
// Empty string.
{
value: "",
prefixLen: 7,
encoded: []byte{
0x00, // length 0
},
},
} {
highBitMask := ^((byte(1) << (test.prefixLen + 1)) - 1)
for _, highBits := range []byte{
0, highBitMask, 0b1010_1010 & highBitMask,
} {
gotEnc := appendPrefixedString(nil, highBits, test.prefixLen, test.value)
wantEnc := append([]byte{}, test.encoded...)
wantEnc[0] |= highBits
if !bytes.Equal(gotEnc, wantEnc) {
t.Errorf("appendPrefixedString(nil, 0b%08b, %v, %v) = {%x}, want {%x}",
highBits, test.prefixLen, test.value, gotEnc, wantEnc)
}
st1.Write(gotEnc)
if err := st1.Flush(); err != nil {
t.Fatal(err)
}
gotFirstByte, v, err := st2.readPrefixedString(test.prefixLen)
if err != nil || gotFirstByte&highBitMask != highBits || v != test.value {
t.Errorf("st.readPrefixedInt(%v) = 0b%08b, %q, %v; want 0b%08b, %q, nil", test.prefixLen, gotFirstByte, v, err, highBits, test.value)
}
}
}
}
func TestHuffmanDecodingFailure(t *testing.T) {
st1, st2 := newStreamPair(t)
st1.Write([]byte{
0x82, // H bit + length 4
0b_1111_1111,
0b_1111_1111,
0b_1111_1111,
0b_1111_1111,
})
if err := st1.Flush(); err != nil {
t.Fatal(err)
}
if b, v, err := st2.readPrefixedString(7); err == nil {
t.Fatalf("readPrefixedString(7) = %x, %v, nil; want error", b, v)
}
}