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 }