golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/crypto_stream_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  	"crypto/rand"
    11  	"reflect"
    12  	"testing"
    13  )
    14  
    15  func TestCryptoStreamReceive(t *testing.T) {
    16  	data := make([]byte, 1<<20)
    17  	rand.Read(data) // doesn't need to be crypto/rand, but non-deprecated and harmless
    18  	type frame struct {
    19  		start int64
    20  		end   int64
    21  		want  int
    22  	}
    23  	for _, test := range []struct {
    24  		name   string
    25  		frames []frame
    26  	}{{
    27  		name: "linear",
    28  		frames: []frame{{
    29  			start: 0,
    30  			end:   1000,
    31  			want:  1000,
    32  		}, {
    33  			start: 1000,
    34  			end:   2000,
    35  			want:  2000,
    36  		}, {
    37  			// larger than any realistic packet can hold
    38  			start: 2000,
    39  			end:   1 << 20,
    40  			want:  1 << 20,
    41  		}},
    42  	}, {
    43  		name: "out of order",
    44  		frames: []frame{{
    45  			start: 1000,
    46  			end:   2000,
    47  		}, {
    48  			start: 2000,
    49  			end:   3000,
    50  		}, {
    51  			start: 0,
    52  			end:   1000,
    53  			want:  3000,
    54  		}},
    55  	}, {
    56  		name: "resent",
    57  		frames: []frame{{
    58  			start: 0,
    59  			end:   1000,
    60  			want:  1000,
    61  		}, {
    62  			start: 0,
    63  			end:   1000,
    64  			want:  1000,
    65  		}, {
    66  			start: 1000,
    67  			end:   2000,
    68  			want:  2000,
    69  		}, {
    70  			start: 0,
    71  			end:   1000,
    72  			want:  2000,
    73  		}, {
    74  			start: 1000,
    75  			end:   2000,
    76  			want:  2000,
    77  		}},
    78  	}, {
    79  		name: "overlapping",
    80  		frames: []frame{{
    81  			start: 0,
    82  			end:   1000,
    83  			want:  1000,
    84  		}, {
    85  			start: 3000,
    86  			end:   4000,
    87  			want:  1000,
    88  		}, {
    89  			start: 2000,
    90  			end:   3000,
    91  			want:  1000,
    92  		}, {
    93  			start: 1000,
    94  			end:   3000,
    95  			want:  4000,
    96  		}},
    97  	}, {
    98  		name: "resent consumed data",
    99  		frames: []frame{{
   100  			start: 0,
   101  			end:   1000,
   102  			want:  1000,
   103  		}, {
   104  			start: 1000,
   105  			end:   2000,
   106  			want:  2000,
   107  		}, {
   108  			start: 0,
   109  			end:   1000,
   110  			want:  2000,
   111  		}},
   112  	}} {
   113  		t.Run(test.name, func(t *testing.T) {
   114  			var s cryptoStream
   115  			var got []byte
   116  			for _, f := range test.frames {
   117  				t.Logf("receive [%v,%v)", f.start, f.end)
   118  				s.handleCrypto(
   119  					f.start,
   120  					data[f.start:f.end],
   121  					func(b []byte) error {
   122  						t.Logf("got new bytes [%v,%v)", len(got), len(got)+len(b))
   123  						got = append(got, b...)
   124  						return nil
   125  					},
   126  				)
   127  				if len(got) != f.want {
   128  					t.Fatalf("have bytes [0,%v), want [0,%v)", len(got), f.want)
   129  				}
   130  				for i := range got {
   131  					if got[i] != data[i] {
   132  						t.Fatalf("byte %v of received data = %v, want %v", i, got[i], data[i])
   133  					}
   134  				}
   135  			}
   136  		})
   137  	}
   138  }
   139  
   140  func TestCryptoStreamSends(t *testing.T) {
   141  	data := make([]byte, 1<<20)
   142  	rand.Read(data) // doesn't need to be crypto/rand, but non-deprecated and harmless
   143  	type (
   144  		sendOp i64range[int64]
   145  		ackOp  i64range[int64]
   146  		lossOp i64range[int64]
   147  	)
   148  	for _, test := range []struct {
   149  		name        string
   150  		size        int64
   151  		ops         []any
   152  		wantSend    []i64range[int64]
   153  		wantPTOSend []i64range[int64]
   154  	}{{
   155  		name: "writes with data remaining",
   156  		size: 4000,
   157  		ops: []any{
   158  			sendOp{0, 1000},
   159  			sendOp{1000, 2000},
   160  			sendOp{2000, 3000},
   161  		},
   162  		wantSend: []i64range[int64]{
   163  			{3000, 4000},
   164  		},
   165  		wantPTOSend: []i64range[int64]{
   166  			{0, 4000},
   167  		},
   168  	}, {
   169  		name: "lost data is resent",
   170  		size: 4000,
   171  		ops: []any{
   172  			sendOp{0, 1000},
   173  			sendOp{1000, 2000},
   174  			sendOp{2000, 3000},
   175  			sendOp{3000, 4000},
   176  			lossOp{1000, 2000},
   177  			lossOp{3000, 4000},
   178  		},
   179  		wantSend: []i64range[int64]{
   180  			{1000, 2000},
   181  			{3000, 4000},
   182  		},
   183  		wantPTOSend: []i64range[int64]{
   184  			{0, 4000},
   185  		},
   186  	}, {
   187  		name: "acked data at start of range",
   188  		size: 4000,
   189  		ops: []any{
   190  			sendOp{0, 4000},
   191  			ackOp{0, 1000},
   192  			ackOp{1000, 2000},
   193  			ackOp{2000, 3000},
   194  		},
   195  		wantSend: nil,
   196  		wantPTOSend: []i64range[int64]{
   197  			{3000, 4000},
   198  		},
   199  	}, {
   200  		name: "acked data is not resent on pto",
   201  		size: 4000,
   202  		ops: []any{
   203  			sendOp{0, 4000},
   204  			ackOp{1000, 2000},
   205  		},
   206  		wantSend: nil,
   207  		wantPTOSend: []i64range[int64]{
   208  			{0, 1000},
   209  		},
   210  	}, {
   211  		// This is an unusual, but possible scenario:
   212  		// Data is sent, resent, one of the two sends is acked, and the other is lost.
   213  		name: "acked and then lost data is not resent",
   214  		size: 4000,
   215  		ops: []any{
   216  			sendOp{0, 4000},
   217  			sendOp{1000, 2000}, // resent, no-op
   218  			ackOp{1000, 2000},
   219  			lossOp{1000, 2000},
   220  		},
   221  		wantSend: nil,
   222  		wantPTOSend: []i64range[int64]{
   223  			{0, 1000},
   224  		},
   225  	}, {
   226  		// The opposite of the above scenario: data is marked lost, and then acked
   227  		// before being resent.
   228  		name: "lost and then acked data is not resent",
   229  		size: 4000,
   230  		ops: []any{
   231  			sendOp{0, 4000},
   232  			sendOp{1000, 2000}, // resent, no-op
   233  			lossOp{1000, 2000},
   234  			ackOp{1000, 2000},
   235  		},
   236  		wantSend: nil,
   237  		wantPTOSend: []i64range[int64]{
   238  			{0, 1000},
   239  		},
   240  	}} {
   241  		t.Run(test.name, func(t *testing.T) {
   242  			var s cryptoStream
   243  			s.write(data[:test.size])
   244  			for _, op := range test.ops {
   245  				switch op := op.(type) {
   246  				case sendOp:
   247  					t.Logf("send [%v,%v)", op.start, op.end)
   248  					b := make([]byte, op.end-op.start)
   249  					s.sendData(op.start, b)
   250  				case ackOp:
   251  					t.Logf("ack  [%v,%v)", op.start, op.end)
   252  					s.ackOrLoss(op.start, op.end, packetAcked)
   253  				case lossOp:
   254  					t.Logf("loss [%v,%v)", op.start, op.end)
   255  					s.ackOrLoss(op.start, op.end, packetLost)
   256  				default:
   257  					t.Fatalf("unhandled type %T", op)
   258  				}
   259  			}
   260  			var gotSend []i64range[int64]
   261  			s.dataToSend(true, func(off, size int64) (wrote int64) {
   262  				gotSend = append(gotSend, i64range[int64]{off, off + size})
   263  				return 0
   264  			})
   265  			if !reflect.DeepEqual(gotSend, test.wantPTOSend) {
   266  				t.Fatalf("got data to send on PTO: %v, want %v", gotSend, test.wantPTOSend)
   267  			}
   268  			gotSend = nil
   269  			s.dataToSend(false, func(off, size int64) (wrote int64) {
   270  				gotSend = append(gotSend, i64range[int64]{off, off + size})
   271  				b := make([]byte, size)
   272  				s.sendData(off, b)
   273  				return int64(len(b))
   274  			})
   275  			if !reflect.DeepEqual(gotSend, test.wantSend) {
   276  				t.Fatalf("got data to send: %v, want %v", gotSend, test.wantSend)
   277  			}
   278  		})
   279  	}
   280  }