github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/send_queue_test.go (about) 1 package quic 2 3 import ( 4 "errors" 5 6 "github.com/apernet/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 })