github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/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/daeuniverse/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  })