github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/send_queue_test.go (about)

     1  package quic
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/daeuniverse/quic-go/internal/protocol"
     7  
     8  	. "github.com/onsi/ginkgo/v2"
     9  	. "github.com/onsi/gomega"
    10  	"go.uber.org/mock/gomock"
    11  )
    12  
    13  var _ = Describe("Send Queue", func() {
    14  	var q sender
    15  	var c *MockSendConn
    16  
    17  	BeforeEach(func() {
    18  		c = NewMockSendConn(mockCtrl)
    19  		q = newSendQueue(c)
    20  	})
    21  
    22  	getPacket := func(b []byte) *packetBuffer {
    23  		buf := getPacketBuffer()
    24  		buf.Data = buf.Data[:len(b)]
    25  		copy(buf.Data, b)
    26  		return buf
    27  	}
    28  
    29  	It("sends a packet", func() {
    30  		p := getPacket([]byte("foobar"))
    31  		q.Send(p, 10, protocol.ECT1) // make sure the packet size is passed through to the conn
    32  
    33  		written := make(chan struct{})
    34  		c.EXPECT().Write([]byte("foobar"), uint16(10), protocol.ECT1).Do(func([]byte, uint16, protocol.ECN) error { close(written); return nil })
    35  		done := make(chan struct{})
    36  		go func() {
    37  			defer GinkgoRecover()
    38  			q.Run()
    39  			close(done)
    40  		}()
    41  
    42  		Eventually(written).Should(BeClosed())
    43  		q.Close()
    44  		Eventually(done).Should(BeClosed())
    45  	})
    46  
    47  	It("panics when Send() is called although there's no space in the queue", func() {
    48  		for i := 0; i < sendQueueCapacity; i++ {
    49  			Expect(q.WouldBlock()).To(BeFalse())
    50  			q.Send(getPacket([]byte("foobar")), 6, protocol.ECNNon)
    51  		}
    52  		Expect(q.WouldBlock()).To(BeTrue())
    53  		Expect(func() { q.Send(getPacket([]byte("raboof")), 6, protocol.ECNNon) }).To(Panic())
    54  	})
    55  
    56  	It("signals when sending is possible again", func() {
    57  		Expect(q.WouldBlock()).To(BeFalse())
    58  		q.Send(getPacket([]byte("foobar1")), 6, protocol.ECNNon)
    59  		Consistently(q.Available()).ShouldNot(Receive())
    60  
    61  		// now start sending out packets. This should free up queue space.
    62  		c.EXPECT().Write(gomock.Any(), gomock.Any(), protocol.ECNNon).MinTimes(1).MaxTimes(2)
    63  		done := make(chan struct{})
    64  		go func() {
    65  			defer GinkgoRecover()
    66  			q.Run()
    67  			close(done)
    68  		}()
    69  
    70  		Eventually(q.Available()).Should(Receive())
    71  		Expect(q.WouldBlock()).To(BeFalse())
    72  		Expect(func() { q.Send(getPacket([]byte("foobar2")), 7, protocol.ECNNon) }).ToNot(Panic())
    73  
    74  		q.Close()
    75  		Eventually(done).Should(BeClosed())
    76  	})
    77  
    78  	It("signals when sending is possible again, when the first write succeeded", func() {
    79  		write := make(chan struct{}, 1)
    80  		written := make(chan struct{}, 100)
    81  		// now start sending out packets. This should free up queue space.
    82  		c.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func([]byte, uint16, protocol.ECN) error {
    83  			written <- struct{}{}
    84  			<-write
    85  			return nil
    86  		}).AnyTimes()
    87  		// allow the first packet to be sent immediately
    88  		write <- struct{}{}
    89  
    90  		done := make(chan struct{})
    91  		go func() {
    92  			defer GinkgoRecover()
    93  			q.Run()
    94  			close(done)
    95  		}()
    96  
    97  		q.Send(getPacket([]byte("foobar")), 6, protocol.ECNNon)
    98  		<-written
    99  
   100  		// now fill up the send queue
   101  		for i := 0; i < sendQueueCapacity; i++ {
   102  			Expect(q.WouldBlock()).To(BeFalse())
   103  			q.Send(getPacket([]byte("foobar")), 6, protocol.ECNNon)
   104  		}
   105  		// One more packet is queued when it's picked up by Run and written to the connection.
   106  		// In this test, it's blocked on write channel in the mocked Write call.
   107  		<-written
   108  		Eventually(q.WouldBlock()).Should(BeFalse())
   109  		q.Send(getPacket([]byte("foobar")), 6, protocol.ECNNon)
   110  
   111  		Expect(q.WouldBlock()).To(BeTrue())
   112  		Consistently(q.Available()).ShouldNot(Receive())
   113  		write <- struct{}{}
   114  		Eventually(q.Available()).Should(Receive())
   115  
   116  		// test shutdown
   117  		for i := 0; i < sendQueueCapacity; i++ {
   118  			write <- struct{}{}
   119  		}
   120  
   121  		q.Close()
   122  		Eventually(done).Should(BeClosed())
   123  	})
   124  
   125  	It("does not block pending send after the queue has stopped running", func() {
   126  		done := make(chan struct{})
   127  		go func() {
   128  			defer GinkgoRecover()
   129  			q.Run()
   130  			close(done)
   131  		}()
   132  
   133  		// the run loop exits if there is a write error
   134  		testErr := errors.New("test error")
   135  		c.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()).Return(testErr)
   136  		q.Send(getPacket([]byte("foobar")), 6, protocol.ECNNon)
   137  		Eventually(done).Should(BeClosed())
   138  
   139  		sent := make(chan struct{})
   140  		go func() {
   141  			defer GinkgoRecover()
   142  			q.Send(getPacket([]byte("raboof")), 6, protocol.ECNNon)
   143  			q.Send(getPacket([]byte("quux")), 4, protocol.ECNNon)
   144  			close(sent)
   145  		}()
   146  
   147  		Eventually(sent).Should(BeClosed())
   148  	})
   149  
   150  	It("blocks Close() until the packet has been sent out", func() {
   151  		written := make(chan []byte)
   152  		c.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(p []byte, _ uint16, _ protocol.ECN) error { written <- p; return nil })
   153  		done := make(chan struct{})
   154  		go func() {
   155  			defer GinkgoRecover()
   156  			q.Run()
   157  			close(done)
   158  		}()
   159  
   160  		q.Send(getPacket([]byte("foobar")), 6, protocol.ECNNon)
   161  
   162  		closed := make(chan struct{})
   163  		go func() {
   164  			defer GinkgoRecover()
   165  			q.Close()
   166  			close(closed)
   167  		}()
   168  
   169  		Consistently(closed).ShouldNot(BeClosed())
   170  		// now write the packet
   171  		Expect(written).To(Receive())
   172  		Eventually(done).Should(BeClosed())
   173  		Eventually(closed).Should(BeClosed())
   174  	})
   175  })