Source file src/vendor/golang.org/x/net/quic/idle.go
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 package quic 6 7 import ( 8 "time" 9 ) 10 11 // idleState tracks connection idle events. 12 // 13 // Before the handshake is confirmed, the idle timeout is Config.HandshakeTimeout. 14 // 15 // After the handshake is confirmed, the idle timeout is 16 // the minimum of Config.MaxIdleTimeout and the peer's max_idle_timeout transport parameter. 17 // 18 // If KeepAlivePeriod is set, keep-alive pings are sent. 19 // Keep-alives are only sent after the handshake is confirmed. 20 // 21 // https://www.rfc-editor.org/rfc/rfc9000#section-10.1 22 type idleState struct { 23 // idleDuration is the negotiated idle timeout for the connection. 24 idleDuration time.Duration 25 26 // idleTimeout is the time at which the connection will be closed due to inactivity. 27 idleTimeout time.Time 28 29 // nextTimeout is the time of the next idle event. 30 // If nextTimeout == idleTimeout, this is the idle timeout. 31 // Otherwise, this is the keep-alive timeout. 32 nextTimeout time.Time 33 34 // sentSinceLastReceive is set if we have sent an ack-eliciting packet 35 // since the last time we received and processed a packet from the peer. 36 sentSinceLastReceive bool 37 } 38 39 // receivePeerMaxIdleTimeout handles the peer's max_idle_timeout transport parameter. 40 func (c *Conn) receivePeerMaxIdleTimeout(peerMaxIdleTimeout time.Duration) { 41 localMaxIdleTimeout := c.config.maxIdleTimeout() 42 switch { 43 case localMaxIdleTimeout == 0: 44 c.idle.idleDuration = peerMaxIdleTimeout 45 case peerMaxIdleTimeout == 0: 46 c.idle.idleDuration = localMaxIdleTimeout 47 default: 48 c.idle.idleDuration = min(localMaxIdleTimeout, peerMaxIdleTimeout) 49 } 50 } 51 52 func (c *Conn) idleHandlePacketReceived(now time.Time) { 53 if !c.handshakeConfirmed.isSet() { 54 return 55 } 56 // "An endpoint restarts its idle timer when a packet from its peer is 57 // received and processed successfully." 58 // https://www.rfc-editor.org/rfc/rfc9000#section-10.1-3 59 c.idle.sentSinceLastReceive = false 60 c.restartIdleTimer(now) 61 } 62 63 func (c *Conn) idleHandlePacketSent(now time.Time, sent *sentPacket) { 64 // "An endpoint also restarts its idle timer when sending an ack-eliciting packet 65 // if no other ack-eliciting packets have been sent since 66 // last receiving and processing a packet." 67 // https://www.rfc-editor.org/rfc/rfc9000#section-10.1-3 68 if c.idle.sentSinceLastReceive || !sent.ackEliciting || !c.handshakeConfirmed.isSet() { 69 return 70 } 71 c.idle.sentSinceLastReceive = true 72 c.restartIdleTimer(now) 73 } 74 75 func (c *Conn) restartIdleTimer(now time.Time) { 76 if !c.isAlive() { 77 // Connection is closing, disable timeouts. 78 c.idle.idleTimeout = time.Time{} 79 c.idle.nextTimeout = time.Time{} 80 return 81 } 82 var idleDuration time.Duration 83 if c.handshakeConfirmed.isSet() { 84 idleDuration = c.idle.idleDuration 85 } else { 86 idleDuration = c.config.handshakeTimeout() 87 } 88 if idleDuration == 0 { 89 c.idle.idleTimeout = time.Time{} 90 } else { 91 // "[...] endpoints MUST increase the idle timeout period to be 92 // at least three times the current Probe Timeout (PTO)." 93 // https://www.rfc-editor.org/rfc/rfc9000#section-10.1-4 94 idleDuration = max(idleDuration, 3*c.loss.ptoPeriod()) 95 c.idle.idleTimeout = now.Add(idleDuration) 96 } 97 // Set the time of our next event: 98 // The idle timer if no keep-alive is set, or the keep-alive timer if one is. 99 c.idle.nextTimeout = c.idle.idleTimeout 100 keepAlive := c.config.keepAlivePeriod() 101 switch { 102 case !c.handshakeConfirmed.isSet(): 103 // We do not send keep-alives before the handshake is complete. 104 case keepAlive <= 0: 105 // Keep-alives are not enabled. 106 case c.idle.sentSinceLastReceive: 107 // We have sent an ack-eliciting packet to the peer. 108 // If they don't acknowledge it, loss detection will follow up with PTO probes, 109 // which will function as keep-alives. 110 // We don't need to send further pings. 111 case idleDuration == 0: 112 // The connection does not have a negotiated idle timeout. 113 // Send keep-alives anyway, since they may be required to keep middleboxes 114 // from losing state. 115 c.idle.nextTimeout = now.Add(keepAlive) 116 default: 117 // Schedule our next keep-alive. 118 // If our configured keep-alive period is greater than half the negotiated 119 // connection idle timeout, we reduce the keep-alive period to half 120 // the idle timeout to ensure we have time for the ping to arrive. 121 c.idle.nextTimeout = now.Add(min(keepAlive, idleDuration/2)) 122 } 123 } 124 125 func (c *Conn) appendKeepAlive(now time.Time) bool { 126 if c.idle.nextTimeout.IsZero() || c.idle.nextTimeout.After(now) { 127 return true // timer has not expired 128 } 129 if c.idle.nextTimeout.Equal(c.idle.idleTimeout) { 130 return true // no keepalive timer set, only idle 131 } 132 if c.idle.sentSinceLastReceive { 133 return true // already sent an ack-eliciting packet 134 } 135 if c.w.sent.ackEliciting { 136 return true // this packet is already ack-eliciting 137 } 138 // Send an ack-eliciting PING frame to the peer to keep the connection alive. 139 return c.w.appendPingFrame() 140 } 141 142 var errHandshakeTimeout error = localTransportError{ 143 code: errConnectionRefused, 144 reason: "handshake timeout", 145 } 146 147 func (c *Conn) idleAdvance(now time.Time) (shouldExit bool) { 148 if c.idle.idleTimeout.IsZero() || now.Before(c.idle.idleTimeout) { 149 return false 150 } 151 c.idle.idleTimeout = time.Time{} 152 c.idle.nextTimeout = time.Time{} 153 if !c.handshakeConfirmed.isSet() { 154 // Handshake timeout has expired. 155 // If we're a server, we're refusing the too-slow client. 156 // If we're a client, we're giving up. 157 // In either case, we're going to send a CONNECTION_CLOSE frame and 158 // enter the closing state rather than unceremoniously dropping the connection, 159 // since the peer might still be trying to complete the handshake. 160 c.abort(now, errHandshakeTimeout) 161 return false 162 } 163 // Idle timeout has expired. 164 // 165 // "[...] the connection is silently closed and its state is discarded [...]" 166 // https://www.rfc-editor.org/rfc/rfc9000#section-10.1-1 167 return true 168 } 169