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.streamsCleanup()
   109  	}
   110  }
   111  
   112  // handshakeDone is called when the TLS handshake completes.
   113  func (c *Conn) handshakeDone() {
   114  	close(c.lifetime.readyc)
   115  }
   116  
   117  // isDraining reports whether the conn is in the draining state.
   118  //
   119  // The draining state is entered once an endpoint receives a CONNECTION_CLOSE frame.
   120  // The endpoint will no longer send any packets, but we retain knowledge of the connection
   121  // until the end of the drain period to ensure we discard packets for the connection
   122  // rather than treating them as starting a new connection.
   123  //
   124  // https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.2
   125  func (c *Conn) isDraining() bool {
   126  	switch c.lifetime.state {
   127  	case connStateDraining, connStateDone:
   128  		return true
   129  	}
   130  	return false
   131  }
   132  
   133  // isAlive reports whether the conn is handling packets.
   134  func (c *Conn) isAlive() bool {
   135  	return c.lifetime.state == connStateAlive
   136  }
   137  
   138  // sendOK reports whether the conn can send frames at this time.
   139  func (c *Conn) sendOK(now time.Time) bool {
   140  	switch c.lifetime.state {
   141  	case connStateAlive:
   142  		return true
   143  	case connStatePeerClosed:
   144  		if c.lifetime.localErr == nil {
   145  			// We're waiting for the user to close the connection, providing us with
   146  			// a final status to send to the peer.
   147  			return false
   148  		}
   149  		// We should send a CONNECTION_CLOSE.
   150  		return true
   151  	case connStateClosing:
   152  		if c.lifetime.connCloseSentTime.IsZero() {
   153  			return true
   154  		}
   155  		maxRecvTime := c.acks[initialSpace].maxRecvTime
   156  		if t := c.acks[handshakeSpace].maxRecvTime; t.After(maxRecvTime) {
   157  			maxRecvTime = t
   158  		}
   159  		if t := c.acks[appDataSpace].maxRecvTime; t.After(maxRecvTime) {
   160  			maxRecvTime = t
   161  		}
   162  		if maxRecvTime.Before(c.lifetime.connCloseSentTime.Add(c.lifetime.connCloseDelay)) {
   163  			// After sending CONNECTION_CLOSE, ignore packets from the peer for
   164  			// a delay. On the next packet received after the delay, send another
   165  			// CONNECTION_CLOSE.
   166  			return false
   167  		}
   168  		return true
   169  	case connStateDraining:
   170  		// We are in the draining state, and will send no more packets.
   171  		return false
   172  	case connStateDone:
   173  		return false
   174  	default:
   175  		panic("BUG: unhandled connection state")
   176  	}
   177  }
   178  
   179  // sentConnectionClose reports that the conn has sent a CONNECTION_CLOSE to the peer.
   180  func (c *Conn) sentConnectionClose(now time.Time) {
   181  	switch c.lifetime.state {
   182  	case connStatePeerClosed:
   183  		c.enterDraining(now)
   184  	}
   185  	if c.lifetime.connCloseSentTime.IsZero() {
   186  		// Set the initial delay before we will send another CONNECTION_CLOSE.
   187  		//
   188  		// RFC 9000 states that we should rate limit CONNECTION_CLOSE frames,
   189  		// but leaves the implementation of the limit up to us. Here, we start
   190  		// with the same delay as the PTO timer (RFC 9002, Section 6.2.1),
   191  		// not including max_ack_delay, and double it on every CONNECTION_CLOSE sent.
   192  		c.lifetime.connCloseDelay = c.loss.rtt.smoothedRTT + max(4*c.loss.rtt.rttvar, timerGranularity)
   193  	} else if !c.lifetime.connCloseSentTime.Equal(now) {
   194  		// If connCloseSentTime == now, we're sending two CONNECTION_CLOSE frames
   195  		// coalesced into the same datagram. We only want to increase the delay once.
   196  		c.lifetime.connCloseDelay *= 2
   197  	}
   198  	c.lifetime.connCloseSentTime = now
   199  }
   200  
   201  // handlePeerConnectionClose handles a CONNECTION_CLOSE from the peer.
   202  func (c *Conn) handlePeerConnectionClose(now time.Time, err error) {
   203  	c.setFinalError(err)
   204  	switch c.lifetime.state {
   205  	case connStateAlive:
   206  		c.setState(now, connStatePeerClosed)
   207  	case connStatePeerClosed:
   208  		// Duplicate CONNECTION_CLOSE, ignore.
   209  	case connStateClosing:
   210  		if c.lifetime.connCloseSentTime.IsZero() {
   211  			c.setState(now, connStatePeerClosed)
   212  		} else {
   213  			c.setState(now, connStateDraining)
   214  		}
   215  	case connStateDraining:
   216  	case connStateDone:
   217  	}
   218  }
   219  
   220  // setFinalError records the final connection status we report to the user.
   221  func (c *Conn) setFinalError(err error) {
   222  	select {
   223  	case <-c.lifetime.donec:
   224  		return // already set
   225  	default:
   226  	}
   227  	c.lifetime.finalErr = err
   228  	close(c.lifetime.donec)
   229  }
   230  
   231  // finalError returns the final connection status reported to the user,
   232  // or nil if a final status has not yet been set.
   233  func (c *Conn) finalError() error {
   234  	select {
   235  	case <-c.lifetime.donec:
   236  		return c.lifetime.finalErr
   237  	default:
   238  	}
   239  	return nil
   240  }
   241  
   242  func (c *Conn) waitReady(ctx context.Context) error {
   243  	select {
   244  	case <-c.lifetime.readyc:
   245  		return nil
   246  	case <-c.lifetime.donec:
   247  		return c.lifetime.finalErr
   248  	default:
   249  	}
   250  	select {
   251  	case <-c.lifetime.readyc:
   252  		return nil
   253  	case <-c.lifetime.donec:
   254  		return c.lifetime.finalErr
   255  	case <-ctx.Done():
   256  		return ctx.Err()
   257  	}
   258  }
   259  
   260  // Close closes the connection.
   261  //
   262  // Close is equivalent to:
   263  //
   264  //	conn.Abort(nil)
   265  //	err := conn.Wait(context.Background())
   266  func (c *Conn) Close() error {
   267  	c.Abort(nil)
   268  	<-c.lifetime.donec
   269  	return c.lifetime.finalErr
   270  }
   271  
   272  // Wait waits for the peer to close the connection.
   273  //
   274  // If the connection is closed locally and the peer does not close its end of the connection,
   275  // Wait will return with a non-nil error after the drain period expires.
   276  //
   277  // If the peer closes the connection with a NO_ERROR transport error, Wait returns nil.
   278  // If the peer closes the connection with an application error, Wait returns an ApplicationError
   279  // containing the peer's error code and reason.
   280  // If the peer closes the connection with any other status, Wait returns a non-nil error.
   281  func (c *Conn) Wait(ctx context.Context) error {
   282  	if err := c.waitOnDone(ctx, c.lifetime.donec); err != nil {
   283  		return err
   284  	}
   285  	return c.lifetime.finalErr
   286  }
   287  
   288  // Abort closes the connection and returns immediately.
   289  //
   290  // If err is nil, Abort sends a transport error of NO_ERROR to the peer.
   291  // If err is an ApplicationError, Abort sends its error code and text.
   292  // Otherwise, Abort sends a transport error of APPLICATION_ERROR with the error's text.
   293  func (c *Conn) Abort(err error) {
   294  	if err == nil {
   295  		err = localTransportError{code: errNo}
   296  	}
   297  	c.sendMsg(func(now time.Time, c *Conn) {
   298  		c.enterClosing(now, err)
   299  	})
   300  }
   301  
   302  // abort terminates a connection with an error.
   303  func (c *Conn) abort(now time.Time, err error) {
   304  	c.setFinalError(err) // this error takes precedence over the peer's CONNECTION_CLOSE
   305  	c.enterClosing(now, err)
   306  }
   307  
   308  // abortImmediately terminates a connection.
   309  // The connection does not send a CONNECTION_CLOSE, and skips the draining period.
   310  func (c *Conn) abortImmediately(now time.Time, err error) {
   311  	c.setFinalError(err)
   312  	c.setState(now, connStateDone)
   313  }
   314  
   315  // enterClosing starts an immediate close.
   316  // We will send a CONNECTION_CLOSE to the peer and wait for their response.
   317  func (c *Conn) enterClosing(now time.Time, err error) {
   318  	switch c.lifetime.state {
   319  	case connStateAlive:
   320  		c.lifetime.localErr = err
   321  		c.setState(now, connStateClosing)
   322  	case connStatePeerClosed:
   323  		c.lifetime.localErr = err
   324  	}
   325  }
   326  
   327  // enterDraining moves directly to the draining state, without sending a CONNECTION_CLOSE.
   328  func (c *Conn) enterDraining(now time.Time) {
   329  	switch c.lifetime.state {
   330  	case connStateAlive, connStatePeerClosed, connStateClosing:
   331  		c.setState(now, connStateDraining)
   332  	}
   333  }
   334  
   335  // exit fully terminates a connection immediately.
   336  func (c *Conn) exit() {
   337  	c.sendMsg(func(now time.Time, c *Conn) {
   338  		c.abortImmediately(now, errors.New("connection closed"))
   339  	})
   340  }
   341  

View as plain text