github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/integrationtests/self/deadline_test.go (about) 1 package self_test 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net" 8 "time" 9 10 "github.com/apernet/quic-go" 11 12 . "github.com/onsi/ginkgo/v2" 13 . "github.com/onsi/gomega" 14 ) 15 16 var _ = Describe("Stream deadline tests", func() { 17 setup := func() (serverStr, clientStr quic.Stream, close func()) { 18 server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) 19 Expect(err).ToNot(HaveOccurred()) 20 strChan := make(chan quic.SendStream) 21 go func() { 22 defer GinkgoRecover() 23 conn, err := server.Accept(context.Background()) 24 Expect(err).ToNot(HaveOccurred()) 25 str, err := conn.AcceptStream(context.Background()) 26 Expect(err).ToNot(HaveOccurred()) 27 _, err = str.Read([]byte{0}) 28 Expect(err).ToNot(HaveOccurred()) 29 strChan <- str 30 }() 31 32 conn, err := quic.DialAddr( 33 context.Background(), 34 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 35 getTLSClientConfig(), 36 getQuicConfig(nil), 37 ) 38 Expect(err).ToNot(HaveOccurred()) 39 clientStr, err = conn.OpenStream() 40 Expect(err).ToNot(HaveOccurred()) 41 _, err = clientStr.Write([]byte{0}) // need to write one byte so the server learns about the stream 42 Expect(err).ToNot(HaveOccurred()) 43 Eventually(strChan).Should(Receive(&serverStr)) 44 return serverStr, clientStr, func() { 45 Expect(server.Close()).To(Succeed()) 46 Expect(conn.CloseWithError(0, "")).To(Succeed()) 47 } 48 } 49 50 Context("read deadlines", func() { 51 It("completes a transfer when the deadline is set", func() { 52 serverStr, clientStr, closeFn := setup() 53 defer closeFn() 54 55 const timeout = time.Millisecond 56 done := make(chan struct{}) 57 go func() { 58 defer GinkgoRecover() 59 _, err := serverStr.Write(PRDataLong) 60 Expect(err).ToNot(HaveOccurred()) 61 close(done) 62 }() 63 64 var bytesRead int 65 var timeoutCounter int 66 buf := make([]byte, 1<<10) 67 data := make([]byte, len(PRDataLong)) 68 clientStr.SetReadDeadline(time.Now().Add(timeout)) 69 for bytesRead < len(PRDataLong) { 70 n, err := clientStr.Read(buf) 71 if nerr, ok := err.(net.Error); ok && nerr.Timeout() { 72 timeoutCounter++ 73 clientStr.SetReadDeadline(time.Now().Add(timeout)) 74 } else { 75 Expect(err).ToNot(HaveOccurred()) 76 } 77 copy(data[bytesRead:], buf[:n]) 78 bytesRead += n 79 } 80 Expect(data).To(Equal(PRDataLong)) 81 // make sure the test actually worked and Read actually ran into the deadline a few times 82 Expect(timeoutCounter).To(BeNumerically(">=", 10)) 83 Eventually(done).Should(BeClosed()) 84 }) 85 86 It("completes a transfer when the deadline is set concurrently", func() { 87 serverStr, clientStr, closeFn := setup() 88 defer closeFn() 89 90 const timeout = time.Millisecond 91 go func() { 92 defer GinkgoRecover() 93 _, err := serverStr.Write(PRDataLong) 94 Expect(err).ToNot(HaveOccurred()) 95 }() 96 97 var bytesRead int 98 var timeoutCounter int 99 buf := make([]byte, 1<<10) 100 data := make([]byte, len(PRDataLong)) 101 clientStr.SetReadDeadline(time.Now().Add(timeout)) 102 deadlineDone := make(chan struct{}) 103 received := make(chan struct{}) 104 go func() { 105 defer close(deadlineDone) 106 for { 107 select { 108 case <-received: 109 return 110 default: 111 time.Sleep(timeout) 112 } 113 clientStr.SetReadDeadline(time.Now().Add(timeout)) 114 } 115 }() 116 117 for bytesRead < len(PRDataLong) { 118 n, err := clientStr.Read(buf) 119 if nerr, ok := err.(net.Error); ok && nerr.Timeout() { 120 timeoutCounter++ 121 } else { 122 Expect(err).ToNot(HaveOccurred()) 123 } 124 copy(data[bytesRead:], buf[:n]) 125 bytesRead += n 126 } 127 close(received) 128 Expect(data).To(Equal(PRDataLong)) 129 // make sure the test actually worked an Read actually ran into the deadline a few times 130 Expect(timeoutCounter).To(BeNumerically(">=", 10)) 131 Eventually(deadlineDone).Should(BeClosed()) 132 }) 133 }) 134 135 Context("write deadlines", func() { 136 It("completes a transfer when the deadline is set", func() { 137 serverStr, clientStr, closeFn := setup() 138 defer closeFn() 139 140 const timeout = time.Millisecond 141 done := make(chan struct{}) 142 go func() { 143 defer GinkgoRecover() 144 data, err := io.ReadAll(serverStr) 145 Expect(err).ToNot(HaveOccurred()) 146 Expect(data).To(Equal(PRDataLong)) 147 close(done) 148 }() 149 150 var bytesWritten int 151 var timeoutCounter int 152 clientStr.SetWriteDeadline(time.Now().Add(timeout)) 153 for bytesWritten < len(PRDataLong) { 154 n, err := clientStr.Write(PRDataLong[bytesWritten:]) 155 if nerr, ok := err.(net.Error); ok && nerr.Timeout() { 156 timeoutCounter++ 157 clientStr.SetWriteDeadline(time.Now().Add(timeout)) 158 } else { 159 Expect(err).ToNot(HaveOccurred()) 160 } 161 bytesWritten += n 162 } 163 clientStr.Close() 164 // make sure the test actually worked an Read actually ran into the deadline a few times 165 Expect(timeoutCounter).To(BeNumerically(">=", 10)) 166 Eventually(done).Should(BeClosed()) 167 }) 168 169 It("completes a transfer when the deadline is set concurrently", func() { 170 serverStr, clientStr, closeFn := setup() 171 defer closeFn() 172 173 const timeout = time.Millisecond 174 readDone := make(chan struct{}) 175 go func() { 176 defer GinkgoRecover() 177 data, err := io.ReadAll(serverStr) 178 Expect(err).ToNot(HaveOccurred()) 179 Expect(data).To(Equal(PRDataLong)) 180 close(readDone) 181 }() 182 183 clientStr.SetWriteDeadline(time.Now().Add(timeout)) 184 deadlineDone := make(chan struct{}) 185 go func() { 186 defer close(deadlineDone) 187 for { 188 select { 189 case <-readDone: 190 return 191 default: 192 time.Sleep(timeout) 193 } 194 clientStr.SetWriteDeadline(time.Now().Add(timeout)) 195 } 196 }() 197 198 var bytesWritten int 199 var timeoutCounter int 200 clientStr.SetWriteDeadline(time.Now().Add(timeout)) 201 for bytesWritten < len(PRDataLong) { 202 n, err := clientStr.Write(PRDataLong[bytesWritten:]) 203 if nerr, ok := err.(net.Error); ok && nerr.Timeout() { 204 timeoutCounter++ 205 } else { 206 Expect(err).ToNot(HaveOccurred()) 207 } 208 bytesWritten += n 209 } 210 clientStr.Close() 211 // make sure the test actually worked an Read actually ran into the deadline a few times 212 Expect(timeoutCounter).To(BeNumerically(">=", 10)) 213 Eventually(readDone).Should(BeClosed()) 214 Eventually(deadlineDone).Should(BeClosed()) 215 }) 216 }) 217 })