2011-12-20 14:17:39 -07:00
|
|
|
// Copyright 2011 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package net
|
|
|
|
|
|
|
|
import (
|
2015-12-14 15:21:48 -07:00
|
|
|
"internal/testenv"
|
2015-04-10 15:15:54 -06:00
|
|
|
"io"
|
2015-04-01 07:01:24 -06:00
|
|
|
"net/internal/socktest"
|
2011-12-20 14:17:39 -07:00
|
|
|
"runtime"
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
"sync"
|
2011-12-20 14:17:39 -07:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2015-04-30 16:49:12 -06:00
|
|
|
var prohibitionaryDialArgTests = []struct {
|
|
|
|
network string
|
|
|
|
address string
|
|
|
|
}{
|
|
|
|
{"tcp6", "127.0.0.1"},
|
|
|
|
{"tcp6", "::ffff:127.0.0.1"},
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestProhibitionaryDialArg(t *testing.T) {
|
|
|
|
switch runtime.GOOS {
|
|
|
|
case "plan9":
|
|
|
|
t.Skipf("not supported on %s", runtime.GOOS)
|
|
|
|
}
|
|
|
|
if testing.Short() || !*testExternal {
|
|
|
|
t.Skip("avoid external network")
|
|
|
|
}
|
|
|
|
if !supportsIPv4map {
|
|
|
|
t.Skip("mapping ipv4 address inside ipv6 address not supported")
|
|
|
|
}
|
|
|
|
|
|
|
|
ln, err := Listen("tcp", "[::]:0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer ln.Close()
|
|
|
|
|
|
|
|
_, port, err := SplitHostPort(ln.Addr().String())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tt := range prohibitionaryDialArgTests {
|
|
|
|
c, err := Dial(tt.network, JoinHostPort(tt.address, port))
|
|
|
|
if err == nil {
|
|
|
|
c.Close()
|
|
|
|
t.Errorf("#%d: %v", i, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-12 21:25:55 -07:00
|
|
|
func TestSelfConnect(t *testing.T) {
|
2012-02-13 19:51:38 -07:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
// TODO(brainman): do not know why it hangs.
|
2015-04-30 21:38:42 -06:00
|
|
|
t.Skip("known-broken test on windows")
|
2012-02-13 19:51:38 -07:00
|
|
|
}
|
2014-09-16 12:02:59 -06:00
|
|
|
|
2012-02-12 21:25:55 -07:00
|
|
|
// Test that Dial does not honor self-connects.
|
|
|
|
// See the comment in DialTCP.
|
|
|
|
|
|
|
|
// Find a port that would be used as a local address.
|
|
|
|
l, err := Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
c, err := Dial("tcp", l.Addr().String())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
addr := c.LocalAddr().String()
|
|
|
|
c.Close()
|
|
|
|
l.Close()
|
|
|
|
|
|
|
|
// Try to connect to that address repeatedly.
|
|
|
|
n := 100000
|
|
|
|
if testing.Short() {
|
|
|
|
n = 1000
|
|
|
|
}
|
2012-02-13 22:40:37 -07:00
|
|
|
switch runtime.GOOS {
|
2014-02-24 20:31:01 -07:00
|
|
|
case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd", "plan9", "solaris", "windows":
|
2012-02-13 22:40:37 -07:00
|
|
|
// Non-Linux systems take a long time to figure
|
|
|
|
// out that there is nothing listening on localhost.
|
|
|
|
n = 100
|
|
|
|
}
|
2012-02-12 21:25:55 -07:00
|
|
|
for i := 0; i < n; i++ {
|
2013-12-27 09:49:47 -07:00
|
|
|
c, err := DialTimeout("tcp", addr, time.Millisecond)
|
2012-02-12 21:25:55 -07:00
|
|
|
if err == nil {
|
2014-09-16 12:02:59 -06:00
|
|
|
if c.LocalAddr().String() == addr {
|
|
|
|
t.Errorf("#%d: Dial %q self-connect", i, addr)
|
|
|
|
} else {
|
|
|
|
t.Logf("#%d: Dial %q succeeded - possibly racing with other listener", i, addr)
|
|
|
|
}
|
2012-02-12 21:25:55 -07:00
|
|
|
c.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-02-23 19:58:30 -07:00
|
|
|
|
2012-11-08 09:35:16 -07:00
|
|
|
func TestDialTimeoutFDLeak(t *testing.T) {
|
2015-04-01 07:01:24 -06:00
|
|
|
switch runtime.GOOS {
|
|
|
|
case "plan9":
|
|
|
|
t.Skipf("%s does not have full support of socktest", runtime.GOOS)
|
2012-11-08 09:35:16 -07:00
|
|
|
}
|
|
|
|
|
2015-04-01 07:01:24 -06:00
|
|
|
const T = 100 * time.Millisecond
|
2012-11-08 09:35:16 -07:00
|
|
|
|
2015-04-01 07:01:24 -06:00
|
|
|
switch runtime.GOOS {
|
|
|
|
case "plan9", "windows":
|
|
|
|
origTestHookDialChannel := testHookDialChannel
|
|
|
|
testHookDialChannel = func() { time.Sleep(2 * T) }
|
|
|
|
defer func() { testHookDialChannel = origTestHookDialChannel }()
|
|
|
|
if runtime.GOOS == "plan9" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
fallthrough
|
|
|
|
default:
|
|
|
|
sw.Set(socktest.FilterConnect, func(so *socktest.Status) (socktest.AfterFilter, error) {
|
|
|
|
time.Sleep(2 * T)
|
|
|
|
return nil, errTimeout
|
|
|
|
})
|
|
|
|
defer sw.Set(socktest.FilterConnect, nil)
|
2012-11-08 09:35:16 -07:00
|
|
|
}
|
|
|
|
|
2015-07-26 21:19:00 -06:00
|
|
|
// Avoid tracking open-close jitterbugs between netFD and
|
|
|
|
// socket that leads to confusion of information inside
|
|
|
|
// socktest.Switch.
|
|
|
|
// It may happen when the Dial call bumps against TCP
|
|
|
|
// simultaneous open. See selfConnect in tcpsock_posix.go.
|
|
|
|
defer func() {
|
|
|
|
sw.Set(socktest.FilterClose, nil)
|
|
|
|
forceCloseSockets()
|
|
|
|
}()
|
|
|
|
var mu sync.Mutex
|
|
|
|
var attempts int
|
|
|
|
sw.Set(socktest.FilterClose, func(so *socktest.Status) (socktest.AfterFilter, error) {
|
|
|
|
mu.Lock()
|
|
|
|
attempts++
|
|
|
|
mu.Unlock()
|
|
|
|
return nil, errTimedout
|
|
|
|
})
|
|
|
|
|
2015-04-01 07:01:24 -06:00
|
|
|
const N = 100
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(N)
|
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
|
|
|
// This dial never starts to send any SYN
|
|
|
|
// segment because of above socket filter and
|
|
|
|
// test hook.
|
|
|
|
c, err := DialTimeout("tcp", "127.0.0.1:0", T)
|
|
|
|
if err == nil {
|
|
|
|
t.Errorf("unexpectedly established: tcp:%s->%s", c.LocalAddr(), c.RemoteAddr())
|
|
|
|
c.Close()
|
2012-11-08 09:35:16 -07:00
|
|
|
}
|
2015-04-01 07:01:24 -06:00
|
|
|
}()
|
2012-11-08 09:35:16 -07:00
|
|
|
}
|
2015-04-01 07:01:24 -06:00
|
|
|
wg.Wait()
|
2015-07-26 21:19:00 -06:00
|
|
|
if attempts < N {
|
|
|
|
t.Errorf("got %d; want >= %d", attempts, N)
|
2012-11-08 09:35:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-05 02:00:14 -06:00
|
|
|
func TestDialerDualStackFDLeak(t *testing.T) {
|
|
|
|
switch runtime.GOOS {
|
|
|
|
case "plan9":
|
|
|
|
t.Skipf("%s does not have full support of socktest", runtime.GOOS)
|
|
|
|
case "windows":
|
|
|
|
t.Skipf("not implemented a way to cancel dial racers in TCP SYN-SENT state on %s", runtime.GOOS)
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}
|
|
|
|
if !supportsIPv4 || !supportsIPv6 {
|
2015-05-06 17:20:42 -06:00
|
|
|
t.Skip("both IPv4 and IPv6 are required")
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}
|
|
|
|
|
2016-02-19 01:41:44 -07:00
|
|
|
before := sw.Sockets()
|
2015-04-05 02:00:14 -06:00
|
|
|
origTestHookLookupIP := testHookLookupIP
|
|
|
|
defer func() { testHookLookupIP = origTestHookLookupIP }()
|
|
|
|
testHookLookupIP = lookupLocalhost
|
2015-04-02 08:11:39 -06:00
|
|
|
handler := func(dss *dualStackServer, ln Listener) {
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
for {
|
2015-04-05 02:00:14 -06:00
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
return
|
|
|
|
}
|
2015-04-05 02:00:14 -06:00
|
|
|
c.Close()
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
dss, err := newDualStackServer([]streamListener{
|
2015-04-02 08:11:39 -06:00
|
|
|
{network: "tcp4", address: "127.0.0.1"},
|
|
|
|
{network: "tcp6", address: "::1"},
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
})
|
|
|
|
if err != nil {
|
2015-04-05 02:00:14 -06:00
|
|
|
t.Fatal(err)
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}
|
2015-04-02 08:11:39 -06:00
|
|
|
if err := dss.buildup(handler); err != nil {
|
2016-02-19 01:41:44 -07:00
|
|
|
dss.teardown()
|
2015-04-05 02:00:14 -06:00
|
|
|
t.Fatal(err)
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
const N = 10
|
2015-04-05 02:00:14 -06:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(N)
|
2016-02-19 01:41:44 -07:00
|
|
|
d := &Dialer{DualStack: true, Timeout: 100 * time.Millisecond}
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
for i := 0; i < N; i++ {
|
|
|
|
go func() {
|
|
|
|
defer wg.Done()
|
2015-04-05 02:00:14 -06:00
|
|
|
c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}
|
2015-04-05 02:00:14 -06:00
|
|
|
c.Close()
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}()
|
|
|
|
}
|
|
|
|
wg.Wait()
|
2016-02-19 01:41:44 -07:00
|
|
|
dss.teardown()
|
2015-04-05 02:00:14 -06:00
|
|
|
after := sw.Sockets()
|
|
|
|
if len(after) != len(before) {
|
|
|
|
t.Errorf("got %d; want %d", len(after), len(before))
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-10 15:15:54 -06:00
|
|
|
// Define a pair of blackholed (IPv4, IPv6) addresses, for which dialTCP is
|
|
|
|
// expected to hang until the timeout elapses. These addresses are reserved
|
|
|
|
// for benchmarking by RFC 6890.
|
|
|
|
const (
|
2016-02-08 21:25:38 -07:00
|
|
|
slowDst4 = "198.18.0.254"
|
|
|
|
slowDst6 = "2001:2::254"
|
2015-04-10 15:15:54 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
// In some environments, the slow IPs may be explicitly unreachable, and fail
|
|
|
|
// more quickly than expected. This test hook prevents dialTCP from returning
|
|
|
|
// before the deadline.
|
2015-12-14 15:21:48 -07:00
|
|
|
func slowDialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-chan struct{}) (*TCPConn, error) {
|
|
|
|
c, err := dialTCP(net, laddr, raddr, deadline, cancel)
|
2015-04-10 15:15:54 -06:00
|
|
|
if ParseIP(slowDst4).Equal(raddr.IP) || ParseIP(slowDst6).Equal(raddr.IP) {
|
2016-03-09 15:49:39 -07:00
|
|
|
// Wait for the deadline, or indefinitely if none exists.
|
|
|
|
var wait <-chan time.Time
|
|
|
|
if !deadline.IsZero() {
|
|
|
|
wait = time.After(deadline.Sub(time.Now()))
|
|
|
|
}
|
2016-02-08 21:25:38 -07:00
|
|
|
select {
|
|
|
|
case <-cancel:
|
2016-03-09 15:49:39 -07:00
|
|
|
case <-wait:
|
2016-02-08 21:25:38 -07:00
|
|
|
}
|
2015-04-10 15:15:54 -06:00
|
|
|
}
|
|
|
|
return c, err
|
|
|
|
}
|
|
|
|
|
2015-07-20 17:04:25 -06:00
|
|
|
func dialClosedPort() (actual, expected time.Duration) {
|
|
|
|
// Estimate the expected time for this platform.
|
|
|
|
// On Windows, dialing a closed port takes roughly 1 second,
|
|
|
|
// but other platforms should be instantaneous.
|
|
|
|
if runtime.GOOS == "windows" {
|
2015-07-30 08:39:07 -06:00
|
|
|
expected = 1500 * time.Millisecond
|
2015-07-20 17:04:25 -06:00
|
|
|
} else {
|
|
|
|
expected = 95 * time.Millisecond
|
|
|
|
}
|
|
|
|
|
2015-04-10 15:15:54 -06:00
|
|
|
l, err := Listen("tcp", "127.0.0.1:0")
|
|
|
|
if err != nil {
|
2015-07-20 17:04:25 -06:00
|
|
|
return 999 * time.Hour, expected
|
2015-04-10 15:15:54 -06:00
|
|
|
}
|
|
|
|
addr := l.Addr().String()
|
|
|
|
l.Close()
|
|
|
|
// On OpenBSD, interference from TestSelfConnect is mysteriously
|
|
|
|
// causing the first attempt to hang for a few seconds, so we throw
|
|
|
|
// away the first result and keep the second.
|
|
|
|
for i := 1; ; i++ {
|
|
|
|
startTime := time.Now()
|
|
|
|
c, err := Dial("tcp", addr)
|
|
|
|
if err == nil {
|
|
|
|
c.Close()
|
|
|
|
}
|
|
|
|
elapsed := time.Now().Sub(startTime)
|
|
|
|
if i == 2 {
|
2015-07-20 17:04:25 -06:00
|
|
|
return elapsed, expected
|
2015-04-10 15:15:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDialParallel(t *testing.T) {
|
|
|
|
if testing.Short() || !*testExternal {
|
|
|
|
t.Skip("avoid external network")
|
|
|
|
}
|
|
|
|
if !supportsIPv4 || !supportsIPv6 {
|
|
|
|
t.Skip("both IPv4 and IPv6 are required")
|
|
|
|
}
|
2016-02-08 21:25:38 -07:00
|
|
|
if runtime.GOOS == "plan9" {
|
|
|
|
t.Skip("skipping on plan9; cannot cancel dialTCP, golang.org/issue/11225")
|
|
|
|
}
|
2015-04-10 15:15:54 -06:00
|
|
|
|
2015-07-20 17:04:25 -06:00
|
|
|
closedPortDelay, expectClosedPortDelay := dialClosedPort()
|
2015-04-10 15:15:54 -06:00
|
|
|
if closedPortDelay > expectClosedPortDelay {
|
|
|
|
t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
|
|
|
|
}
|
|
|
|
|
|
|
|
const instant time.Duration = 0
|
|
|
|
const fallbackDelay = 200 * time.Millisecond
|
|
|
|
|
|
|
|
// Some cases will run quickly when "connection refused" is fast,
|
2016-03-01 16:21:55 -07:00
|
|
|
// or trigger the fallbackDelay on Windows. This value holds the
|
2015-04-10 15:15:54 -06:00
|
|
|
// lesser of the two delays.
|
|
|
|
var closedPortOrFallbackDelay time.Duration
|
|
|
|
if closedPortDelay < fallbackDelay {
|
|
|
|
closedPortOrFallbackDelay = closedPortDelay
|
|
|
|
} else {
|
|
|
|
closedPortOrFallbackDelay = fallbackDelay
|
|
|
|
}
|
|
|
|
|
|
|
|
origTestHookDialTCP := testHookDialTCP
|
|
|
|
defer func() { testHookDialTCP = origTestHookDialTCP }()
|
|
|
|
testHookDialTCP = slowDialTCP
|
|
|
|
|
|
|
|
nCopies := func(s string, n int) []string {
|
|
|
|
out := make([]string, n)
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
out[i] = s
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
var testCases = []struct {
|
|
|
|
primaries []string
|
|
|
|
fallbacks []string
|
|
|
|
teardownNetwork string
|
|
|
|
expectOk bool
|
|
|
|
expectElapsed time.Duration
|
|
|
|
}{
|
|
|
|
// These should just work on the first try.
|
|
|
|
{[]string{"127.0.0.1"}, []string{}, "", true, instant},
|
|
|
|
{[]string{"::1"}, []string{}, "", true, instant},
|
|
|
|
{[]string{"127.0.0.1", "::1"}, []string{slowDst6}, "tcp6", true, instant},
|
|
|
|
{[]string{"::1", "127.0.0.1"}, []string{slowDst4}, "tcp4", true, instant},
|
|
|
|
// Primary is slow; fallback should kick in.
|
|
|
|
{[]string{slowDst4}, []string{"::1"}, "", true, fallbackDelay},
|
|
|
|
// Skip a "connection refused" in the primary thread.
|
|
|
|
{[]string{"127.0.0.1", "::1"}, []string{}, "tcp4", true, closedPortDelay},
|
|
|
|
{[]string{"::1", "127.0.0.1"}, []string{}, "tcp6", true, closedPortDelay},
|
|
|
|
// Skip a "connection refused" in the fallback thread.
|
|
|
|
{[]string{slowDst4, slowDst6}, []string{"::1", "127.0.0.1"}, "tcp6", true, fallbackDelay + closedPortDelay},
|
|
|
|
// Primary refused, fallback without delay.
|
|
|
|
{[]string{"127.0.0.1"}, []string{"::1"}, "tcp4", true, closedPortOrFallbackDelay},
|
|
|
|
{[]string{"::1"}, []string{"127.0.0.1"}, "tcp6", true, closedPortOrFallbackDelay},
|
|
|
|
// Everything is refused.
|
|
|
|
{[]string{"127.0.0.1"}, []string{}, "tcp4", false, closedPortDelay},
|
|
|
|
// Nothing to do; fail instantly.
|
|
|
|
{[]string{}, []string{}, "", false, instant},
|
|
|
|
// Connecting to tons of addresses should not trip the deadline.
|
|
|
|
{nCopies("::1", 1000), []string{}, "", true, instant},
|
|
|
|
}
|
|
|
|
|
|
|
|
handler := func(dss *dualStackServer, ln Listener) {
|
|
|
|
for {
|
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert a list of IP strings into TCPAddrs.
|
|
|
|
makeAddrs := func(ips []string, port string) addrList {
|
|
|
|
var out addrList
|
|
|
|
for _, ip := range ips {
|
|
|
|
addr, err := ResolveTCPAddr("tcp", JoinHostPort(ip, port))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
out = append(out, addr)
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tt := range testCases {
|
|
|
|
dss, err := newDualStackServer([]streamListener{
|
|
|
|
{network: "tcp4", address: "127.0.0.1"},
|
|
|
|
{network: "tcp6", address: "::1"},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer dss.teardown()
|
|
|
|
if err := dss.buildup(handler); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if tt.teardownNetwork != "" {
|
|
|
|
// Destroy one of the listening sockets, creating an unreachable port.
|
|
|
|
dss.teardownNetwork(tt.teardownNetwork)
|
|
|
|
}
|
|
|
|
|
|
|
|
primaries := makeAddrs(tt.primaries, dss.port)
|
|
|
|
fallbacks := makeAddrs(tt.fallbacks, dss.port)
|
2015-07-27 15:39:32 -06:00
|
|
|
d := Dialer{
|
|
|
|
FallbackDelay: fallbackDelay,
|
|
|
|
}
|
2015-04-10 15:15:54 -06:00
|
|
|
ctx := &dialContext{
|
2015-07-27 15:39:32 -06:00
|
|
|
Dialer: d,
|
|
|
|
network: "tcp",
|
|
|
|
address: "?",
|
|
|
|
finalDeadline: d.deadline(time.Now()),
|
2015-04-10 15:15:54 -06:00
|
|
|
}
|
|
|
|
startTime := time.Now()
|
2016-02-08 21:25:38 -07:00
|
|
|
c, err := dialParallel(ctx, primaries, fallbacks, nil)
|
2015-04-10 15:15:54 -06:00
|
|
|
elapsed := time.Now().Sub(startTime)
|
|
|
|
|
|
|
|
if c != nil {
|
|
|
|
c.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
if tt.expectOk && err != nil {
|
|
|
|
t.Errorf("#%d: got %v; want nil", i, err)
|
|
|
|
} else if !tt.expectOk && err == nil {
|
|
|
|
t.Errorf("#%d: got nil; want non-nil", i)
|
|
|
|
}
|
|
|
|
|
|
|
|
expectElapsedMin := tt.expectElapsed - 95*time.Millisecond
|
|
|
|
expectElapsedMax := tt.expectElapsed + 95*time.Millisecond
|
|
|
|
if !(elapsed >= expectElapsedMin) {
|
|
|
|
t.Errorf("#%d: got %v; want >= %v", i, elapsed, expectElapsedMin)
|
|
|
|
} else if !(elapsed <= expectElapsedMax) {
|
|
|
|
t.Errorf("#%d: got %v; want <= %v", i, elapsed, expectElapsedMax)
|
|
|
|
}
|
2016-02-08 21:25:38 -07:00
|
|
|
|
|
|
|
// Repeat each case, ensuring that it can be canceled quickly.
|
|
|
|
cancel := make(chan struct{})
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
time.Sleep(5 * time.Millisecond)
|
|
|
|
close(cancel)
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
startTime = time.Now()
|
|
|
|
c, err = dialParallel(ctx, primaries, fallbacks, cancel)
|
|
|
|
if c != nil {
|
|
|
|
c.Close()
|
|
|
|
}
|
|
|
|
elapsed = time.Now().Sub(startTime)
|
|
|
|
if elapsed > 100*time.Millisecond {
|
|
|
|
t.Errorf("#%d (cancel): got %v; want <= 100ms", i, elapsed)
|
|
|
|
}
|
|
|
|
wg.Wait()
|
2015-04-10 15:15:54 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func lookupSlowFast(fn func(string) ([]IPAddr, error), host string) ([]IPAddr, error) {
|
|
|
|
switch host {
|
|
|
|
case "slow6loopback4":
|
|
|
|
// Returns a slow IPv6 address, and a local IPv4 address.
|
|
|
|
return []IPAddr{
|
|
|
|
{IP: ParseIP(slowDst6)},
|
|
|
|
{IP: ParseIP("127.0.0.1")},
|
|
|
|
}, nil
|
|
|
|
default:
|
|
|
|
return fn(host)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDialerFallbackDelay(t *testing.T) {
|
|
|
|
if testing.Short() || !*testExternal {
|
|
|
|
t.Skip("avoid external network")
|
|
|
|
}
|
|
|
|
if !supportsIPv4 || !supportsIPv6 {
|
|
|
|
t.Skip("both IPv4 and IPv6 are required")
|
|
|
|
}
|
|
|
|
|
|
|
|
origTestHookLookupIP := testHookLookupIP
|
|
|
|
defer func() { testHookLookupIP = origTestHookLookupIP }()
|
|
|
|
testHookLookupIP = lookupSlowFast
|
|
|
|
|
|
|
|
origTestHookDialTCP := testHookDialTCP
|
|
|
|
defer func() { testHookDialTCP = origTestHookDialTCP }()
|
|
|
|
testHookDialTCP = slowDialTCP
|
|
|
|
|
|
|
|
var testCases = []struct {
|
|
|
|
dualstack bool
|
|
|
|
delay time.Duration
|
|
|
|
expectElapsed time.Duration
|
|
|
|
}{
|
|
|
|
// Use a very brief delay, which should fallback immediately.
|
|
|
|
{true, 1 * time.Nanosecond, 0},
|
|
|
|
// Use a 200ms explicit timeout.
|
|
|
|
{true, 200 * time.Millisecond, 200 * time.Millisecond},
|
|
|
|
// The default is 300ms.
|
|
|
|
{true, 0, 300 * time.Millisecond},
|
|
|
|
}
|
|
|
|
|
|
|
|
handler := func(dss *dualStackServer, ln Listener) {
|
|
|
|
for {
|
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dss, err := newDualStackServer([]streamListener{
|
|
|
|
{network: "tcp", address: "127.0.0.1"},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer dss.teardown()
|
|
|
|
if err := dss.buildup(handler); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, tt := range testCases {
|
2016-02-08 21:25:38 -07:00
|
|
|
d := &Dialer{DualStack: tt.dualstack, FallbackDelay: tt.delay}
|
2015-04-10 15:15:54 -06:00
|
|
|
|
|
|
|
startTime := time.Now()
|
|
|
|
c, err := d.Dial("tcp", JoinHostPort("slow6loopback4", dss.port))
|
|
|
|
elapsed := time.Now().Sub(startTime)
|
|
|
|
if err == nil {
|
|
|
|
c.Close()
|
|
|
|
} else if tt.dualstack {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
expectMin := tt.expectElapsed - 1*time.Millisecond
|
|
|
|
expectMax := tt.expectElapsed + 95*time.Millisecond
|
|
|
|
if !(elapsed >= expectMin) {
|
|
|
|
t.Errorf("#%d: got %v; want >= %v", i, elapsed, expectMin)
|
|
|
|
}
|
|
|
|
if !(elapsed <= expectMax) {
|
|
|
|
t.Errorf("#%d: got %v; want <= %v", i, elapsed, expectMax)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-08 21:25:38 -07:00
|
|
|
func TestDialParallelSpuriousConnection(t *testing.T) {
|
|
|
|
if !supportsIPv4 || !supportsIPv6 {
|
|
|
|
t.Skip("both IPv4 and IPv6 are required")
|
|
|
|
}
|
2015-10-16 10:28:42 -06:00
|
|
|
if runtime.GOOS == "plan9" {
|
2016-02-08 21:25:38 -07:00
|
|
|
t.Skip("skipping on plan9; cannot cancel dialTCP, golang.org/issue/11225")
|
|
|
|
}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(2)
|
|
|
|
handler := func(dss *dualStackServer, ln Listener) {
|
|
|
|
// Accept one connection per address.
|
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
// The client should close itself, without sending data.
|
|
|
|
c.SetReadDeadline(time.Now().Add(1 * time.Second))
|
|
|
|
var b [1]byte
|
|
|
|
if _, err := c.Read(b[:]); err != io.EOF {
|
|
|
|
t.Errorf("got %v; want %v", err, io.EOF)
|
|
|
|
}
|
|
|
|
c.Close()
|
|
|
|
wg.Done()
|
2015-10-16 10:28:42 -06:00
|
|
|
}
|
2016-02-08 21:25:38 -07:00
|
|
|
dss, err := newDualStackServer([]streamListener{
|
|
|
|
{network: "tcp4", address: "127.0.0.1"},
|
|
|
|
{network: "tcp6", address: "::1"},
|
|
|
|
})
|
2015-04-10 15:15:54 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2016-02-08 21:25:38 -07:00
|
|
|
defer dss.teardown()
|
|
|
|
if err := dss.buildup(handler); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
const fallbackDelay = 100 * time.Millisecond
|
|
|
|
|
|
|
|
origTestHookDialTCP := testHookDialTCP
|
|
|
|
defer func() { testHookDialTCP = origTestHookDialTCP }()
|
|
|
|
testHookDialTCP = func(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-chan struct{}) (*TCPConn, error) {
|
|
|
|
// Sleep long enough for Happy Eyeballs to kick in, and inhibit cancelation.
|
|
|
|
// This forces dialParallel to juggle two successful connections.
|
|
|
|
time.Sleep(fallbackDelay * 2)
|
|
|
|
cancel = nil
|
|
|
|
return dialTCP(net, laddr, raddr, deadline, cancel)
|
|
|
|
}
|
2015-04-10 15:15:54 -06:00
|
|
|
|
2016-02-08 21:25:38 -07:00
|
|
|
d := Dialer{
|
|
|
|
FallbackDelay: fallbackDelay,
|
|
|
|
}
|
2015-04-10 15:15:54 -06:00
|
|
|
ctx := &dialContext{
|
2015-07-27 15:39:32 -06:00
|
|
|
Dialer: d,
|
|
|
|
network: "tcp",
|
|
|
|
address: "?",
|
|
|
|
finalDeadline: d.deadline(time.Now()),
|
2015-04-10 15:15:54 -06:00
|
|
|
}
|
|
|
|
|
2016-02-08 21:25:38 -07:00
|
|
|
makeAddr := func(ip string) addrList {
|
|
|
|
addr, err := ResolveTCPAddr("tcp", JoinHostPort(ip, dss.port))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
return addrList{addr}
|
|
|
|
}
|
2015-04-10 15:15:54 -06:00
|
|
|
|
2016-02-08 21:25:38 -07:00
|
|
|
// dialParallel returns one connection (and closes the other.)
|
|
|
|
c, err := dialParallel(ctx, makeAddr("127.0.0.1"), makeAddr("::1"), nil)
|
2015-04-10 15:15:54 -06:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2016-02-08 21:25:38 -07:00
|
|
|
c.Close()
|
2015-04-10 15:15:54 -06:00
|
|
|
|
2016-02-08 21:25:38 -07:00
|
|
|
// The server should've seen both connections.
|
|
|
|
wg.Wait()
|
2015-04-10 15:15:54 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestDialerPartialDeadline(t *testing.T) {
|
|
|
|
now := time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
var testCases = []struct {
|
|
|
|
now time.Time
|
|
|
|
deadline time.Time
|
|
|
|
addrs int
|
|
|
|
expectDeadline time.Time
|
|
|
|
expectErr error
|
|
|
|
}{
|
|
|
|
// Regular division.
|
|
|
|
{now, now.Add(12 * time.Second), 1, now.Add(12 * time.Second), nil},
|
|
|
|
{now, now.Add(12 * time.Second), 2, now.Add(6 * time.Second), nil},
|
|
|
|
{now, now.Add(12 * time.Second), 3, now.Add(4 * time.Second), nil},
|
|
|
|
// Bump against the 2-second sane minimum.
|
|
|
|
{now, now.Add(12 * time.Second), 999, now.Add(2 * time.Second), nil},
|
|
|
|
// Total available is now below the sane minimum.
|
|
|
|
{now, now.Add(1900 * time.Millisecond), 999, now.Add(1900 * time.Millisecond), nil},
|
|
|
|
// Null deadline.
|
|
|
|
{now, noDeadline, 1, noDeadline, nil},
|
|
|
|
// Step the clock forward and cross the deadline.
|
|
|
|
{now.Add(-1 * time.Millisecond), now, 1, now, nil},
|
|
|
|
{now.Add(0 * time.Millisecond), now, 1, noDeadline, errTimeout},
|
|
|
|
{now.Add(1 * time.Millisecond), now, 1, noDeadline, errTimeout},
|
|
|
|
}
|
|
|
|
for i, tt := range testCases {
|
2015-07-20 17:04:25 -06:00
|
|
|
deadline, err := partialDeadline(tt.now, tt.deadline, tt.addrs)
|
2015-04-10 15:15:54 -06:00
|
|
|
if err != tt.expectErr {
|
|
|
|
t.Errorf("#%d: got %v; want %v", i, err, tt.expectErr)
|
|
|
|
}
|
|
|
|
if deadline != tt.expectDeadline {
|
|
|
|
t.Errorf("#%d: got %v; want %v", i, deadline, tt.expectDeadline)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-02 08:11:39 -06:00
|
|
|
func TestDialerLocalAddr(t *testing.T) {
|
2013-04-30 18:47:39 -06:00
|
|
|
ch := make(chan error, 1)
|
2015-04-02 08:11:39 -06:00
|
|
|
handler := func(ls *localServer, ln Listener) {
|
2013-04-30 18:47:39 -06:00
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
2015-04-30 21:38:42 -06:00
|
|
|
ch <- err
|
2013-04-30 18:47:39 -06:00
|
|
|
return
|
|
|
|
}
|
|
|
|
defer c.Close()
|
|
|
|
ch <- nil
|
2015-04-02 08:11:39 -06:00
|
|
|
}
|
|
|
|
ls, err := newLocalServer("tcp")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer ls.teardown()
|
|
|
|
if err := ls.buildup(handler); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2013-04-30 18:47:39 -06:00
|
|
|
|
2015-04-02 08:11:39 -06:00
|
|
|
laddr, err := ResolveTCPAddr(ls.Listener.Addr().Network(), ls.Listener.Addr().String())
|
2013-04-30 18:47:39 -06:00
|
|
|
if err != nil {
|
2015-04-30 21:38:42 -06:00
|
|
|
t.Fatal(err)
|
2013-04-30 18:47:39 -06:00
|
|
|
}
|
2015-04-02 08:11:39 -06:00
|
|
|
laddr.Port = 0
|
2013-04-30 18:47:39 -06:00
|
|
|
d := &Dialer{LocalAddr: laddr}
|
2015-04-02 08:11:39 -06:00
|
|
|
c, err := d.Dial(ls.Listener.Addr().Network(), ls.Addr().String())
|
2013-04-30 18:47:39 -06:00
|
|
|
if err != nil {
|
2015-04-30 21:38:42 -06:00
|
|
|
t.Fatal(err)
|
2013-04-30 18:47:39 -06:00
|
|
|
}
|
|
|
|
defer c.Close()
|
|
|
|
c.Read(make([]byte, 1))
|
|
|
|
err = <-ch
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
}
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
|
2015-04-02 08:11:39 -06:00
|
|
|
func TestDialerDualStack(t *testing.T) {
|
2015-04-05 02:00:14 -06:00
|
|
|
if !supportsIPv4 || !supportsIPv6 {
|
2015-05-06 17:20:42 -06:00
|
|
|
t.Skip("both IPv4 and IPv6 are required")
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}
|
|
|
|
|
2015-07-20 17:04:25 -06:00
|
|
|
closedPortDelay, expectClosedPortDelay := dialClosedPort()
|
|
|
|
if closedPortDelay > expectClosedPortDelay {
|
|
|
|
t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
|
|
|
|
}
|
|
|
|
|
2015-04-05 02:00:14 -06:00
|
|
|
origTestHookLookupIP := testHookLookupIP
|
|
|
|
defer func() { testHookLookupIP = origTestHookLookupIP }()
|
|
|
|
testHookLookupIP = lookupLocalhost
|
2015-04-02 08:11:39 -06:00
|
|
|
handler := func(dss *dualStackServer, ln Listener) {
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
for {
|
2015-04-05 02:00:14 -06:00
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
return
|
|
|
|
}
|
2015-04-05 02:00:14 -06:00
|
|
|
c.Close()
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-31 14:30:22 -06:00
|
|
|
var timeout = 150*time.Millisecond + closedPortDelay
|
2015-04-10 15:15:54 -06:00
|
|
|
for _, dualstack := range []bool{false, true} {
|
|
|
|
dss, err := newDualStackServer([]streamListener{
|
|
|
|
{network: "tcp4", address: "127.0.0.1"},
|
|
|
|
{network: "tcp6", address: "::1"},
|
|
|
|
})
|
2015-04-05 02:00:14 -06:00
|
|
|
if err != nil {
|
2015-04-10 15:15:54 -06:00
|
|
|
t.Fatal(err)
|
2015-04-05 02:00:14 -06:00
|
|
|
}
|
2015-04-10 15:15:54 -06:00
|
|
|
defer dss.teardown()
|
|
|
|
if err := dss.buildup(handler); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2015-07-20 17:04:25 -06:00
|
|
|
d := &Dialer{DualStack: dualstack, Timeout: timeout}
|
2015-04-10 15:15:54 -06:00
|
|
|
for range dss.lns {
|
|
|
|
c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
|
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
switch addr := c.LocalAddr().(*TCPAddr); {
|
|
|
|
case addr.IP.To4() != nil:
|
|
|
|
dss.teardownNetwork("tcp4")
|
|
|
|
case addr.IP.To16() != nil && addr.IP.To4() == nil:
|
|
|
|
dss.teardownNetwork("tcp6")
|
|
|
|
}
|
|
|
|
c.Close()
|
net: implement TCP connection setup with fast failover
This CL adds minimal support of Happy Eyeballs-like TCP connection
setup to Dialer API. Happy Eyeballs and derivation techniques are
described in the following:
- Happy Eyeballs: Success with Dual-Stack Hosts
http://tools.ietf.org/html/rfc6555
- Analysing Dual Stack Behaviour and IPv6 Quality
http://www.potaroo.net/presentations/2012-04-17-dual-stack-quality.pdf
Usually, the techniques consist of three components below.
- DNS query racers, that run A and AAAA queries in parallel or series
- A short list of destination addresses
- TCP SYN racers, that run IPv4 and IPv6 transport in parallel or series
This CL implements only the latter two. The existing DNS query
component gathers together A and AAAA records in series, so we don't
touch it here. This CL just uses extended resolveInternetAddr and makes
it possible to run multiple Dial racers in parallel.
For example, when the given destination is a DNS name and the name has
multiple address family A and AAAA records, and it happens on the TCP
wildcard network "tcp" with DualStack=true like the following:
(&net.Dialer{DualStack: true}).Dial("tcp", "www.example.com:80")
The function will return a first established connection either TCP over
IPv4 or TCP over IPv6, and close the other connection internally.
Fixes #3610.
Fixes #5267.
Benchmark results on freebsd/amd64 virtual machine, tip vs. tip+12416043:
benchmark old ns/op new ns/op delta
BenchmarkTCP4OneShot 50696 52141 +2.85%
BenchmarkTCP4OneShotTimeout 65775 66426 +0.99%
BenchmarkTCP4Persistent 10986 10457 -4.82%
BenchmarkTCP4PersistentTimeout 11207 10445 -6.80%
BenchmarkTCP6OneShot 62009 63718 +2.76%
BenchmarkTCP6OneShotTimeout 78351 79138 +1.00%
BenchmarkTCP6Persistent 14695 14659 -0.24%
BenchmarkTCP6PersistentTimeout 15032 14646 -2.57%
BenchmarkTCP4ConcurrentReadWrite 7215 6217 -13.83%
BenchmarkTCP6ConcurrentReadWrite 7528 7493 -0.46%
benchmark old allocs new allocs delta
BenchmarkTCP4OneShot 36 36 0.00%
BenchmarkTCP4OneShotTimeout 36 36 0.00%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 37 37 0.00%
BenchmarkTCP6OneShotTimeout 37 37 0.00%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
benchmark old bytes new bytes delta
BenchmarkTCP4OneShot 2500 2503 0.12%
BenchmarkTCP4OneShotTimeout 2508 2505 -0.12%
BenchmarkTCP4Persistent 0 0 n/a%
BenchmarkTCP4PersistentTimeout 0 0 n/a%
BenchmarkTCP6OneShot 2713 2707 -0.22%
BenchmarkTCP6OneShotTimeout 2722 2720 -0.07%
BenchmarkTCP6Persistent 0 0 n/a%
BenchmarkTCP6PersistentTimeout 0 0 n/a%
BenchmarkTCP4ConcurrentReadWrite 0 0 n/a%
BenchmarkTCP6ConcurrentReadWrite 0 0 n/a%
R=golang-dev, bradfitz, nightlyone, rsc
CC=golang-dev
https://golang.org/cl/12416043
2013-09-11 08:48:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-02-24 14:14:48 -07:00
|
|
|
|
|
|
|
func TestDialerKeepAlive(t *testing.T) {
|
2015-04-02 08:11:39 -06:00
|
|
|
handler := func(ls *localServer, ln Listener) {
|
2014-02-24 14:14:48 -07:00
|
|
|
for {
|
|
|
|
c, err := ln.Accept()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
c.Close()
|
|
|
|
}
|
2015-04-02 08:11:39 -06:00
|
|
|
}
|
|
|
|
ls, err := newLocalServer("tcp")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer ls.teardown()
|
|
|
|
if err := ls.buildup(handler); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2015-04-30 21:38:42 -06:00
|
|
|
defer func() { testHookSetKeepAlive = func() {} }()
|
2015-04-02 08:11:39 -06:00
|
|
|
|
2014-02-24 14:14:48 -07:00
|
|
|
for _, keepAlive := range []bool{false, true} {
|
|
|
|
got := false
|
|
|
|
testHookSetKeepAlive = func() { got = true }
|
|
|
|
var d Dialer
|
|
|
|
if keepAlive {
|
|
|
|
d.KeepAlive = 30 * time.Second
|
|
|
|
}
|
2015-04-02 08:11:39 -06:00
|
|
|
c, err := d.Dial("tcp", ls.Listener.Addr().String())
|
2014-02-24 14:14:48 -07:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
c.Close()
|
|
|
|
if got != keepAlive {
|
|
|
|
t.Errorf("Dialer.KeepAlive = %v: SetKeepAlive called = %v, want %v", d.KeepAlive, got, !got)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-12-14 15:21:48 -07:00
|
|
|
|
|
|
|
func TestDialCancel(t *testing.T) {
|
|
|
|
if runtime.GOOS == "plan9" || runtime.GOOS == "nacl" {
|
|
|
|
// plan9 is not implemented and nacl doesn't have
|
|
|
|
// external network access.
|
2015-12-15 17:35:23 -07:00
|
|
|
t.Skipf("skipping on %s", runtime.GOOS)
|
2015-12-14 15:21:48 -07:00
|
|
|
}
|
|
|
|
onGoBuildFarm := testenv.Builder() != ""
|
|
|
|
if testing.Short() && !onGoBuildFarm {
|
|
|
|
t.Skip("skipping in short mode")
|
|
|
|
}
|
|
|
|
|
|
|
|
blackholeIPPort := JoinHostPort(slowDst4, "1234")
|
|
|
|
if !supportsIPv4 {
|
|
|
|
blackholeIPPort = JoinHostPort(slowDst6, "1234")
|
|
|
|
}
|
|
|
|
|
|
|
|
ticker := time.NewTicker(10 * time.Millisecond)
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
const cancelTick = 5 // the timer tick we cancel the dial at
|
|
|
|
const timeoutTick = 100
|
|
|
|
|
|
|
|
var d Dialer
|
|
|
|
cancel := make(chan struct{})
|
|
|
|
d.Cancel = cancel
|
|
|
|
errc := make(chan error, 1)
|
|
|
|
connc := make(chan Conn, 1)
|
|
|
|
go func() {
|
|
|
|
if c, err := d.Dial("tcp", blackholeIPPort); err != nil {
|
|
|
|
errc <- err
|
|
|
|
} else {
|
|
|
|
connc <- c
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
ticks := 0
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
|
|
|
ticks++
|
|
|
|
if ticks == cancelTick {
|
|
|
|
close(cancel)
|
|
|
|
}
|
|
|
|
if ticks == timeoutTick {
|
|
|
|
t.Fatal("timeout waiting for dial to fail")
|
|
|
|
}
|
|
|
|
case c := <-connc:
|
|
|
|
c.Close()
|
|
|
|
t.Fatal("unexpected successful connection")
|
|
|
|
case err := <-errc:
|
2015-12-15 17:35:23 -07:00
|
|
|
if perr := parseDialError(err); perr != nil {
|
|
|
|
t.Error(perr)
|
|
|
|
}
|
2015-12-14 15:21:48 -07:00
|
|
|
if ticks < cancelTick {
|
|
|
|
t.Fatalf("dial error after %d ticks (%d before cancel sent): %v",
|
|
|
|
ticks, cancelTick-ticks, err)
|
|
|
|
}
|
|
|
|
if oe, ok := err.(*OpError); !ok || oe.Err != errCanceled {
|
|
|
|
t.Fatalf("dial error = %v (%T); want OpError with Err == errCanceled", err, err)
|
|
|
|
}
|
|
|
|
return // success.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|