1
2
3
4
5 package quic
6
7 import (
8 "context"
9 "errors"
10 "time"
11 )
12
13
14 type connState int
15
16 const (
17
18 connStateAlive = connState(iota)
19
20
21
22
23
24 connStatePeerClosed
25
26
27
28
29
30
31
32
33
34
35 connStateClosing
36
37
38
39
40
41
42
43 connStateDraining
44
45
46 connStateDone
47 )
48
49
50
51
52
53 type lifetimeState struct {
54 state connState
55
56 readyc chan struct{}
57 donec chan struct{}
58
59 localErr error
60 finalErr error
61
62 connCloseSentTime time.Time
63 connCloseDelay time.Duration
64 drainEndTime time.Time
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
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
83
84 c.lifetime.drainEndTime = time.Time{}
85 if c.lifetime.state != connStateDraining {
86
87 c.setFinalError(errNoPeerResponse)
88 }
89 c.setState(now, connStateDone)
90 return true
91 }
92
93
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
113 func (c *Conn) handshakeDone() {
114 close(c.lifetime.readyc)
115 }
116
117
118
119
120
121
122
123
124
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
134 func (c *Conn) isAlive() bool {
135 return c.lifetime.state == connStateAlive
136 }
137
138
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
146
147 return false
148 }
149
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
164
165
166 return false
167 }
168 return true
169 case connStateDraining:
170
171 return false
172 case connStateDone:
173 return false
174 default:
175 panic("BUG: unhandled connection state")
176 }
177 }
178
179
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
187
188
189
190
191
192 c.lifetime.connCloseDelay = c.loss.rtt.smoothedRTT + max(4*c.loss.rtt.rttvar, timerGranularity)
193 } else if !c.lifetime.connCloseSentTime.Equal(now) {
194
195
196 c.lifetime.connCloseDelay *= 2
197 }
198 c.lifetime.connCloseSentTime = now
199 }
200
201
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
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
221 func (c *Conn) setFinalError(err error) {
222 select {
223 case <-c.lifetime.donec:
224 return
225 default:
226 }
227 c.lifetime.finalErr = err
228 close(c.lifetime.donec)
229 }
230
231
232
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
261
262
263
264
265
266 func (c *Conn) Close() error {
267 c.Abort(nil)
268 <-c.lifetime.donec
269 return c.lifetime.finalErr
270 }
271
272
273
274
275
276
277
278
279
280
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
289
290
291
292
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
303 func (c *Conn) abort(now time.Time, err error) {
304 c.setFinalError(err)
305 c.enterClosing(now, err)
306 }
307
308
309
310 func (c *Conn) abortImmediately(now time.Time, err error) {
311 c.setFinalError(err)
312 c.setState(now, connStateDone)
313 }
314
315
316
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
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
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