golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/conn_close_test.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  	"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) // long enough to exit the draining state
   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) // long enough to exit the draining state
   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  	// The conn has Initial and Handshake keys, so it will send CONN_CLOSE in both spaces.
   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  		s.SetReadContext(ctx)
   253  		buf := make([]byte, 16)
   254  		_, err := s.Read(buf)
   255  		return err
   256  	}, permissiveTransportParameters)
   257  }
   258  
   259  func TestConnCloseUnblocksStreamWrite(t *testing.T) {
   260  	testConnCloseUnblocks(t, func(ctx context.Context, tc *testConn) error {
   261  		s := newLocalStream(t, tc, bidiStream)
   262  		s.SetWriteContext(ctx)
   263  		buf := make([]byte, 32)
   264  		_, err := s.Write(buf)
   265  		return err
   266  	}, permissiveTransportParameters, func(c *Config) {
   267  		c.MaxStreamWriteBufferSize = 16
   268  	})
   269  }
   270  
   271  func TestConnCloseUnblocksStreamClose(t *testing.T) {
   272  	testConnCloseUnblocks(t, func(ctx context.Context, tc *testConn) error {
   273  		s := newLocalStream(t, tc, bidiStream)
   274  		s.SetWriteContext(ctx)
   275  		buf := make([]byte, 16)
   276  		_, err := s.Write(buf)
   277  		if err != nil {
   278  			return err
   279  		}
   280  		return s.Close()
   281  	}, permissiveTransportParameters)
   282  }