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

View as plain text