]> Cypherpunks.ru repositories - gostls13.git/blob - src/crypto/tls/tls_test.go
crypto/tls: fix race in Benchmark{Throughput,Latency}
[gostls13.git] / src / crypto / tls / tls_test.go
1 // Copyright 2012 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 package tls
6
7 import (
8         "bytes"
9         "errors"
10         "fmt"
11         "internal/testenv"
12         "io"
13         "math"
14         "net"
15         "strings"
16         "testing"
17         "time"
18 )
19
20 var rsaCertPEM = `-----BEGIN CERTIFICATE-----
21 MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
22 BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
23 aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF
24 MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
25 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANLJ
26 hPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wok/4xIA+ui35/MmNa
27 rtNuC+BdZ1tMuVCPFZcCAwEAAaNQME4wHQYDVR0OBBYEFJvKs8RfJaXTH08W+SGv
28 zQyKn0H8MB8GA1UdIwQYMBaAFJvKs8RfJaXTH08W+SGvzQyKn0H8MAwGA1UdEwQF
29 MAMBAf8wDQYJKoZIhvcNAQEFBQADQQBJlffJHybjDGxRMqaRmDhX0+6v02TUKZsW
30 r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V
31 -----END CERTIFICATE-----
32 `
33
34 var rsaKeyPEM = `-----BEGIN RSA PRIVATE KEY-----
35 MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
36 k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
37 6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
38 MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW
39 SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T
40 xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi
41 D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
42 -----END RSA PRIVATE KEY-----
43 `
44
45 // keyPEM is the same as rsaKeyPEM, but declares itself as just
46 // "PRIVATE KEY", not "RSA PRIVATE KEY".  https://golang.org/issue/4477
47 var keyPEM = `-----BEGIN PRIVATE KEY-----
48 MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
49 k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
50 6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
51 MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW
52 SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T
53 xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi
54 D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
55 -----END PRIVATE KEY-----
56 `
57
58 var ecdsaCertPEM = `-----BEGIN CERTIFICATE-----
59 MIIB/jCCAWICCQDscdUxw16XFDAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw
60 EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0
61 eSBMdGQwHhcNMTIxMTE0MTI0MDQ4WhcNMTUxMTE0MTI0MDQ4WjBFMQswCQYDVQQG
62 EwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lk
63 Z2l0cyBQdHkgTHRkMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBY9+my9OoeSUR
64 lDQdV/x8LsOuLilthhiS1Tz4aGDHIPwC1mlvnf7fg5lecYpMCrLLhauAc1UJXcgl
65 01xoLuzgtAEAgv2P/jgytzRSpUYvgLBt1UA0leLYBy6mQQbrNEuqT3INapKIcUv8
66 XxYP0xMEUksLPq6Ca+CRSqTtrd/23uTnapkwCQYHKoZIzj0EAQOBigAwgYYCQXJo
67 A7Sl2nLVf+4Iu/tAX/IF4MavARKC4PPHK3zfuGfPR3oCCcsAoz3kAzOeijvd0iXb
68 H5jBImIxPL4WxQNiBTexAkF8D1EtpYuWdlVQ80/h/f4pBcGiXPqX5h2PQSQY7hP1
69 +jwM1FGS4fREIOvlBYr/SzzQRtwrvrzGYxDEDbsC0ZGRnA==
70 -----END CERTIFICATE-----
71 `
72
73 var ecdsaKeyPEM = `-----BEGIN EC PARAMETERS-----
74 BgUrgQQAIw==
75 -----END EC PARAMETERS-----
76 -----BEGIN EC PRIVATE KEY-----
77 MIHcAgEBBEIBrsoKp0oqcv6/JovJJDoDVSGWdirrkgCWxrprGlzB9o0X8fV675X0
78 NwuBenXFfeZvVcwluO7/Q9wkYoPd/t3jGImgBwYFK4EEACOhgYkDgYYABAFj36bL
79 06h5JRGUNB1X/Hwuw64uKW2GGJLVPPhoYMcg/ALWaW+d/t+DmV5xikwKssuFq4Bz
80 VQldyCXTXGgu7OC0AQCC/Y/+ODK3NFKlRi+AsG3VQDSV4tgHLqZBBus0S6pPcg1q
81 kohxS/xfFg/TEwRSSws+roJr4JFKpO2t3/be5OdqmQ==
82 -----END EC PRIVATE KEY-----
83 `
84
85 var keyPairTests = []struct {
86         algo string
87         cert string
88         key  string
89 }{
90         {"ECDSA", ecdsaCertPEM, ecdsaKeyPEM},
91         {"RSA", rsaCertPEM, rsaKeyPEM},
92         {"RSA-untyped", rsaCertPEM, keyPEM}, // golang.org/issue/4477
93 }
94
95 func TestX509KeyPair(t *testing.T) {
96         var pem []byte
97         for _, test := range keyPairTests {
98                 pem = []byte(test.cert + test.key)
99                 if _, err := X509KeyPair(pem, pem); err != nil {
100                         t.Errorf("Failed to load %s cert followed by %s key: %s", test.algo, test.algo, err)
101                 }
102                 pem = []byte(test.key + test.cert)
103                 if _, err := X509KeyPair(pem, pem); err != nil {
104                         t.Errorf("Failed to load %s key followed by %s cert: %s", test.algo, test.algo, err)
105                 }
106         }
107 }
108
109 func TestX509KeyPairErrors(t *testing.T) {
110         _, err := X509KeyPair([]byte(rsaKeyPEM), []byte(rsaCertPEM))
111         if err == nil {
112                 t.Fatalf("X509KeyPair didn't return an error when arguments were switched")
113         }
114         if subStr := "been switched"; !strings.Contains(err.Error(), subStr) {
115                 t.Fatalf("Expected %q in the error when switching arguments to X509KeyPair, but the error was %q", subStr, err)
116         }
117
118         _, err = X509KeyPair([]byte(rsaCertPEM), []byte(rsaCertPEM))
119         if err == nil {
120                 t.Fatalf("X509KeyPair didn't return an error when both arguments were certificates")
121         }
122         if subStr := "certificate"; !strings.Contains(err.Error(), subStr) {
123                 t.Fatalf("Expected %q in the error when both arguments to X509KeyPair were certificates, but the error was %q", subStr, err)
124         }
125
126         const nonsensePEM = `
127 -----BEGIN NONSENSE-----
128 Zm9vZm9vZm9v
129 -----END NONSENSE-----
130 `
131
132         _, err = X509KeyPair([]byte(nonsensePEM), []byte(nonsensePEM))
133         if err == nil {
134                 t.Fatalf("X509KeyPair didn't return an error when both arguments were nonsense")
135         }
136         if subStr := "NONSENSE"; !strings.Contains(err.Error(), subStr) {
137                 t.Fatalf("Expected %q in the error when both arguments to X509KeyPair were nonsense, but the error was %q", subStr, err)
138         }
139 }
140
141 func TestX509MixedKeyPair(t *testing.T) {
142         if _, err := X509KeyPair([]byte(rsaCertPEM), []byte(ecdsaKeyPEM)); err == nil {
143                 t.Error("Load of RSA certificate succeeded with ECDSA private key")
144         }
145         if _, err := X509KeyPair([]byte(ecdsaCertPEM), []byte(rsaKeyPEM)); err == nil {
146                 t.Error("Load of ECDSA certificate succeeded with RSA private key")
147         }
148 }
149
150 func newLocalListener(t testing.TB) net.Listener {
151         ln, err := net.Listen("tcp", "127.0.0.1:0")
152         if err != nil {
153                 ln, err = net.Listen("tcp6", "[::1]:0")
154         }
155         if err != nil {
156                 t.Fatal(err)
157         }
158         return ln
159 }
160
161 func TestDialTimeout(t *testing.T) {
162         if testing.Short() {
163                 t.Skip("skipping in short mode")
164         }
165         listener := newLocalListener(t)
166
167         addr := listener.Addr().String()
168         defer listener.Close()
169
170         complete := make(chan bool)
171         defer close(complete)
172
173         go func() {
174                 conn, err := listener.Accept()
175                 if err != nil {
176                         t.Error(err)
177                         return
178                 }
179                 <-complete
180                 conn.Close()
181         }()
182
183         dialer := &net.Dialer{
184                 Timeout: 10 * time.Millisecond,
185         }
186
187         var err error
188         if _, err = DialWithDialer(dialer, "tcp", addr, nil); err == nil {
189                 t.Fatal("DialWithTimeout completed successfully")
190         }
191
192         if !isTimeoutError(err) {
193                 t.Errorf("resulting error not a timeout: %v\nType %T: %#v", err, err, err)
194         }
195 }
196
197 func isTimeoutError(err error) bool {
198         if ne, ok := err.(net.Error); ok {
199                 return ne.Timeout()
200         }
201         return false
202 }
203
204 // tests that Conn.Read returns (non-zero, io.EOF) instead of
205 // (non-zero, nil) when a Close (alertCloseNotify) is sitting right
206 // behind the application data in the buffer.
207 func TestConnReadNonzeroAndEOF(t *testing.T) {
208         // This test is racy: it assumes that after a write to a
209         // localhost TCP connection, the peer TCP connection can
210         // immediately read it. Because it's racy, we skip this test
211         // in short mode, and then retry it several times with an
212         // increasing sleep in between our final write (via srv.Close
213         // below) and the following read.
214         if testing.Short() {
215                 t.Skip("skipping in short mode")
216         }
217         var err error
218         for delay := time.Millisecond; delay <= 64*time.Millisecond; delay *= 2 {
219                 if err = testConnReadNonzeroAndEOF(t, delay); err == nil {
220                         return
221                 }
222         }
223         t.Error(err)
224 }
225
226 func testConnReadNonzeroAndEOF(t *testing.T, delay time.Duration) error {
227         ln := newLocalListener(t)
228         defer ln.Close()
229
230         srvCh := make(chan *Conn, 1)
231         var serr error
232         go func() {
233                 sconn, err := ln.Accept()
234                 if err != nil {
235                         serr = err
236                         srvCh <- nil
237                         return
238                 }
239                 serverConfig := *testConfig
240                 srv := Server(sconn, &serverConfig)
241                 if err := srv.Handshake(); err != nil {
242                         serr = fmt.Errorf("handshake: %v", err)
243                         srvCh <- nil
244                         return
245                 }
246                 srvCh <- srv
247         }()
248
249         clientConfig := *testConfig
250         conn, err := Dial("tcp", ln.Addr().String(), &clientConfig)
251         if err != nil {
252                 t.Fatal(err)
253         }
254         defer conn.Close()
255
256         srv := <-srvCh
257         if srv == nil {
258                 return serr
259         }
260
261         buf := make([]byte, 6)
262
263         srv.Write([]byte("foobar"))
264         n, err := conn.Read(buf)
265         if n != 6 || err != nil || string(buf) != "foobar" {
266                 return fmt.Errorf("Read = %d, %v, data %q; want 6, nil, foobar", n, err, buf)
267         }
268
269         srv.Write([]byte("abcdef"))
270         srv.Close()
271         time.Sleep(delay)
272         n, err = conn.Read(buf)
273         if n != 6 || string(buf) != "abcdef" {
274                 return fmt.Errorf("Read = %d, buf= %q; want 6, abcdef", n, buf)
275         }
276         if err != io.EOF {
277                 return fmt.Errorf("Second Read error = %v; want io.EOF", err)
278         }
279         return nil
280 }
281
282 func TestTLSUniqueMatches(t *testing.T) {
283         ln := newLocalListener(t)
284         defer ln.Close()
285
286         serverTLSUniques := make(chan []byte)
287         go func() {
288                 for i := 0; i < 2; i++ {
289                         sconn, err := ln.Accept()
290                         if err != nil {
291                                 t.Fatal(err)
292                         }
293                         serverConfig := *testConfig
294                         srv := Server(sconn, &serverConfig)
295                         if err := srv.Handshake(); err != nil {
296                                 t.Fatal(err)
297                         }
298                         serverTLSUniques <- srv.ConnectionState().TLSUnique
299                 }
300         }()
301
302         clientConfig := *testConfig
303         clientConfig.ClientSessionCache = NewLRUClientSessionCache(1)
304         conn, err := Dial("tcp", ln.Addr().String(), &clientConfig)
305         if err != nil {
306                 t.Fatal(err)
307         }
308         if !bytes.Equal(conn.ConnectionState().TLSUnique, <-serverTLSUniques) {
309                 t.Error("client and server channel bindings differ")
310         }
311         conn.Close()
312
313         conn, err = Dial("tcp", ln.Addr().String(), &clientConfig)
314         if err != nil {
315                 t.Fatal(err)
316         }
317         defer conn.Close()
318         if !conn.ConnectionState().DidResume {
319                 t.Error("second session did not use resumption")
320         }
321         if !bytes.Equal(conn.ConnectionState().TLSUnique, <-serverTLSUniques) {
322                 t.Error("client and server channel bindings differ when session resumption is used")
323         }
324 }
325
326 func TestVerifyHostname(t *testing.T) {
327         testenv.MustHaveExternalNetwork(t)
328
329         c, err := Dial("tcp", "www.google.com:https", nil)
330         if err != nil {
331                 t.Fatal(err)
332         }
333         if err := c.VerifyHostname("www.google.com"); err != nil {
334                 t.Fatalf("verify www.google.com: %v", err)
335         }
336         if err := c.VerifyHostname("www.yahoo.com"); err == nil {
337                 t.Fatalf("verify www.yahoo.com succeeded")
338         }
339
340         c, err = Dial("tcp", "www.google.com:https", &Config{InsecureSkipVerify: true})
341         if err != nil {
342                 t.Fatal(err)
343         }
344         if err := c.VerifyHostname("www.google.com"); err == nil {
345                 t.Fatalf("verify www.google.com succeeded with InsecureSkipVerify=true")
346         }
347         if err := c.VerifyHostname("www.yahoo.com"); err == nil {
348                 t.Fatalf("verify www.google.com succeeded with InsecureSkipVerify=true")
349         }
350 }
351
352 func TestVerifyHostnameResumed(t *testing.T) {
353         testenv.MustHaveExternalNetwork(t)
354
355         config := &Config{
356                 ClientSessionCache: NewLRUClientSessionCache(32),
357         }
358         for i := 0; i < 2; i++ {
359                 c, err := Dial("tcp", "www.google.com:https", config)
360                 if err != nil {
361                         t.Fatalf("Dial #%d: %v", i, err)
362                 }
363                 cs := c.ConnectionState()
364                 if i > 0 && !cs.DidResume {
365                         t.Fatalf("Subsequent connection unexpectedly didn't resume")
366                 }
367                 if cs.VerifiedChains == nil {
368                         t.Fatalf("Dial #%d: cs.VerifiedChains == nil", i)
369                 }
370                 if err := c.VerifyHostname("www.google.com"); err != nil {
371                         t.Fatalf("verify www.google.com #%d: %v", i, err)
372                 }
373                 c.Close()
374         }
375 }
376
377 func TestConnCloseBreakingWrite(t *testing.T) {
378         ln := newLocalListener(t)
379         defer ln.Close()
380
381         srvCh := make(chan *Conn, 1)
382         var serr error
383         var sconn net.Conn
384         go func() {
385                 var err error
386                 sconn, err = ln.Accept()
387                 if err != nil {
388                         serr = err
389                         srvCh <- nil
390                         return
391                 }
392                 serverConfig := *testConfig
393                 srv := Server(sconn, &serverConfig)
394                 if err := srv.Handshake(); err != nil {
395                         serr = fmt.Errorf("handshake: %v", err)
396                         srvCh <- nil
397                         return
398                 }
399                 srvCh <- srv
400         }()
401
402         cconn, err := net.Dial("tcp", ln.Addr().String())
403         if err != nil {
404                 t.Fatal(err)
405         }
406         defer cconn.Close()
407
408         conn := &changeImplConn{
409                 Conn: cconn,
410         }
411
412         clientConfig := *testConfig
413         tconn := Client(conn, &clientConfig)
414         if err := tconn.Handshake(); err != nil {
415                 t.Fatal(err)
416         }
417
418         srv := <-srvCh
419         if srv == nil {
420                 t.Fatal(serr)
421         }
422         defer sconn.Close()
423
424         connClosed := make(chan struct{})
425         conn.closeFunc = func() error {
426                 close(connClosed)
427                 return nil
428         }
429
430         inWrite := make(chan bool, 1)
431         var errConnClosed = errors.New("conn closed for test")
432         conn.writeFunc = func(p []byte) (n int, err error) {
433                 inWrite <- true
434                 <-connClosed
435                 return 0, errConnClosed
436         }
437
438         closeReturned := make(chan bool, 1)
439         go func() {
440                 <-inWrite
441                 tconn.Close() // test that this doesn't block forever.
442                 closeReturned <- true
443         }()
444
445         _, err = tconn.Write([]byte("foo"))
446         if err != errConnClosed {
447                 t.Errorf("Write error = %v; want errConnClosed", err)
448         }
449
450         <-closeReturned
451         if err := tconn.Close(); err != errClosed {
452                 t.Errorf("Close error = %v; want errClosed", err)
453         }
454 }
455
456 // changeImplConn is a net.Conn which can change its Write and Close
457 // methods.
458 type changeImplConn struct {
459         net.Conn
460         writeFunc func([]byte) (int, error)
461         closeFunc func() error
462 }
463
464 func (w *changeImplConn) Write(p []byte) (n int, err error) {
465         if w.writeFunc != nil {
466                 return w.writeFunc(p)
467         }
468         return w.Conn.Write(p)
469 }
470
471 func (w *changeImplConn) Close() error {
472         if w.closeFunc != nil {
473                 return w.closeFunc()
474         }
475         return w.Conn.Close()
476 }
477
478 func throughput(b *testing.B, totalBytes int64, dynamicRecordSizingDisabled bool) {
479         ln := newLocalListener(b)
480         defer ln.Close()
481
482         N := b.N
483
484         var serr error
485         go func() {
486                 for i := 0; i < N; i++ {
487                         sconn, err := ln.Accept()
488                         if err != nil {
489                                 serr = err
490                                 return
491                         }
492                         serverConfig := *testConfig
493                         serverConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled
494                         srv := Server(sconn, &serverConfig)
495                         if err := srv.Handshake(); err != nil {
496                                 serr = fmt.Errorf("handshake: %v", err)
497                                 return
498                         }
499                         io.Copy(srv, srv)
500                 }
501         }()
502
503         b.SetBytes(totalBytes)
504         clientConfig := *testConfig
505         clientConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled
506
507         buf := make([]byte, 1<<16)
508         chunks := int(math.Ceil(float64(totalBytes) / float64(len(buf))))
509         for i := 0; i < N; i++ {
510                 conn, err := Dial("tcp", ln.Addr().String(), &clientConfig)
511                 if err != nil {
512                         b.Fatal(err)
513                 }
514                 for j := 0; j < chunks; j++ {
515                         _, err := conn.Write(buf)
516                         if err != nil {
517                                 b.Fatal(err)
518                         }
519                         _, err = io.ReadFull(conn, buf)
520                         if err != nil {
521                                 b.Fatal(err)
522                         }
523                 }
524                 conn.Close()
525         }
526 }
527
528 func BenchmarkThroughput(b *testing.B) {
529         for _, mode := range []string{"Max", "Dynamic"} {
530                 for size := 1; size <= 64; size<<=1{
531                         name := fmt.Sprintf("%sPacket/%dMB", mode, size)
532                         b.Run(name, func(b *testing.B) {
533                                 throughput(b, int64(size<<20), mode == "Max")
534                         })
535                 }
536         }
537 }
538
539 type slowConn struct {
540         net.Conn
541         bps int
542 }
543
544 func (c *slowConn) Write(p []byte) (int, error) {
545         if c.bps == 0 {
546                 panic("too slow")
547         }
548         t0 := time.Now()
549         wrote := 0
550         for wrote < len(p) {
551                 time.Sleep(100*time.Microsecond)
552                 allowed := int(time.Since(t0).Seconds() * float64(c.bps)) / 8
553                 if allowed > len(p) {
554                         allowed = len(p)
555                 }
556                 if wrote < allowed {
557                         n, err := c.Conn.Write(p[wrote:allowed])
558                         wrote += n
559                         if err != nil {
560                                 return wrote, err
561                         }
562                 }
563         }
564         return len(p), nil
565 }
566
567 func latency(b *testing.B, bps int, dynamicRecordSizingDisabled bool) {
568         ln := newLocalListener(b)
569         defer ln.Close()
570
571         N := b.N
572
573         var serr error
574         go func() {
575                 for i := 0; i < N; i++ {
576                         sconn, err := ln.Accept()
577                         if err != nil {
578                                 serr = err
579                                 return
580                         }
581                         serverConfig := *testConfig
582                         serverConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled
583                         srv := Server(&slowConn{sconn, bps}, &serverConfig)
584                         if err := srv.Handshake(); err != nil {
585                                 serr = fmt.Errorf("handshake: %v", err)
586                                 return
587                         }
588                         io.Copy(srv, srv)
589                 }
590         }()
591
592         clientConfig := *testConfig
593         clientConfig.DynamicRecordSizingDisabled = dynamicRecordSizingDisabled
594
595         buf := make([]byte, 16384)
596         peek := make([]byte, 1)
597
598         for i := 0; i < N; i++ {
599                 conn, err := Dial("tcp", ln.Addr().String(), &clientConfig)
600                 if err != nil {
601                         b.Fatal(err)
602                 }
603                 // make sure we're connected and previous connection has stopped
604                 if _, err := conn.Write(buf[:1]); err != nil {
605                         b.Fatal(err)
606                 }
607                 if _, err := io.ReadFull(conn, peek); err != nil {
608                         b.Fatal(err)
609                 }
610                 if _, err := conn.Write(buf); err != nil {
611                         b.Fatal(err)
612                 }
613                 if _, err = io.ReadFull(conn, peek); err != nil {
614                         b.Fatal(err)
615                 }
616                 conn.Close()
617         }
618 }
619
620
621 func BenchmarkLatency(b *testing.B) {
622         for _, mode := range []string{"Max", "Dynamic"} {
623                 for _, kbps := range []int{200, 500, 1000, 2000, 5000} {
624                         name := fmt.Sprintf("%sPacket/%dkbps", mode, kbps)
625                         b.Run(name, func(b *testing.B) {
626                                 latency(b, kbps*1000, mode == "Max")
627                         })
628                 }
629         }
630 }