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 }