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