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 }