golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/idle_test.go (about)

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build go1.21
     6  
     7  package quic
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"fmt"
    13  	"testing"
    14  	"time"
    15  )
    16  
    17  func TestHandshakeTimeoutExpiresServer(t *testing.T) {
    18  	const timeout = 5 * time.Second
    19  	tc := newTestConn(t, serverSide, func(c *Config) {
    20  		c.HandshakeTimeout = timeout
    21  	})
    22  	tc.ignoreFrame(frameTypeAck)
    23  	tc.ignoreFrame(frameTypeNewConnectionID)
    24  	tc.writeFrames(packetTypeInitial,
    25  		debugFrameCrypto{
    26  			data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
    27  		})
    28  	// Server starts its end of the handshake.
    29  	// Client acks these packets to avoid starting the PTO timer.
    30  	tc.wantFrameType("server sends Initial CRYPTO flight",
    31  		packetTypeInitial, debugFrameCrypto{})
    32  	tc.writeAckForAll()
    33  	tc.wantFrameType("server sends Handshake CRYPTO flight",
    34  		packetTypeHandshake, debugFrameCrypto{})
    35  	tc.writeAckForAll()
    36  
    37  	if got, want := tc.timerDelay(), timeout; got != want {
    38  		t.Errorf("connection timer = %v, want %v (handshake timeout)", got, want)
    39  	}
    40  
    41  	// Client sends a packet, but this does not extend the handshake timer.
    42  	tc.advance(1 * time.Second)
    43  	tc.writeFrames(packetTypeHandshake, debugFrameCrypto{
    44  		data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake][:1], // partial data
    45  	})
    46  	tc.wantIdle("handshake is not complete")
    47  
    48  	tc.advance(timeout - 1*time.Second)
    49  	tc.wantFrame("server closes connection after handshake timeout",
    50  		packetTypeHandshake, debugFrameConnectionCloseTransport{
    51  			code: errConnectionRefused,
    52  		})
    53  }
    54  
    55  func TestHandshakeTimeoutExpiresClient(t *testing.T) {
    56  	const timeout = 5 * time.Second
    57  	tc := newTestConn(t, clientSide, func(c *Config) {
    58  		c.HandshakeTimeout = timeout
    59  	})
    60  	tc.ignoreFrame(frameTypeAck)
    61  	tc.ignoreFrame(frameTypeNewConnectionID)
    62  	// Start the handshake.
    63  	// The client always sets a PTO timer until it gets an ack for a handshake packet
    64  	// or confirms the handshake, so proceed far enough through the handshake to
    65  	// let us not worry about PTO.
    66  	tc.wantFrameType("client sends Initial CRYPTO flight",
    67  		packetTypeInitial, debugFrameCrypto{})
    68  	tc.writeAckForAll()
    69  	tc.writeFrames(packetTypeInitial,
    70  		debugFrameCrypto{
    71  			data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
    72  		})
    73  	tc.writeFrames(packetTypeHandshake,
    74  		debugFrameCrypto{
    75  			data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
    76  		})
    77  	tc.wantFrameType("client sends Handshake CRYPTO flight",
    78  		packetTypeHandshake, debugFrameCrypto{})
    79  	tc.writeAckForAll()
    80  	tc.wantIdle("client is waiting for end of handshake")
    81  
    82  	if got, want := tc.timerDelay(), timeout; got != want {
    83  		t.Errorf("connection timer = %v, want %v (handshake timeout)", got, want)
    84  	}
    85  	tc.advance(timeout)
    86  	tc.wantFrame("client closes connection after handshake timeout",
    87  		packetTypeHandshake, debugFrameConnectionCloseTransport{
    88  			code: errConnectionRefused,
    89  		})
    90  }
    91  
    92  func TestIdleTimeoutExpires(t *testing.T) {
    93  	for _, test := range []struct {
    94  		localMaxIdleTimeout time.Duration
    95  		peerMaxIdleTimeout  time.Duration
    96  		wantTimeout         time.Duration
    97  	}{{
    98  		localMaxIdleTimeout: 10 * time.Second,
    99  		peerMaxIdleTimeout:  20 * time.Second,
   100  		wantTimeout:         10 * time.Second,
   101  	}, {
   102  		localMaxIdleTimeout: 20 * time.Second,
   103  		peerMaxIdleTimeout:  10 * time.Second,
   104  		wantTimeout:         10 * time.Second,
   105  	}, {
   106  		localMaxIdleTimeout: 0,
   107  		peerMaxIdleTimeout:  10 * time.Second,
   108  		wantTimeout:         10 * time.Second,
   109  	}, {
   110  		localMaxIdleTimeout: 10 * time.Second,
   111  		peerMaxIdleTimeout:  0,
   112  		wantTimeout:         10 * time.Second,
   113  	}} {
   114  		name := fmt.Sprintf("local=%v/peer=%v", test.localMaxIdleTimeout, test.peerMaxIdleTimeout)
   115  		t.Run(name, func(t *testing.T) {
   116  			tc := newTestConn(t, serverSide, func(p *transportParameters) {
   117  				p.maxIdleTimeout = test.peerMaxIdleTimeout
   118  			}, func(c *Config) {
   119  				c.MaxIdleTimeout = test.localMaxIdleTimeout
   120  			})
   121  			tc.handshake()
   122  			if got, want := tc.timeUntilEvent(), test.wantTimeout; got != want {
   123  				t.Errorf("new conn timeout=%v, want %v (idle timeout)", got, want)
   124  			}
   125  			tc.advance(test.wantTimeout - 1)
   126  			tc.wantIdle("connection is idle and alive prior to timeout")
   127  			ctx := canceledContext()
   128  			if err := tc.conn.Wait(ctx); err != context.Canceled {
   129  				t.Fatalf("conn.Wait() = %v, want Canceled", err)
   130  			}
   131  			tc.advance(1)
   132  			tc.wantIdle("connection exits after timeout")
   133  			if err := tc.conn.Wait(ctx); err != errIdleTimeout {
   134  				t.Fatalf("conn.Wait() = %v, want errIdleTimeout", err)
   135  			}
   136  		})
   137  	}
   138  }
   139  
   140  func TestIdleTimeoutKeepAlive(t *testing.T) {
   141  	for _, test := range []struct {
   142  		idleTimeout time.Duration
   143  		keepAlive   time.Duration
   144  		wantTimeout time.Duration
   145  	}{{
   146  		idleTimeout: 30 * time.Second,
   147  		keepAlive:   10 * time.Second,
   148  		wantTimeout: 10 * time.Second,
   149  	}, {
   150  		idleTimeout: 10 * time.Second,
   151  		keepAlive:   30 * time.Second,
   152  		wantTimeout: 5 * time.Second,
   153  	}, {
   154  		idleTimeout: -1, // disabled
   155  		keepAlive:   30 * time.Second,
   156  		wantTimeout: 30 * time.Second,
   157  	}} {
   158  		name := fmt.Sprintf("idle_timeout=%v/keepalive=%v", test.idleTimeout, test.keepAlive)
   159  		t.Run(name, func(t *testing.T) {
   160  			tc := newTestConn(t, serverSide, func(c *Config) {
   161  				c.MaxIdleTimeout = test.idleTimeout
   162  				c.KeepAlivePeriod = test.keepAlive
   163  			})
   164  			tc.handshake()
   165  			if got, want := tc.timeUntilEvent(), test.wantTimeout; got != want {
   166  				t.Errorf("new conn timeout=%v, want %v (keepalive timeout)", got, want)
   167  			}
   168  			tc.advance(test.wantTimeout - 1)
   169  			tc.wantIdle("connection is idle prior to timeout")
   170  			tc.advance(1)
   171  			tc.wantFrameType("keep-alive ping is sent", packetType1RTT,
   172  				debugFramePing{})
   173  		})
   174  	}
   175  }
   176  
   177  func TestIdleLongTermKeepAliveSent(t *testing.T) {
   178  	// This test examines a connection sitting idle and sending periodic keep-alive pings.
   179  	const keepAlivePeriod = 30 * time.Second
   180  	tc := newTestConn(t, clientSide, func(c *Config) {
   181  		c.KeepAlivePeriod = keepAlivePeriod
   182  		c.MaxIdleTimeout = -1
   183  	})
   184  	tc.handshake()
   185  	// The handshake will have completed a little bit after the point at which the
   186  	// keepalive timer was set. Send two PING frames to the conn, triggering an immediate ack
   187  	// and resetting the timer.
   188  	tc.writeFrames(packetType1RTT, debugFramePing{})
   189  	tc.writeFrames(packetType1RTT, debugFramePing{})
   190  	tc.wantFrameType("conn acks received pings", packetType1RTT, debugFrameAck{})
   191  	for i := 0; i < 10; i++ {
   192  		tc.wantIdle("conn has nothing more to send")
   193  		if got, want := tc.timeUntilEvent(), keepAlivePeriod; got != want {
   194  			t.Errorf("i=%v conn timeout=%v, want %v (keepalive timeout)", i, got, want)
   195  		}
   196  		tc.advance(keepAlivePeriod)
   197  		tc.wantFrameType("keep-alive ping is sent", packetType1RTT,
   198  			debugFramePing{})
   199  		tc.writeAckForAll()
   200  	}
   201  }
   202  
   203  func TestIdleLongTermKeepAliveReceived(t *testing.T) {
   204  	// This test examines a connection sitting idle, but receiving periodic peer
   205  	// traffic to keep the connection alive.
   206  	const idleTimeout = 30 * time.Second
   207  	tc := newTestConn(t, serverSide, func(c *Config) {
   208  		c.MaxIdleTimeout = idleTimeout
   209  	})
   210  	tc.handshake()
   211  	for i := 0; i < 10; i++ {
   212  		tc.advance(idleTimeout - 1*time.Second)
   213  		tc.writeFrames(packetType1RTT, debugFramePing{})
   214  		if got, want := tc.timeUntilEvent(), maxAckDelay-timerGranularity; got != want {
   215  			t.Errorf("i=%v conn timeout=%v, want %v (max_ack_delay)", i, got, want)
   216  		}
   217  		tc.advanceToTimer()
   218  		tc.wantFrameType("conn acks received ping", packetType1RTT, debugFrameAck{})
   219  	}
   220  	// Connection is still alive.
   221  	ctx := canceledContext()
   222  	if err := tc.conn.Wait(ctx); err != context.Canceled {
   223  		t.Fatalf("conn.Wait() = %v, want Canceled", err)
   224  	}
   225  }