github.com/MerlinKodo/quic-go@v0.39.2/framer_test.go (about)

     1  package quic
     2  
     3  import (
     4  	"bytes"
     5  	"math/rand"
     6  
     7  	"github.com/MerlinKodo/quic-go/internal/ackhandler"
     8  	"github.com/MerlinKodo/quic-go/internal/protocol"
     9  	"github.com/MerlinKodo/quic-go/internal/wire"
    10  
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  	"go.uber.org/mock/gomock"
    14  )
    15  
    16  var _ = Describe("Framer", func() {
    17  	const (
    18  		id1 = protocol.StreamID(10)
    19  		id2 = protocol.StreamID(11)
    20  	)
    21  
    22  	var (
    23  		framer           framer
    24  		stream1, stream2 *MockSendStreamI
    25  		streamGetter     *MockStreamGetter
    26  		version          protocol.VersionNumber
    27  	)
    28  
    29  	BeforeEach(func() {
    30  		streamGetter = NewMockStreamGetter(mockCtrl)
    31  		stream1 = NewMockSendStreamI(mockCtrl)
    32  		stream1.EXPECT().StreamID().Return(protocol.StreamID(5)).AnyTimes()
    33  		stream2 = NewMockSendStreamI(mockCtrl)
    34  		stream2.EXPECT().StreamID().Return(protocol.StreamID(6)).AnyTimes()
    35  		framer = newFramer(streamGetter)
    36  	})
    37  
    38  	Context("handling control frames", func() {
    39  		It("adds control frames", func() {
    40  			mdf := &wire.MaxDataFrame{MaximumData: 0x42}
    41  			msf := &wire.MaxStreamsFrame{MaxStreamNum: 0x1337}
    42  			framer.QueueControlFrame(mdf)
    43  			framer.QueueControlFrame(msf)
    44  			frames, length := framer.AppendControlFrames(nil, 1000, protocol.Version1)
    45  			Expect(frames).To(HaveLen(2))
    46  			fs := []wire.Frame{frames[0].Frame, frames[1].Frame}
    47  			Expect(fs).To(ContainElement(mdf))
    48  			Expect(fs).To(ContainElement(msf))
    49  			Expect(length).To(Equal(mdf.Length(version) + msf.Length(version)))
    50  		})
    51  
    52  		It("says if it has data", func() {
    53  			Expect(framer.HasData()).To(BeFalse())
    54  			f := &wire.MaxDataFrame{MaximumData: 0x42}
    55  			framer.QueueControlFrame(f)
    56  			Expect(framer.HasData()).To(BeTrue())
    57  			frames, _ := framer.AppendControlFrames(nil, 1000, protocol.Version1)
    58  			Expect(frames).To(HaveLen(1))
    59  			Expect(framer.HasData()).To(BeFalse())
    60  		})
    61  
    62  		It("appends to the slice given", func() {
    63  			ping := &wire.PingFrame{}
    64  			mdf := &wire.MaxDataFrame{MaximumData: 0x42}
    65  			framer.QueueControlFrame(mdf)
    66  			frames, length := framer.AppendControlFrames([]ackhandler.Frame{{Frame: ping}}, 1000, protocol.Version1)
    67  			Expect(frames).To(HaveLen(2))
    68  			Expect(frames[0].Frame).To(Equal(ping))
    69  			Expect(frames[1].Frame).To(Equal(mdf))
    70  			Expect(length).To(Equal(mdf.Length(version)))
    71  		})
    72  
    73  		It("adds the right number of frames", func() {
    74  			maxSize := protocol.ByteCount(1000)
    75  			bf := &wire.DataBlockedFrame{MaximumData: 0x1337}
    76  			bfLen := bf.Length(version)
    77  			numFrames := int(maxSize / bfLen) // max number of frames that fit into maxSize
    78  			for i := 0; i < numFrames+1; i++ {
    79  				framer.QueueControlFrame(bf)
    80  			}
    81  			frames, length := framer.AppendControlFrames(nil, maxSize, protocol.Version1)
    82  			Expect(frames).To(HaveLen(numFrames))
    83  			Expect(length).To(BeNumerically(">", maxSize-bfLen))
    84  			frames, length = framer.AppendControlFrames(nil, maxSize, protocol.Version1)
    85  			Expect(frames).To(HaveLen(1))
    86  			Expect(length).To(Equal(bfLen))
    87  		})
    88  
    89  		It("drops *_BLOCKED frames when 0-RTT is rejected", func() {
    90  			ping := &wire.PingFrame{}
    91  			ncid := &wire.NewConnectionIDFrame{
    92  				SequenceNumber: 10,
    93  				ConnectionID:   protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}),
    94  			}
    95  			frames := []wire.Frame{
    96  				&wire.DataBlockedFrame{MaximumData: 1337},
    97  				&wire.StreamDataBlockedFrame{StreamID: 42, MaximumStreamData: 1337},
    98  				&wire.StreamsBlockedFrame{StreamLimit: 13},
    99  				ping,
   100  				ncid,
   101  			}
   102  			rand.Shuffle(len(frames), func(i, j int) { frames[i], frames[j] = frames[j], frames[i] })
   103  			for _, f := range frames {
   104  				framer.QueueControlFrame(f)
   105  			}
   106  			Expect(framer.Handle0RTTRejection()).To(Succeed())
   107  			fs, length := framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1)
   108  			Expect(fs).To(HaveLen(2))
   109  			Expect(length).To(Equal(ping.Length(version) + ncid.Length(version)))
   110  		})
   111  	})
   112  
   113  	Context("popping STREAM frames", func() {
   114  		It("returns nil when popping an empty framer", func() {
   115  			Expect(framer.AppendStreamFrames(nil, 1000, protocol.Version1)).To(BeEmpty())
   116  		})
   117  
   118  		It("returns STREAM frames", func() {
   119  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
   120  			f := &wire.StreamFrame{
   121  				StreamID:       id1,
   122  				Data:           []byte("foobar"),
   123  				Offset:         42,
   124  				DataLenPresent: true,
   125  			}
   126  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false)
   127  			framer.AddActiveStream(id1)
   128  			fs, length := framer.AppendStreamFrames(nil, 1000, protocol.Version1)
   129  			Expect(fs).To(HaveLen(1))
   130  			Expect(fs[0].Frame.DataLenPresent).To(BeFalse())
   131  			Expect(length).To(Equal(f.Length(version)))
   132  		})
   133  
   134  		It("says if it has data", func() {
   135  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil).Times(2)
   136  			Expect(framer.HasData()).To(BeFalse())
   137  			framer.AddActiveStream(id1)
   138  			Expect(framer.HasData()).To(BeTrue())
   139  			f1 := &wire.StreamFrame{StreamID: id1, Data: []byte("foo")}
   140  			f2 := &wire.StreamFrame{StreamID: id1, Data: []byte("bar")}
   141  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f1}, true, true)
   142  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f2}, true, false)
   143  			frames, _ := framer.AppendStreamFrames(nil, protocol.MaxByteCount, protocol.Version1)
   144  			Expect(frames).To(HaveLen(1))
   145  			Expect(frames[0].Frame).To(Equal(f1))
   146  			Expect(framer.HasData()).To(BeTrue())
   147  			frames, _ = framer.AppendStreamFrames(nil, protocol.MaxByteCount, protocol.Version1)
   148  			Expect(frames).To(HaveLen(1))
   149  			Expect(frames[0].Frame).To(Equal(f2))
   150  			Expect(framer.HasData()).To(BeFalse())
   151  		})
   152  
   153  		It("appends to a frame slice", func() {
   154  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
   155  			f := &wire.StreamFrame{
   156  				StreamID:       id1,
   157  				Data:           []byte("foobar"),
   158  				DataLenPresent: true,
   159  			}
   160  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false)
   161  			framer.AddActiveStream(id1)
   162  			f0 := ackhandler.StreamFrame{Frame: &wire.StreamFrame{StreamID: 9999}}
   163  			frames := []ackhandler.StreamFrame{f0}
   164  			fs, length := framer.AppendStreamFrames(frames, 1000, protocol.Version1)
   165  			Expect(fs).To(HaveLen(2))
   166  			Expect(fs[0]).To(Equal(f0))
   167  			Expect(fs[1].Frame.Data).To(Equal([]byte("foobar")))
   168  			Expect(fs[1].Frame.DataLenPresent).To(BeFalse())
   169  			Expect(length).To(Equal(f.Length(version)))
   170  		})
   171  
   172  		It("skips a stream that was reported active, but was completed shortly after", func() {
   173  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(nil, nil)
   174  			streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
   175  			f := &wire.StreamFrame{
   176  				StreamID:       id2,
   177  				Data:           []byte("foobar"),
   178  				DataLenPresent: true,
   179  			}
   180  			stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false)
   181  			framer.AddActiveStream(id1)
   182  			framer.AddActiveStream(id2)
   183  			frames, _ := framer.AppendStreamFrames(nil, 1000, protocol.Version1)
   184  			Expect(frames).To(HaveLen(1))
   185  			Expect(frames[0].Frame).To(Equal(f))
   186  		})
   187  
   188  		It("skips a stream that was reported active, but doesn't have any data", func() {
   189  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
   190  			streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
   191  			f := &wire.StreamFrame{
   192  				StreamID:       id2,
   193  				Data:           []byte("foobar"),
   194  				DataLenPresent: true,
   195  			}
   196  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{}, false, false)
   197  			stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false)
   198  			framer.AddActiveStream(id1)
   199  			framer.AddActiveStream(id2)
   200  			frames, _ := framer.AppendStreamFrames(nil, 1000, protocol.Version1)
   201  			Expect(frames).To(HaveLen(1))
   202  			Expect(frames[0].Frame).To(Equal(f))
   203  		})
   204  
   205  		It("pops from a stream multiple times, if it has enough data", func() {
   206  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil).Times(2)
   207  			f1 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobar")}
   208  			f2 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobaz")}
   209  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f1}, true, true)
   210  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f2}, true, false)
   211  			framer.AddActiveStream(id1) // only add it once
   212  			frames, _ := framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1)
   213  			Expect(frames).To(HaveLen(1))
   214  			Expect(frames[0].Frame).To(Equal(f1))
   215  			frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1)
   216  			Expect(frames).To(HaveLen(1))
   217  			Expect(frames[0].Frame).To(Equal(f2))
   218  			// no further calls to popStreamFrame, after popStreamFrame said there's no more data
   219  			frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1)
   220  			Expect(frames).To(BeNil())
   221  		})
   222  
   223  		It("re-queues a stream at the end, if it has enough data", func() {
   224  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil).Times(2)
   225  			streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
   226  			f11 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobar")}
   227  			f12 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobaz")}
   228  			f2 := &wire.StreamFrame{StreamID: id2, Data: []byte("raboof")}
   229  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f11}, true, true)
   230  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f12}, true, false)
   231  			stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f2}, true, false)
   232  			framer.AddActiveStream(id1) // only add it once
   233  			framer.AddActiveStream(id2)
   234  			// first a frame from stream 1
   235  			frames, _ := framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1)
   236  			Expect(frames).To(HaveLen(1))
   237  			Expect(frames[0].Frame).To(Equal(f11))
   238  			// then a frame from stream 2
   239  			frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1)
   240  			Expect(frames).To(HaveLen(1))
   241  			Expect(frames[0].Frame).To(Equal(f2))
   242  			// then another frame from stream 1
   243  			frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1)
   244  			Expect(frames).To(HaveLen(1))
   245  			Expect(frames[0].Frame).To(Equal(f12))
   246  		})
   247  
   248  		It("only dequeues data from each stream once per packet", func() {
   249  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
   250  			streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
   251  			f1 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobar")}
   252  			f2 := &wire.StreamFrame{StreamID: id2, Data: []byte("raboof")}
   253  			// both streams have more data, and will be re-queued
   254  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f1}, true, true)
   255  			stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f2}, true, true)
   256  			framer.AddActiveStream(id1)
   257  			framer.AddActiveStream(id2)
   258  			frames, length := framer.AppendStreamFrames(nil, 1000, protocol.Version1)
   259  			Expect(frames).To(HaveLen(2))
   260  			Expect(frames[0].Frame).To(Equal(f1))
   261  			Expect(frames[1].Frame).To(Equal(f2))
   262  			Expect(length).To(Equal(f1.Length(version) + f2.Length(version)))
   263  		})
   264  
   265  		It("returns multiple normal frames in the order they were reported active", func() {
   266  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
   267  			streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
   268  			f1 := &wire.StreamFrame{Data: []byte("foobar")}
   269  			f2 := &wire.StreamFrame{Data: []byte("foobaz")}
   270  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f1}, true, false)
   271  			stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f2}, true, false)
   272  			framer.AddActiveStream(id2)
   273  			framer.AddActiveStream(id1)
   274  			frames, _ := framer.AppendStreamFrames(nil, 1000, protocol.Version1)
   275  			Expect(frames).To(HaveLen(2))
   276  			Expect(frames[0].Frame).To(Equal(f2))
   277  			Expect(frames[1].Frame).To(Equal(f1))
   278  		})
   279  
   280  		It("only asks a stream for data once, even if it was reported active multiple times", func() {
   281  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
   282  			f := &wire.StreamFrame{Data: []byte("foobar")}
   283  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false) // only one call to this function
   284  			framer.AddActiveStream(id1)
   285  			framer.AddActiveStream(id1)
   286  			frames, _ := framer.AppendStreamFrames(nil, 1000, protocol.Version1)
   287  			Expect(frames).To(HaveLen(1))
   288  		})
   289  
   290  		It("does not pop empty frames", func() {
   291  			fs, length := framer.AppendStreamFrames(nil, 500, protocol.Version1)
   292  			Expect(fs).To(BeEmpty())
   293  			Expect(length).To(BeZero())
   294  		})
   295  
   296  		It("pops maximum size STREAM frames", func() {
   297  			for i := protocol.MinStreamFrameSize; i < 2000; i++ {
   298  				streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
   299  				stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).DoAndReturn(func(size protocol.ByteCount, v protocol.VersionNumber) (ackhandler.StreamFrame, bool, bool) {
   300  					f := &wire.StreamFrame{
   301  						StreamID:       id1,
   302  						DataLenPresent: true,
   303  					}
   304  					f.Data = make([]byte, f.MaxDataLen(size, v))
   305  					Expect(f.Length(version)).To(Equal(size))
   306  					return ackhandler.StreamFrame{Frame: f}, true, false
   307  				})
   308  				framer.AddActiveStream(id1)
   309  				frames, _ := framer.AppendStreamFrames(nil, i, protocol.Version1)
   310  				Expect(frames).To(HaveLen(1))
   311  				f := frames[0].Frame
   312  				Expect(f.DataLenPresent).To(BeFalse())
   313  				Expect(f.Length(version)).To(Equal(i))
   314  			}
   315  		})
   316  
   317  		It("pops multiple STREAM frames", func() {
   318  			for i := 2 * protocol.MinStreamFrameSize; i < 2000; i++ {
   319  				streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
   320  				streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil)
   321  				stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).DoAndReturn(func(size protocol.ByteCount, v protocol.VersionNumber) (ackhandler.StreamFrame, bool, bool) {
   322  					f := &wire.StreamFrame{
   323  						StreamID:       id2,
   324  						DataLenPresent: true,
   325  					}
   326  					f.Data = make([]byte, f.MaxDataLen(protocol.MinStreamFrameSize, v))
   327  					return ackhandler.StreamFrame{Frame: f}, true, false
   328  				})
   329  				stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).DoAndReturn(func(size protocol.ByteCount, v protocol.VersionNumber) (ackhandler.StreamFrame, bool, bool) {
   330  					f := &wire.StreamFrame{
   331  						StreamID:       id2,
   332  						DataLenPresent: true,
   333  					}
   334  					f.Data = make([]byte, f.MaxDataLen(size, v))
   335  					Expect(f.Length(version)).To(Equal(size))
   336  					return ackhandler.StreamFrame{Frame: f}, true, false
   337  				})
   338  				framer.AddActiveStream(id1)
   339  				framer.AddActiveStream(id2)
   340  				frames, _ := framer.AppendStreamFrames(nil, i, protocol.Version1)
   341  				Expect(frames).To(HaveLen(2))
   342  				f1 := frames[0].Frame
   343  				f2 := frames[1].Frame
   344  				Expect(f1.DataLenPresent).To(BeTrue())
   345  				Expect(f2.DataLenPresent).To(BeFalse())
   346  				Expect(f1.Length(version) + f2.Length(version)).To(Equal(i))
   347  			}
   348  		})
   349  
   350  		It("pops frames that when asked for the the minimum STREAM frame size", func() {
   351  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
   352  			f := &wire.StreamFrame{Data: []byte("foobar")}
   353  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false)
   354  			framer.AddActiveStream(id1)
   355  			framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1)
   356  		})
   357  
   358  		It("does not pop frames smaller than the minimum size", func() {
   359  			// don't expect a call to PopStreamFrame()
   360  			framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize-1, protocol.Version1)
   361  		})
   362  
   363  		It("stops iterating when the remaining size is smaller than the minimum STREAM frame size", func() {
   364  			streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil)
   365  			// pop a frame such that the remaining size is one byte less than the minimum STREAM frame size
   366  			f := &wire.StreamFrame{
   367  				StreamID:       id1,
   368  				Data:           bytes.Repeat([]byte("f"), int(500-protocol.MinStreamFrameSize)),
   369  				DataLenPresent: true,
   370  			}
   371  			stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false)
   372  			framer.AddActiveStream(id1)
   373  			fs, length := framer.AppendStreamFrames(nil, 500, protocol.Version1)
   374  			Expect(fs).To(HaveLen(1))
   375  			Expect(fs[0].Frame).To(Equal(f))
   376  			Expect(length).To(Equal(f.Length(version)))
   377  		})
   378  
   379  		It("drops all STREAM frames when 0-RTT is rejected", func() {
   380  			framer.AddActiveStream(id1)
   381  			Expect(framer.Handle0RTTRejection()).To(Succeed())
   382  			fs, length := framer.AppendStreamFrames(nil, protocol.MaxByteCount, protocol.Version1)
   383  			Expect(fs).To(BeEmpty())
   384  			Expect(length).To(BeZero())
   385  		})
   386  	})
   387  })