github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/http3/frames_test.go (about)

     1  package http3
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  
     9  	"github.com/metacubex/quic-go"
    10  	mockquic "github.com/metacubex/quic-go/internal/mocks/quic"
    11  	"github.com/metacubex/quic-go/quicvarint"
    12  
    13  	. "github.com/onsi/ginkgo/v2"
    14  	. "github.com/onsi/gomega"
    15  	"go.uber.org/mock/gomock"
    16  )
    17  
    18  type errReader struct{ err error }
    19  
    20  func (e errReader) Read([]byte) (int, error) { return 0, e.err }
    21  
    22  var _ = Describe("Frames", func() {
    23  	It("skips unknown frames", func() {
    24  		data := quicvarint.Append(nil, 0xdeadbeef) // type byte
    25  		data = quicvarint.Append(data, 0x42)
    26  		data = append(data, make([]byte, 0x42)...)
    27  		data = (&dataFrame{Length: 0x1234}).Append(data)
    28  		fp := frameParser{r: bytes.NewReader(data)}
    29  		frame, err := fp.ParseNext()
    30  		Expect(err).ToNot(HaveOccurred())
    31  		Expect(frame).To(BeAssignableToTypeOf(&dataFrame{}))
    32  		Expect(frame.(*dataFrame).Length).To(Equal(uint64(0x1234)))
    33  	})
    34  
    35  	It("closes the connection when encountering a reserved frame type", func() {
    36  		conn := mockquic.NewMockEarlyConnection(mockCtrl)
    37  		for _, ft := range []uint64{0x2, 0x6, 0x8, 0x9} {
    38  			data := quicvarint.Append(nil, ft)
    39  			data = quicvarint.Append(data, 6)
    40  			data = append(data, []byte("foobar")...)
    41  
    42  			conn.EXPECT().CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), gomock.Any())
    43  			fp := frameParser{
    44  				r:    bytes.NewReader(data),
    45  				conn: conn,
    46  			}
    47  			_, err := fp.ParseNext()
    48  			Expect(err).To(HaveOccurred())
    49  			Expect(err.Error()).To(ContainSubstring("http3: reserved frame type"))
    50  		}
    51  	})
    52  
    53  	Context("DATA frames", func() {
    54  		It("parses", func() {
    55  			data := quicvarint.Append(nil, 0) // type byte
    56  			data = quicvarint.Append(data, 0x1337)
    57  			fp := frameParser{r: bytes.NewReader(data)}
    58  			frame, err := fp.ParseNext()
    59  			Expect(err).ToNot(HaveOccurred())
    60  			Expect(frame).To(BeAssignableToTypeOf(&dataFrame{}))
    61  			Expect(frame.(*dataFrame).Length).To(Equal(uint64(0x1337)))
    62  		})
    63  
    64  		It("writes", func() {
    65  			fp := frameParser{r: bytes.NewReader((&dataFrame{Length: 0xdeadbeef}).Append(nil))}
    66  			frame, err := fp.ParseNext()
    67  			Expect(err).ToNot(HaveOccurred())
    68  			Expect(err).ToNot(HaveOccurred())
    69  			Expect(frame).To(BeAssignableToTypeOf(&dataFrame{}))
    70  			Expect(frame.(*dataFrame).Length).To(Equal(uint64(0xdeadbeef)))
    71  		})
    72  	})
    73  
    74  	Context("HEADERS frames", func() {
    75  		It("parses", func() {
    76  			data := quicvarint.Append(nil, 1) // type byte
    77  			data = quicvarint.Append(data, 0x1337)
    78  			fp := frameParser{r: bytes.NewReader(data)}
    79  			frame, err := fp.ParseNext()
    80  			Expect(err).ToNot(HaveOccurred())
    81  			Expect(frame).To(BeAssignableToTypeOf(&headersFrame{}))
    82  			Expect(frame.(*headersFrame).Length).To(Equal(uint64(0x1337)))
    83  		})
    84  
    85  		It("writes", func() {
    86  			data := (&headersFrame{Length: 0xdeadbeef}).Append(nil)
    87  			fp := frameParser{r: bytes.NewReader(data)}
    88  			frame, err := fp.ParseNext()
    89  			Expect(err).ToNot(HaveOccurred())
    90  			Expect(err).ToNot(HaveOccurred())
    91  			Expect(frame).To(BeAssignableToTypeOf(&headersFrame{}))
    92  			Expect(frame.(*headersFrame).Length).To(Equal(uint64(0xdeadbeef)))
    93  		})
    94  	})
    95  
    96  	Context("SETTINGS frames", func() {
    97  		It("parses", func() {
    98  			settings := quicvarint.Append(nil, 13)
    99  			settings = quicvarint.Append(settings, 37)
   100  			settings = quicvarint.Append(settings, 0xdead)
   101  			settings = quicvarint.Append(settings, 0xbeef)
   102  			data := quicvarint.Append(nil, 4) // type byte
   103  			data = quicvarint.Append(data, uint64(len(settings)))
   104  			data = append(data, settings...)
   105  			fp := frameParser{r: bytes.NewReader(data)}
   106  			frame, err := fp.ParseNext()
   107  			Expect(err).ToNot(HaveOccurred())
   108  			Expect(frame).To(BeAssignableToTypeOf(&settingsFrame{}))
   109  			sf := frame.(*settingsFrame)
   110  			Expect(sf.Other).To(HaveKeyWithValue(uint64(13), uint64(37)))
   111  			Expect(sf.Other).To(HaveKeyWithValue(uint64(0xdead), uint64(0xbeef)))
   112  		})
   113  
   114  		It("rejects duplicate settings", func() {
   115  			settings := quicvarint.Append(nil, 13)
   116  			settings = quicvarint.Append(settings, 37)
   117  			settings = quicvarint.Append(settings, 13)
   118  			settings = quicvarint.Append(settings, 38)
   119  			data := quicvarint.Append(nil, 4) // type byte
   120  			data = quicvarint.Append(data, uint64(len(settings)))
   121  			data = append(data, settings...)
   122  			fp := frameParser{r: bytes.NewReader(data)}
   123  			_, err := fp.ParseNext()
   124  			Expect(err).To(MatchError("duplicate setting: 13"))
   125  		})
   126  
   127  		It("writes", func() {
   128  			sf := &settingsFrame{Other: map[uint64]uint64{
   129  				1:  2,
   130  				99: 999,
   131  				13: 37,
   132  			}}
   133  			fp := frameParser{r: bytes.NewReader(sf.Append(nil))}
   134  			frame, err := fp.ParseNext()
   135  			Expect(err).ToNot(HaveOccurred())
   136  			Expect(frame).To(Equal(sf))
   137  		})
   138  
   139  		It("errors on EOF", func() {
   140  			sf := &settingsFrame{Other: map[uint64]uint64{
   141  				13:         37,
   142  				0xdeadbeef: 0xdecafbad,
   143  			}}
   144  			data := sf.Append(nil)
   145  			fp := frameParser{r: bytes.NewReader(data)}
   146  			_, err := fp.ParseNext()
   147  			Expect(err).ToNot(HaveOccurred())
   148  
   149  			for i := range data {
   150  				b := make([]byte, i)
   151  				copy(b, data[:i])
   152  				fp := frameParser{r: bytes.NewReader(b)}
   153  				_, err := fp.ParseNext()
   154  				Expect(err).To(MatchError(io.EOF))
   155  			}
   156  		})
   157  
   158  		Context("HTTP Datagrams", func() {
   159  			It("reads the SETTINGS_H3_DATAGRAM value", func() {
   160  				settings := quicvarint.Append(nil, settingDatagram)
   161  				settings = quicvarint.Append(settings, 1)
   162  				data := quicvarint.Append(nil, 4) // type byte
   163  				data = quicvarint.Append(data, uint64(len(settings)))
   164  				data = append(data, settings...)
   165  				fp := frameParser{r: bytes.NewReader(data)}
   166  				f, err := fp.ParseNext()
   167  				Expect(err).ToNot(HaveOccurred())
   168  				Expect(f).To(BeAssignableToTypeOf(&settingsFrame{}))
   169  				sf := f.(*settingsFrame)
   170  				Expect(sf.Datagram).To(BeTrue())
   171  			})
   172  
   173  			It("rejects duplicate SETTINGS_H3_DATAGRAM entries", func() {
   174  				settings := quicvarint.Append(nil, settingDatagram)
   175  				settings = quicvarint.Append(settings, 1)
   176  				settings = quicvarint.Append(settings, settingDatagram)
   177  				settings = quicvarint.Append(settings, 1)
   178  				data := quicvarint.Append(nil, 4) // type byte
   179  				data = quicvarint.Append(data, uint64(len(settings)))
   180  				data = append(data, settings...)
   181  				fp := frameParser{r: bytes.NewReader(data)}
   182  				_, err := fp.ParseNext()
   183  				Expect(err).To(MatchError(fmt.Sprintf("duplicate setting: %d", settingDatagram)))
   184  			})
   185  
   186  			It("rejects invalid values for the SETTINGS_H3_DATAGRAM entry", func() {
   187  				settings := quicvarint.Append(nil, settingDatagram)
   188  				settings = quicvarint.Append(settings, 1337)
   189  				data := quicvarint.Append(nil, 4) // type byte
   190  				data = quicvarint.Append(data, uint64(len(settings)))
   191  				data = append(data, settings...)
   192  				fp := frameParser{r: bytes.NewReader(data)}
   193  				_, err := fp.ParseNext()
   194  				Expect(err).To(MatchError("invalid value for SETTINGS_H3_DATAGRAM: 1337"))
   195  			})
   196  
   197  			It("writes the SETTINGS_H3_DATAGRAM setting", func() {
   198  				sf := &settingsFrame{Datagram: true}
   199  				fp := frameParser{r: bytes.NewReader(sf.Append(nil))}
   200  				frame, err := fp.ParseNext()
   201  				Expect(err).ToNot(HaveOccurred())
   202  				Expect(frame).To(Equal(sf))
   203  			})
   204  		})
   205  
   206  		Context("Extended Connect", func() {
   207  			It("reads the SETTINGS_ENABLE_CONNECT_PROTOCOL value", func() {
   208  				settings := quicvarint.Append(nil, settingExtendedConnect)
   209  				settings = quicvarint.Append(settings, 1)
   210  				data := quicvarint.Append(nil, 4) // type byte
   211  				data = quicvarint.Append(data, uint64(len(settings)))
   212  				data = append(data, settings...)
   213  				fp := frameParser{r: bytes.NewReader(data)}
   214  				f, err := fp.ParseNext()
   215  				Expect(err).ToNot(HaveOccurred())
   216  				Expect(f).To(BeAssignableToTypeOf(&settingsFrame{}))
   217  				sf := f.(*settingsFrame)
   218  				Expect(sf.ExtendedConnect).To(BeTrue())
   219  			})
   220  
   221  			It("rejects duplicate SETTINGS_ENABLE_CONNECT_PROTOCOL entries", func() {
   222  				settings := quicvarint.Append(nil, settingExtendedConnect)
   223  				settings = quicvarint.Append(settings, 1)
   224  				settings = quicvarint.Append(settings, settingExtendedConnect)
   225  				settings = quicvarint.Append(settings, 1)
   226  				data := quicvarint.Append(nil, 4) // type byte
   227  				data = quicvarint.Append(data, uint64(len(settings)))
   228  				data = append(data, settings...)
   229  				fp := frameParser{r: bytes.NewReader(data)}
   230  				_, err := fp.ParseNext()
   231  				Expect(err).To(MatchError(fmt.Sprintf("duplicate setting: %d", settingExtendedConnect)))
   232  			})
   233  
   234  			It("rejects invalid values for the SETTINGS_ENABLE_CONNECT_PROTOCOL entry", func() {
   235  				settings := quicvarint.Append(nil, settingExtendedConnect)
   236  				settings = quicvarint.Append(settings, 1337)
   237  				data := quicvarint.Append(nil, 4) // type byte
   238  				data = quicvarint.Append(data, uint64(len(settings)))
   239  				data = append(data, settings...)
   240  				fp := frameParser{r: bytes.NewReader(data)}
   241  				_, err := fp.ParseNext()
   242  				Expect(err).To(MatchError("invalid value for SETTINGS_ENABLE_CONNECT_PROTOCOL: 1337"))
   243  			})
   244  
   245  			It("writes the SETTINGS_ENABLE_CONNECT_PROTOCOL setting", func() {
   246  				sf := &settingsFrame{ExtendedConnect: true}
   247  				fp := frameParser{r: bytes.NewReader(sf.Append(nil))}
   248  				frame, err := fp.ParseNext()
   249  				Expect(err).ToNot(HaveOccurred())
   250  				Expect(frame).To(Equal(sf))
   251  			})
   252  		})
   253  	})
   254  
   255  	Context("hijacking", func() {
   256  		It("reads a frame without hijacking the stream", func() {
   257  			buf := bytes.NewBuffer(quicvarint.Append(nil, 1337))
   258  			customFrameContents := []byte("foobar")
   259  			buf.Write(customFrameContents)
   260  
   261  			var called bool
   262  			fp := frameParser{
   263  				r: buf,
   264  				unknownFrameHandler: func(ft FrameType, e error) (hijacked bool, err error) {
   265  					Expect(e).ToNot(HaveOccurred())
   266  					Expect(ft).To(BeEquivalentTo(1337))
   267  					called = true
   268  					b := make([]byte, 3)
   269  					_, err = io.ReadFull(buf, b)
   270  					Expect(err).ToNot(HaveOccurred())
   271  					Expect(string(b)).To(Equal("foo"))
   272  					return true, nil
   273  				},
   274  			}
   275  			_, err := fp.ParseNext()
   276  			Expect(err).To(MatchError(errHijacked))
   277  			Expect(called).To(BeTrue())
   278  		})
   279  
   280  		It("passes on errors that occur when reading the frame type", func() {
   281  			testErr := errors.New("test error")
   282  			var called bool
   283  			fp := frameParser{
   284  				r: errReader{err: testErr},
   285  				unknownFrameHandler: func(ft FrameType, e error) (hijacked bool, err error) {
   286  					Expect(e).To(MatchError(testErr))
   287  					Expect(ft).To(BeZero())
   288  					called = true
   289  					return true, nil
   290  				},
   291  			}
   292  			_, err := fp.ParseNext()
   293  			Expect(err).To(MatchError(errHijacked))
   294  			Expect(called).To(BeTrue())
   295  		})
   296  
   297  		It("reads a frame without hijacking the stream", func() {
   298  			b := quicvarint.Append(nil, 1337)
   299  			customFrameContents := []byte("custom frame")
   300  			b = quicvarint.Append(b, uint64(len(customFrameContents)))
   301  			b = append(b, customFrameContents...)
   302  			b = (&dataFrame{Length: 6}).Append(b)
   303  			b = append(b, []byte("foobar")...)
   304  
   305  			var called bool
   306  			fp := frameParser{
   307  				r: bytes.NewReader(b),
   308  				unknownFrameHandler: func(ft FrameType, e error) (hijacked bool, err error) {
   309  					Expect(e).ToNot(HaveOccurred())
   310  					Expect(ft).To(BeEquivalentTo(1337))
   311  					called = true
   312  					return false, nil
   313  				},
   314  			}
   315  			frame, err := fp.ParseNext()
   316  			Expect(err).ToNot(HaveOccurred())
   317  			Expect(frame).To(Equal(&dataFrame{Length: 6}))
   318  			Expect(called).To(BeTrue())
   319  		})
   320  	})
   321  })