1
2
3
4
5
6
7 package quic
8
9 import (
10 "context"
11 "crypto/tls"
12 "errors"
13 "testing"
14 "time"
15 )
16
17 func TestConnCloseResponseBackoff(t *testing.T) {
18 tc := newTestConn(t, clientSide, func(c *Config) {
19 clear(c.StatelessResetKey[:])
20 })
21 tc.handshake()
22
23 tc.conn.Abort(nil)
24 tc.wantFrame("aborting connection generates CONN_CLOSE",
25 packetType1RTT, debugFrameConnectionCloseTransport{
26 code: errNo,
27 })
28
29 waiting := runAsync(tc, func(ctx context.Context) (struct{}, error) {
30 return struct{}{}, tc.conn.Wait(ctx)
31 })
32 if _, err := waiting.result(); err != errNotDone {
33 t.Errorf("conn.Wait() = %v, want still waiting", err)
34 }
35
36 tc.writeFrames(packetType1RTT, debugFramePing{})
37 tc.wantIdle("packets received immediately after CONN_CLOSE receive no response")
38
39 tc.advance(1100 * time.Microsecond)
40 tc.writeFrames(packetType1RTT, debugFramePing{})
41 tc.wantFrame("receiving packet 1.1ms after CONN_CLOSE generates another CONN_CLOSE",
42 packetType1RTT, debugFrameConnectionCloseTransport{
43 code: errNo,
44 })
45
46 tc.advance(1100 * time.Microsecond)
47 tc.writeFrames(packetType1RTT, debugFramePing{})
48 tc.wantIdle("no response to packet, because CONN_CLOSE backoff is now 2ms")
49
50 tc.advance(1000 * time.Microsecond)
51 tc.writeFrames(packetType1RTT, debugFramePing{})
52 tc.wantFrame("2ms since last CONN_CLOSE, receiving a packet generates another CONN_CLOSE",
53 packetType1RTT, debugFrameConnectionCloseTransport{
54 code: errNo,
55 })
56 if _, err := waiting.result(); err != errNotDone {
57 t.Errorf("conn.Wait() = %v, want still waiting", err)
58 }
59
60 tc.advance(100000 * time.Microsecond)
61 tc.writeFrames(packetType1RTT, debugFramePing{})
62 tc.wantIdle("drain timer expired, no more responses")
63
64 if _, err := waiting.result(); !errors.Is(err, errNoPeerResponse) {
65 t.Errorf("blocked conn.Wait() = %v, want errNoPeerResponse", err)
66 }
67 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, errNoPeerResponse) {
68 t.Errorf("non-blocking conn.Wait() = %v, want errNoPeerResponse", err)
69 }
70 }
71
72 func TestConnCloseWithPeerResponse(t *testing.T) {
73 qr := &qlogRecord{}
74 tc := newTestConn(t, clientSide, qr.config)
75 tc.handshake()
76
77 tc.conn.Abort(nil)
78 tc.wantFrame("aborting connection generates CONN_CLOSE",
79 packetType1RTT, debugFrameConnectionCloseTransport{
80 code: errNo,
81 })
82
83 waiting := runAsync(tc, func(ctx context.Context) (struct{}, error) {
84 return struct{}{}, tc.conn.Wait(ctx)
85 })
86 if _, err := waiting.result(); err != errNotDone {
87 t.Errorf("conn.Wait() = %v, want still waiting", err)
88 }
89
90 tc.writeFrames(packetType1RTT, debugFrameConnectionCloseApplication{
91 code: 20,
92 })
93
94 wantErr := &ApplicationError{
95 Code: 20,
96 }
97 if _, err := waiting.result(); !errors.Is(err, wantErr) {
98 t.Errorf("blocked conn.Wait() = %v, want %v", err, wantErr)
99 }
100 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, wantErr) {
101 t.Errorf("non-blocking conn.Wait() = %v, want %v", err, wantErr)
102 }
103
104 tc.advance(1 * time.Second)
105 qr.wantEvents(t, jsonEvent{
106 "name": "connectivity:connection_closed",
107 "data": map[string]any{
108 "trigger": "application",
109 },
110 })
111 }
112
113 func TestConnClosePeerCloses(t *testing.T) {
114 qr := &qlogRecord{}
115 tc := newTestConn(t, clientSide, qr.config)
116 tc.handshake()
117
118 wantErr := &ApplicationError{
119 Code: 42,
120 Reason: "why?",
121 }
122 tc.writeFrames(packetType1RTT, debugFrameConnectionCloseApplication{
123 code: wantErr.Code,
124 reason: wantErr.Reason,
125 })
126 tc.wantIdle("CONN_CLOSE response not sent until user closes this side")
127
128 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, wantErr) {
129 t.Errorf("conn.Wait() = %v, want %v", err, wantErr)
130 }
131
132 tc.conn.Abort(&ApplicationError{
133 Code: 9,
134 Reason: "because",
135 })
136 tc.wantFrame("CONN_CLOSE sent after user closes connection",
137 packetType1RTT, debugFrameConnectionCloseApplication{
138 code: 9,
139 reason: "because",
140 })
141
142 tc.advance(1 * time.Second)
143 qr.wantEvents(t, jsonEvent{
144 "name": "connectivity:connection_closed",
145 "data": map[string]any{
146 "trigger": "application",
147 },
148 })
149 }
150
151 func TestConnCloseReceiveInInitial(t *testing.T) {
152 tc := newTestConn(t, clientSide)
153 tc.wantFrame("client sends Initial CRYPTO frame",
154 packetTypeInitial, debugFrameCrypto{
155 data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
156 })
157 tc.writeFrames(packetTypeInitial, debugFrameConnectionCloseTransport{
158 code: errConnectionRefused,
159 })
160 tc.wantIdle("CONN_CLOSE response not sent until user closes this side")
161
162 wantErr := peerTransportError{code: errConnectionRefused}
163 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, wantErr) {
164 t.Errorf("conn.Wait() = %v, want %v", err, wantErr)
165 }
166
167 tc.conn.Abort(&ApplicationError{Code: 1})
168 tc.wantFrame("CONN_CLOSE in Initial frame is APPLICATION_ERROR",
169 packetTypeInitial, debugFrameConnectionCloseTransport{
170 code: errApplicationError,
171 })
172 tc.wantIdle("no more frames to send")
173 }
174
175 func TestConnCloseReceiveInHandshake(t *testing.T) {
176 tc := newTestConn(t, clientSide)
177 tc.ignoreFrame(frameTypeAck)
178 tc.wantFrame("client sends Initial CRYPTO frame",
179 packetTypeInitial, debugFrameCrypto{
180 data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
181 })
182 tc.writeFrames(packetTypeInitial, debugFrameCrypto{
183 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
184 })
185 tc.writeFrames(packetTypeHandshake, debugFrameConnectionCloseTransport{
186 code: errConnectionRefused,
187 })
188 tc.wantIdle("CONN_CLOSE response not sent until user closes this side")
189
190 wantErr := peerTransportError{code: errConnectionRefused}
191 if err := tc.conn.Wait(canceledContext()); !errors.Is(err, wantErr) {
192 t.Errorf("conn.Wait() = %v, want %v", err, wantErr)
193 }
194
195
196 tc.conn.Abort(&ApplicationError{Code: 1})
197 tc.wantFrame("CONN_CLOSE in Initial frame is APPLICATION_ERROR",
198 packetTypeInitial, debugFrameConnectionCloseTransport{
199 code: errApplicationError,
200 })
201 tc.wantFrame("CONN_CLOSE in Handshake frame is APPLICATION_ERROR",
202 packetTypeHandshake, debugFrameConnectionCloseTransport{
203 code: errApplicationError,
204 })
205 tc.wantIdle("no more frames to send")
206 }
207
208 func TestConnCloseClosedByEndpoint(t *testing.T) {
209 ctx := canceledContext()
210 tc := newTestConn(t, clientSide)
211 tc.handshake()
212
213 tc.endpoint.e.Close(ctx)
214 tc.wantFrame("endpoint closes connection before exiting",
215 packetType1RTT, debugFrameConnectionCloseTransport{
216 code: errNo,
217 })
218 }
219
220 func testConnCloseUnblocks(t *testing.T, f func(context.Context, *testConn) error, opts ...any) {
221 tc := newTestConn(t, clientSide, opts...)
222 tc.handshake()
223 op := runAsync(tc, func(ctx context.Context) (struct{}, error) {
224 return struct{}{}, f(ctx, tc)
225 })
226 if _, err := op.result(); err != errNotDone {
227 t.Fatalf("before abort, op = %v, want errNotDone", err)
228 }
229 tc.conn.Abort(nil)
230 if _, err := op.result(); err == nil || err == errNotDone {
231 t.Fatalf("after abort, op = %v, want error", err)
232 }
233 }
234
235 func TestConnCloseUnblocksAcceptStream(t *testing.T) {
236 testConnCloseUnblocks(t, func(ctx context.Context, tc *testConn) error {
237 _, err := tc.conn.AcceptStream(ctx)
238 return err
239 }, permissiveTransportParameters)
240 }
241
242 func TestConnCloseUnblocksNewStream(t *testing.T) {
243 testConnCloseUnblocks(t, func(ctx context.Context, tc *testConn) error {
244 _, err := tc.conn.NewStream(ctx)
245 return err
246 })
247 }
248
249 func TestConnCloseUnblocksStreamRead(t *testing.T) {
250 testConnCloseUnblocks(t, func(ctx context.Context, tc *testConn) error {
251 s := newLocalStream(t, tc, bidiStream)
252 buf := make([]byte, 16)
253 _, err := s.ReadContext(ctx, buf)
254 return err
255 }, permissiveTransportParameters)
256 }
257
258 func TestConnCloseUnblocksStreamWrite(t *testing.T) {
259 testConnCloseUnblocks(t, func(ctx context.Context, tc *testConn) error {
260 s := newLocalStream(t, tc, bidiStream)
261 buf := make([]byte, 32)
262 _, err := s.WriteContext(ctx, buf)
263 return err
264 }, permissiveTransportParameters, func(c *Config) {
265 c.MaxStreamWriteBufferSize = 16
266 })
267 }
268
269 func TestConnCloseUnblocksStreamClose(t *testing.T) {
270 testConnCloseUnblocks(t, func(ctx context.Context, tc *testConn) error {
271 s := newLocalStream(t, tc, bidiStream)
272 buf := make([]byte, 16)
273 _, err := s.WriteContext(ctx, buf)
274 if err != nil {
275 return err
276 }
277 return s.CloseContext(ctx)
278 }, permissiveTransportParameters)
279 }
280
View as plain text