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  

View as plain text