golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/conn_close.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  	"errors"
    12  	"time"
    13  )
    14  
    15  // connState is the state of a connection.
    16  type connState int
    17  
    18  const (
    19  	// A connection is alive when it is first created.
    20  	connStateAlive = connState(iota)
    21  
    22  	// The connection has received a CONNECTION_CLOSE frame from the peer,
    23  	// and has not yet sent a CONNECTION_CLOSE in response.
    24  	//
    25  	// We will send a CONNECTION_CLOSE, and then enter the draining state.
    26  	connStatePeerClosed
    27  
    28  	// The connection is in the closing state.
    29  	//
    30  	// We will send CONNECTION_CLOSE frames to the peer
    31  	// (once upon entering the closing state, and possibly again in response to peer packets).
    32  	//
    33  	// If we receive a CONNECTION_CLOSE from the peer, we will enter the draining state.
    34  	// Otherwise, we will eventually time out and move to the done state.
    35  	//
    36  	// https://www.rfc-editor.org/rfc/rfc9000#section-10.2.1
    37  	connStateClosing
    38  
    39  	// The connection is in the draining state.
    40  	//
    41  	// We will neither send packets nor process received packets.
    42  	// When the drain timer expires, we move to the done state.
    43  	//
    44  	// https://www.rfc-editor.org/rfc/rfc9000#section-10.2.2
    45  	connStateDraining
    46  
    47  	// The connection is done, and the conn loop will exit.
    48  	connStateDone
    49  )
    50  
    51  // lifetimeState tracks the state of a connection.
    52  //
    53  // This is fairly coupled to the rest of a Conn, but putting it in a struct of its own helps
    54  // reason about operations that cause state transitions.
    55  type lifetimeState struct {
    56  	state connState
    57  
    58  	readyc chan struct{} // closed when TLS handshake completes
    59  	donec  chan struct{} // closed when finalErr is set
    60  
    61  	localErr error // error sent to the peer
    62  	finalErr error // error sent by the peer, or transport error; set before closing donec
    63  
    64  	connCloseSentTime time.Time     // send time of last CONNECTION_CLOSE frame
    65  	connCloseDelay    time.Duration // delay until next CONNECTION_CLOSE frame sent
    66  	drainEndTime      time.Time     // time the connection exits the draining state
    67  }
    68  
    69  func (c *Conn) lifetimeInit() {
    70  	c.lifetime.readyc = make(chan struct{})
    71  	c.lifetime.donec = make(chan struct{})
    72  }
    73  
    74  var (
    75  	errNoPeerResponse = errors.New("peer did not respond to CONNECTION_CLOSE")
    76  	errConnClosed     = errors.New("connection closed")
    77  )
    78  
    79  // advance is called when time passes.
    80  func (c *Conn) lifetimeAdvance(now time.Time) (done bool) {
    81  	if c.lifetime.drainEndTime.IsZero() || c.lifetime.drainEndTime.After(now) {
    82  		return false
    83  	}
    84  	// The connection drain period has ended, and we can shut down.
    85  	// https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2-7
    86  	c.lifetime.drainEndTime = time.Time{}
    87  	if c.lifetime.state != connStateDraining {
    88  		// We were in the closing state, waiting for a CONNECTION_CLOSE from the peer.
    89  		c.setFinalError(errNoPeerResponse)
    90  	}
    91  	c.setState(now, connStateDone)
    92  	return true
    93  }
    94  
    95  // setState sets the conn state.
    96  func (c *Conn) setState(now time.Time, state connState) {
    97  	if c.lifetime.state == state {
    98  		return
    99  	}
   100  	c.lifetime.state = state
   101  	switch state {
   102  	case connStateClosing, connStateDraining:
   103  		if c.lifetime.drainEndTime.IsZero() {
   104  			c.lifetime.drainEndTime = now.Add(3 * c.loss.ptoBasePeriod())
   105  		}
   106  	case connStateDone:
   107  		c.setFinalError(nil)
   108  	}
   109  	if state != connStateAlive {
   110  		c.streamsCleanup()
   111  	}
   112  }
   113  
   114  // confirmHandshake is called when the TLS handshake completes.
   115  func (c *Conn) handshakeDone() {
   116  	close(c.lifetime.readyc)
   117  }
   118  
   119  // isDraining reports whether the conn is in the draining state.
   120  //
   121  // The draining state is entered once an endpoint receives a CONNECTION_CLOSE frame.
   122  // The endpoint will no longer send any packets, but we retain knowledge of the connection
   123  // until the end of the drain period to ensure we discard packets for the connection
   124  // rather than treating them as starting a new connection.
   125  //
   126  // https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.2
   127  func (c *Conn) isDraining() bool {
   128  	switch c.lifetime.state {
   129  	case connStateDraining, connStateDone:
   130  		return true
   131  	}
   132  	return false
   133  }
   134  
   135  // isAlive reports whether the conn is handling packets.
   136  func (c *Conn) isAlive() bool {
   137  	return c.lifetime.state == connStateAlive
   138  }
   139  
   140  // sendOK reports whether the conn can send frames at this time.
   141  func (c *Conn) sendOK(now time.Time) bool {
   142  	switch c.lifetime.state {
   143  	case connStateAlive:
   144  		return true
   145  	case connStatePeerClosed:
   146  		if c.lifetime.localErr == nil {
   147  			// We're waiting for the user to close the connection, providing us with
   148  			// a final status to send to the peer.
   149  			return false
   150  		}
   151  		// We should send a CONNECTION_CLOSE.
   152  		return true
   153  	case connStateClosing:
   154  		if c.lifetime.connCloseSentTime.IsZero() {
   155  			return true
   156  		}
   157  		maxRecvTime := c.acks[initialSpace].maxRecvTime
   158  		if t := c.acks[handshakeSpace].maxRecvTime; t.After(maxRecvTime) {
   159  			maxRecvTime = t
   160  		}
   161  		if t := c.acks[appDataSpace].maxRecvTime; t.After(maxRecvTime) {
   162  			maxRecvTime = t
   163  		}
   164  		if maxRecvTime.Before(c.lifetime.connCloseSentTime.Add(c.lifetime.connCloseDelay)) {
   165  			// After sending CONNECTION_CLOSE, ignore packets from the peer for
   166  			// a delay. On the next packet received after the delay, send another
   167  			// CONNECTION_CLOSE.
   168  			return false
   169  		}
   170  		return true
   171  	case connStateDraining:
   172  		// We are in the draining state, and will send no more packets.
   173  		return false
   174  	case connStateDone:
   175  		return false
   176  	default:
   177  		panic("BUG: unhandled connection state")
   178  	}
   179  }
   180  
   181  // sendConnectionClose reports that the conn has sent a CONNECTION_CLOSE to the peer.
   182  func (c *Conn) sentConnectionClose(now time.Time) {
   183  	switch c.lifetime.state {
   184  	case connStatePeerClosed:
   185  		c.enterDraining(now)
   186  	}
   187  	if c.lifetime.connCloseSentTime.IsZero() {
   188  		// Set the initial delay before we will send another CONNECTION_CLOSE.
   189  		//
   190  		// RFC 9000 states that we should rate limit CONNECTION_CLOSE frames,
   191  		// but leaves the implementation of the limit up to us. Here, we start
   192  		// with the same delay as the PTO timer (RFC 9002, Section 6.2.1),
   193  		// not including max_ack_delay, and double it on every CONNECTION_CLOSE sent.
   194  		c.lifetime.connCloseDelay = c.loss.rtt.smoothedRTT + max(4*c.loss.rtt.rttvar, timerGranularity)
   195  	} else if !c.lifetime.connCloseSentTime.Equal(now) {
   196  		// If connCloseSentTime == now, we're sending two CONNECTION_CLOSE frames
   197  		// coalesced into the same datagram. We only want to increase the delay once.
   198  		c.lifetime.connCloseDelay *= 2
   199  	}
   200  	c.lifetime.connCloseSentTime = now
   201  }
   202  
   203  // handlePeerConnectionClose handles a CONNECTION_CLOSE from the peer.
   204  func (c *Conn) handlePeerConnectionClose(now time.Time, err error) {
   205  	c.setFinalError(err)
   206  	switch c.lifetime.state {
   207  	case connStateAlive:
   208  		c.setState(now, connStatePeerClosed)
   209  	case connStatePeerClosed:
   210  		// Duplicate CONNECTION_CLOSE, ignore.
   211  	case connStateClosing:
   212  		if c.lifetime.connCloseSentTime.IsZero() {
   213  			c.setState(now, connStatePeerClosed)
   214  		} else {
   215  			c.setState(now, connStateDraining)
   216  		}
   217  	case connStateDraining:
   218  	case connStateDone:
   219  	}
   220  }
   221  
   222  // setFinalError records the final connection status we report to the user.
   223  func (c *Conn) setFinalError(err error) {
   224  	select {
   225  	case <-c.lifetime.donec:
   226  		return // already set
   227  	default:
   228  	}
   229  	c.lifetime.finalErr = err
   230  	close(c.lifetime.donec)
   231  }
   232  
   233  func (c *Conn) waitReady(ctx context.Context) error {
   234  	select {
   235  	case <-c.lifetime.readyc:
   236  		return nil
   237  	case <-c.lifetime.donec:
   238  		return c.lifetime.finalErr
   239  	default:
   240  	}
   241  	select {
   242  	case <-c.lifetime.readyc:
   243  		return nil
   244  	case <-c.lifetime.donec:
   245  		return c.lifetime.finalErr
   246  	case <-ctx.Done():
   247  		return ctx.Err()
   248  	}
   249  }
   250  
   251  // Close closes the connection.
   252  //
   253  // Close is equivalent to:
   254  //
   255  //	conn.Abort(nil)
   256  //	err := conn.Wait(context.Background())
   257  func (c *Conn) Close() error {
   258  	c.Abort(nil)
   259  	<-c.lifetime.donec
   260  	return c.lifetime.finalErr
   261  }
   262  
   263  // Wait waits for the peer to close the connection.
   264  //
   265  // If the connection is closed locally and the peer does not close its end of the connection,
   266  // Wait will return with a non-nil error after the drain period expires.
   267  //
   268  // If the peer closes the connection with a NO_ERROR transport error, Wait returns nil.
   269  // If the peer closes the connection with an application error, Wait returns an ApplicationError
   270  // containing the peer's error code and reason.
   271  // If the peer closes the connection with any other status, Wait returns a non-nil error.
   272  func (c *Conn) Wait(ctx context.Context) error {
   273  	if err := c.waitOnDone(ctx, c.lifetime.donec); err != nil {
   274  		return err
   275  	}
   276  	return c.lifetime.finalErr
   277  }
   278  
   279  // Abort closes the connection and returns immediately.
   280  //
   281  // If err is nil, Abort sends a transport error of NO_ERROR to the peer.
   282  // If err is an ApplicationError, Abort sends its error code and text.
   283  // Otherwise, Abort sends a transport error of APPLICATION_ERROR with the error's text.
   284  func (c *Conn) Abort(err error) {
   285  	if err == nil {
   286  		err = localTransportError{code: errNo}
   287  	}
   288  	c.sendMsg(func(now time.Time, c *Conn) {
   289  		c.enterClosing(now, err)
   290  	})
   291  }
   292  
   293  // abort terminates a connection with an error.
   294  func (c *Conn) abort(now time.Time, err error) {
   295  	c.setFinalError(err) // this error takes precedence over the peer's CONNECTION_CLOSE
   296  	c.enterClosing(now, err)
   297  }
   298  
   299  // abortImmediately terminates a connection.
   300  // The connection does not send a CONNECTION_CLOSE, and skips the draining period.
   301  func (c *Conn) abortImmediately(now time.Time, err error) {
   302  	c.setFinalError(err)
   303  	c.setState(now, connStateDone)
   304  }
   305  
   306  // enterClosing starts an immediate close.
   307  // We will send a CONNECTION_CLOSE to the peer and wait for their response.
   308  func (c *Conn) enterClosing(now time.Time, err error) {
   309  	switch c.lifetime.state {
   310  	case connStateAlive:
   311  		c.lifetime.localErr = err
   312  		c.setState(now, connStateClosing)
   313  	case connStatePeerClosed:
   314  		c.lifetime.localErr = err
   315  	}
   316  }
   317  
   318  // enterDraining moves directly to the draining state, without sending a CONNECTION_CLOSE.
   319  func (c *Conn) enterDraining(now time.Time) {
   320  	switch c.lifetime.state {
   321  	case connStateAlive, connStatePeerClosed, connStateClosing:
   322  		c.setState(now, connStateDraining)
   323  	}
   324  }
   325  
   326  // exit fully terminates a connection immediately.
   327  func (c *Conn) exit() {
   328  	c.sendMsg(func(now time.Time, c *Conn) {
   329  		c.abortImmediately(now, errors.New("connection closed"))
   330  	})
   331  }