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