github.com/tumi8/quic-go@v0.37.4-tum/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/tumi8/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() (*quic.Listener, quic.Stream, quic.Stream) {
    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  		var serverStr quic.Stream
    44  		Eventually(strChan).Should(Receive(&serverStr))
    45  		return server, serverStr, clientStr
    46  	}
    47  
    48  	Context("read deadlines", func() {
    49  		It("completes a transfer when the deadline is set", func() {
    50  			server, serverStr, clientStr := setup()
    51  			defer server.Close()
    52  
    53  			const timeout = time.Millisecond
    54  			done := make(chan struct{})
    55  			go func() {
    56  				defer GinkgoRecover()
    57  				_, err := serverStr.Write(PRDataLong)
    58  				Expect(err).ToNot(HaveOccurred())
    59  				close(done)
    60  			}()
    61  
    62  			var bytesRead int
    63  			var timeoutCounter int
    64  			buf := make([]byte, 1<<10)
    65  			data := make([]byte, len(PRDataLong))
    66  			clientStr.SetReadDeadline(time.Now().Add(timeout))
    67  			for bytesRead < len(PRDataLong) {
    68  				n, err := clientStr.Read(buf)
    69  				if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
    70  					timeoutCounter++
    71  					clientStr.SetReadDeadline(time.Now().Add(timeout))
    72  				} else {
    73  					Expect(err).ToNot(HaveOccurred())
    74  				}
    75  				copy(data[bytesRead:], buf[:n])
    76  				bytesRead += n
    77  			}
    78  			Expect(data).To(Equal(PRDataLong))
    79  			// make sure the test actually worked and Read actually ran into the deadline a few times
    80  			Expect(timeoutCounter).To(BeNumerically(">=", 10))
    81  			Eventually(done).Should(BeClosed())
    82  		})
    83  
    84  		It("completes a transfer when the deadline is set concurrently", func() {
    85  			server, serverStr, clientStr := setup()
    86  			defer server.Close()
    87  
    88  			const timeout = time.Millisecond
    89  			go func() {
    90  				defer GinkgoRecover()
    91  				_, err := serverStr.Write(PRDataLong)
    92  				Expect(err).ToNot(HaveOccurred())
    93  			}()
    94  
    95  			var bytesRead int
    96  			var timeoutCounter int
    97  			buf := make([]byte, 1<<10)
    98  			data := make([]byte, len(PRDataLong))
    99  			clientStr.SetReadDeadline(time.Now().Add(timeout))
   100  			deadlineDone := make(chan struct{})
   101  			received := make(chan struct{})
   102  			go func() {
   103  				defer close(deadlineDone)
   104  				for {
   105  					select {
   106  					case <-received:
   107  						return
   108  					default:
   109  						time.Sleep(timeout)
   110  					}
   111  					clientStr.SetReadDeadline(time.Now().Add(timeout))
   112  				}
   113  			}()
   114  
   115  			for bytesRead < len(PRDataLong) {
   116  				n, err := clientStr.Read(buf)
   117  				if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
   118  					timeoutCounter++
   119  				} else {
   120  					Expect(err).ToNot(HaveOccurred())
   121  				}
   122  				copy(data[bytesRead:], buf[:n])
   123  				bytesRead += n
   124  			}
   125  			close(received)
   126  			Expect(data).To(Equal(PRDataLong))
   127  			// make sure the test actually worked an Read actually ran into the deadline a few times
   128  			Expect(timeoutCounter).To(BeNumerically(">=", 10))
   129  			Eventually(deadlineDone).Should(BeClosed())
   130  		})
   131  	})
   132  
   133  	Context("write deadlines", func() {
   134  		It("completes a transfer when the deadline is set", func() {
   135  			server, serverStr, clientStr := setup()
   136  			defer server.Close()
   137  
   138  			const timeout = time.Millisecond
   139  			done := make(chan struct{})
   140  			go func() {
   141  				defer GinkgoRecover()
   142  				data, err := io.ReadAll(serverStr)
   143  				Expect(err).ToNot(HaveOccurred())
   144  				Expect(data).To(Equal(PRDataLong))
   145  				close(done)
   146  			}()
   147  
   148  			var bytesWritten int
   149  			var timeoutCounter int
   150  			clientStr.SetWriteDeadline(time.Now().Add(timeout))
   151  			for bytesWritten < len(PRDataLong) {
   152  				n, err := clientStr.Write(PRDataLong[bytesWritten:])
   153  				if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
   154  					timeoutCounter++
   155  					clientStr.SetWriteDeadline(time.Now().Add(timeout))
   156  				} else {
   157  					Expect(err).ToNot(HaveOccurred())
   158  				}
   159  				bytesWritten += n
   160  			}
   161  			clientStr.Close()
   162  			// make sure the test actually worked an Read actually ran into the deadline a few times
   163  			Expect(timeoutCounter).To(BeNumerically(">=", 10))
   164  			Eventually(done).Should(BeClosed())
   165  		})
   166  
   167  		It("completes a transfer when the deadline is set concurrently", func() {
   168  			server, serverStr, clientStr := setup()
   169  			defer server.Close()
   170  
   171  			const timeout = time.Millisecond
   172  			readDone := make(chan struct{})
   173  			go func() {
   174  				defer GinkgoRecover()
   175  				data, err := io.ReadAll(serverStr)
   176  				Expect(err).ToNot(HaveOccurred())
   177  				Expect(data).To(Equal(PRDataLong))
   178  				close(readDone)
   179  			}()
   180  
   181  			clientStr.SetWriteDeadline(time.Now().Add(timeout))
   182  			deadlineDone := make(chan struct{})
   183  			go func() {
   184  				defer close(deadlineDone)
   185  				for {
   186  					select {
   187  					case <-readDone:
   188  						return
   189  					default:
   190  						time.Sleep(timeout)
   191  					}
   192  					clientStr.SetWriteDeadline(time.Now().Add(timeout))
   193  				}
   194  			}()
   195  
   196  			var bytesWritten int
   197  			var timeoutCounter int
   198  			clientStr.SetWriteDeadline(time.Now().Add(timeout))
   199  			for bytesWritten < len(PRDataLong) {
   200  				n, err := clientStr.Write(PRDataLong[bytesWritten:])
   201  				if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
   202  					timeoutCounter++
   203  				} else {
   204  					Expect(err).ToNot(HaveOccurred())
   205  				}
   206  				bytesWritten += n
   207  			}
   208  			clientStr.Close()
   209  			// make sure the test actually worked an Read actually ran into the deadline a few times
   210  			Expect(timeoutCounter).To(BeNumerically(">=", 10))
   211  			Eventually(readDone).Should(BeClosed())
   212  			Eventually(deadlineDone).Should(BeClosed())
   213  		})
   214  	})
   215  })