| // 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" |
| "io" |
| "testing" |
| |
| "golang.org/x/net/internal/quic/quicwire" |
| ) |
| |
| func TestStreamReadVarint(t *testing.T) { |
| st1, st2 := newStreamPair(t) |
| for _, b := range [][]byte{ |
| {0x00}, |
| {0x3f}, |
| {0x40, 0x00}, |
| {0x7f, 0xff}, |
| {0x80, 0x00, 0x00, 0x00}, |
| {0xbf, 0xff, 0xff, 0xff}, |
| {0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, |
| {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, |
| // Example cases from https://www.rfc-editor.org/rfc/rfc9000.html#section-a.1 |
| {0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c}, |
| {0x9d, 0x7f, 0x3e, 0x7d}, |
| {0x7b, 0xbd}, |
| {0x25}, |
| {0x40, 0x25}, |
| } { |
| trailer := []byte{0xde, 0xad, 0xbe, 0xef} |
| st1.Write(b) |
| st1.Write(trailer) |
| if err := st1.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| got, err := st2.readVarint() |
| if err != nil { |
| t.Fatalf("st.readVarint() = %v", err) |
| } |
| want, _ := quicwire.ConsumeVarintInt64(b) |
| if got != want { |
| t.Fatalf("st.readVarint() = %v, want %v", got, want) |
| } |
| gotTrailer := make([]byte, len(trailer)) |
| if _, err := io.ReadFull(st2, gotTrailer); err != nil { |
| t.Fatal(err) |
| } |
| if !bytes.Equal(gotTrailer, trailer) { |
| t.Fatalf("after st.readVarint, read %x, want %x", gotTrailer, trailer) |
| } |
| } |
| } |
| |
| func TestStreamWriteVarint(t *testing.T) { |
| st1, st2 := newStreamPair(t) |
| for _, v := range []int64{ |
| 0, |
| 63, |
| 16383, |
| 1073741823, |
| 4611686018427387903, |
| // Example cases from https://www.rfc-editor.org/rfc/rfc9000.html#section-a.1 |
| 151288809941952652, |
| 494878333, |
| 15293, |
| 37, |
| } { |
| trailer := []byte{0xde, 0xad, 0xbe, 0xef} |
| st1.writeVarint(v) |
| st1.Write(trailer) |
| if err := st1.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| |
| want := quicwire.AppendVarint(nil, uint64(v)) |
| want = append(want, trailer...) |
| |
| got := make([]byte, len(want)) |
| if _, err := io.ReadFull(st2, got); err != nil { |
| t.Fatal(err) |
| } |
| |
| if !bytes.Equal(got, want) { |
| t.Errorf("AppendVarint(nil, %v) = %x, want %x", v, got, want) |
| } |
| } |
| } |
| |
| func TestStreamReadFrames(t *testing.T) { |
| st1, st2 := newStreamPair(t) |
| for _, frame := range []struct { |
| ftype frameType |
| data []byte |
| }{{ |
| ftype: 1, |
| data: []byte("hello"), |
| }, { |
| ftype: 2, |
| data: []byte{}, |
| }, { |
| ftype: 3, |
| data: []byte("goodbye"), |
| }} { |
| st1.writeVarint(int64(frame.ftype)) |
| st1.writeVarint(int64(len(frame.data))) |
| st1.Write(frame.data) |
| if err := st1.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| |
| if gotFrameType, err := st2.readFrameHeader(); err != nil || gotFrameType != frame.ftype { |
| t.Fatalf("st.readFrameHeader() = %v, %v; want %v, nil", gotFrameType, err, frame.ftype) |
| } |
| if gotData, err := st2.readFrameData(); err != nil || !bytes.Equal(gotData, frame.data) { |
| t.Fatalf("st.readFrameData() = %x, %v; want %x, nil", gotData, err, frame.data) |
| } |
| if err := st2.endFrame(); err != nil { |
| t.Fatalf("st.endFrame() = %v; want nil", err) |
| } |
| } |
| } |
| |
| func TestStreamReadFrameUnderflow(t *testing.T) { |
| const size = 4 |
| st1, st2 := newStreamPair(t) |
| st1.writeVarint(0) // type |
| st1.writeVarint(size) // size |
| st1.Write(make([]byte, size)) // data |
| if err := st1.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| |
| if _, err := st2.readFrameHeader(); err != nil { |
| t.Fatalf("st.readFrameHeader() = %v", err) |
| } |
| if _, err := io.ReadFull(st2, make([]byte, size-1)); err != nil { |
| t.Fatalf("st.Read() = %v", err) |
| } |
| // We have not consumed the full frame: Error. |
| if err := st2.endFrame(); err != errH3FrameError { |
| t.Fatalf("st.endFrame before end: %v, want errH3FrameError", err) |
| } |
| } |
| |
| func TestStreamReadFrameWithoutEnd(t *testing.T) { |
| const size = 4 |
| st1, st2 := newStreamPair(t) |
| st1.writeVarint(0) // type |
| st1.writeVarint(size) // size |
| st1.Write(make([]byte, size)) // data |
| if err := st1.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| |
| if _, err := st2.readFrameHeader(); err != nil { |
| t.Fatalf("st.readFrameHeader() = %v", err) |
| } |
| if _, err := st2.readFrameHeader(); err == nil { |
| t.Fatalf("st.readFrameHeader before st.endFrame for prior frame: success, want error") |
| } |
| } |
| |
| func TestStreamReadFrameOverflow(t *testing.T) { |
| const size = 4 |
| st1, st2 := newStreamPair(t) |
| st1.writeVarint(0) // type |
| st1.writeVarint(size) // size |
| st1.Write(make([]byte, size+1)) // data |
| if err := st1.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| |
| if _, err := st2.readFrameHeader(); err != nil { |
| t.Fatalf("st.readFrameHeader() = %v", err) |
| } |
| if _, err := io.ReadFull(st2, make([]byte, size+1)); err != errH3FrameError { |
| t.Fatalf("st.Read past end of frame: %v, want errH3FrameError", err) |
| } |
| } |
| |
| func TestStreamReadFramePartial(t *testing.T) { |
| var frame []byte |
| frame = quicwire.AppendVarint(frame, 1000) // type |
| frame = quicwire.AppendVarint(frame, 2000) // size |
| |
| for i := 1; i < len(frame)-1; i++ { |
| st1, st2 := newStreamPair(t) |
| st1.Write(frame[:i]) |
| if err := st1.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| st1.stream.CloseWrite() |
| |
| if _, err := st2.readFrameHeader(); err == nil { |
| t.Fatalf("%v/%v bytes of frame available: st.readFrameHeader() succeded; want error", i, len(frame)) |
| } |
| } |
| } |
| |
| func TestStreamReadFrameData(t *testing.T) { |
| const typ = 10 |
| data := []byte("hello") |
| st1, st2 := newStreamPair(t) |
| st1.writeVarint(typ) // type |
| st1.writeVarint(int64(len(data))) // size |
| st1.Write(data) // data |
| if err := st1.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| |
| if got, err := st2.readFrameHeader(); err != nil || got != typ { |
| t.Fatalf("st.readFrameHeader() = %v, %v; want %v, nil", got, err, typ) |
| } |
| if got, err := st2.readFrameData(); err != nil || !bytes.Equal(got, data) { |
| t.Fatalf("st.readFrameData() = %x, %v; want %x, nil", got, err, data) |
| } |
| } |
| |
| func TestStreamReadByte(t *testing.T) { |
| const stype = 1 |
| const want = 42 |
| st1, st2 := newStreamPair(t) |
| st1.writeVarint(stype) // stream type |
| st1.writeVarint(1) // size |
| st1.Write([]byte{want}) // data |
| if err := st1.Flush(); err != nil { |
| t.Fatal(err) |
| } |
| |
| if got, err := st2.readFrameHeader(); err != nil || got != stype { |
| t.Fatalf("st.readFrameHeader() = %v, %v; want %v, nil", got, err, stype) |
| } |
| if got, err := st2.ReadByte(); err != nil || got != want { |
| t.Fatalf("st.ReadByte() = %v, %v; want %v, nil", got, err, want) |
| } |
| if got, err := st2.ReadByte(); err == nil { |
| t.Fatalf("reading past end of frame: st.ReadByte() = %v, %v; want error", got, err) |
| } |
| } |
| |
| func TestStreamDiscardFrame(t *testing.T) { |
| const typ = 10 |
| data := []byte("hello") |
| st1, st2 := newStreamPair(t) |
| st1.writeVarint(typ) // type |
| st1.writeVarint(int64(len(data))) // size |
| st1.Write(data) // data |
| st1.stream.CloseWrite() |
| |
| if got, err := st2.readFrameHeader(); err != nil || got != typ { |
| t.Fatalf("st.readFrameHeader() = %v, %v; want %v, nil", got, err, typ) |
| } |
| if err := st2.discardFrame(); err != nil { |
| t.Fatalf("st.discardFrame() = %v", err) |
| } |
| if b, err := io.ReadAll(st2); err != nil || len(b) > 0 { |
| t.Fatalf("after discarding frame, read %x, %v; want EOF", b, err) |
| } |
| } |
| |
| func newStreamPair(t testing.TB) (s1, s2 *stream) { |
| t.Helper() |
| q1, q2 := newQUICStreamPair(t) |
| return newStream(q1), newStream(q2) |
| } |