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  

View as plain text