1
0
mirror of https://github.com/golang/go synced 2024-10-05 21:21:21 -06:00
go/src/net/dial.go

303 lines
9.0 KiB
Go
Raw Normal View History

// Copyright 2010 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 (
"errors"
"time"
)
// A Dialer contains options for connecting to an address.
//
// The zero value for each field is equivalent to dialing
// without that option. Dialing with the zero value of Dialer
// is therefore equivalent to just calling the Dial function.
type Dialer struct {
// Timeout is the maximum amount of time a dial will wait for
// a connect to complete. If Deadline is also set, it may fail
// earlier.
//
// The default is no timeout.
//
// With or without a timeout, the operating system may impose
// its own earlier timeout. For instance, TCP timeouts are
// often around 3 minutes.
Timeout time.Duration
// Deadline is the absolute point in time after which dials
// will fail. If Timeout is set, it may fail earlier.
// Zero means no deadline, or dependent on the operating system
// as with the Timeout option.
Deadline time.Time
// LocalAddr is the local address to use when dialing an
// address. The address must be of a compatible type for the
// network being dialed.
// If nil, a local address is automatically chosen.
LocalAddr Addr
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
// DualStack allows a single dial to attempt to establish
// multiple IPv4 and IPv6 connections and to return the first
// established connection when the network is "tcp" and the
// destination is a host name that has multiple address family
// DNS records.
DualStack bool
// KeepAlive specifies the keep-alive period for an active
// network connection.
// If zero, keep-alives are not enabled. Network protocols
// that do not support keep-alives ignore this field.
KeepAlive time.Duration
}
// Return either now+Timeout or Deadline, whichever comes first.
// Or zero, if neither is set.
func (d *Dialer) deadline() time.Time {
if d.Timeout == 0 {
return d.Deadline
}
timeoutDeadline := time.Now().Add(d.Timeout)
if d.Deadline.IsZero() || timeoutDeadline.Before(d.Deadline) {
return timeoutDeadline
} else {
return d.Deadline
}
}
func parseNetwork(net string) (afnet string, proto int, err error) {
i := last(net, ':')
if i < 0 { // no colon
switch net {
case "tcp", "tcp4", "tcp6":
case "udp", "udp4", "udp6":
case "ip", "ip4", "ip6":
case "unix", "unixgram", "unixpacket":
default:
return "", 0, UnknownNetworkError(net)
}
return net, 0, nil
}
afnet = net[:i]
switch afnet {
case "ip", "ip4", "ip6":
protostr := net[i+1:]
proto, i, ok := dtoi(protostr, 0)
if !ok || i != len(protostr) {
proto, err = lookupProtocol(protostr)
if err != nil {
return "", 0, err
}
}
return afnet, proto, nil
}
return "", 0, UnknownNetworkError(net)
}
func resolveAddr(op, net, addr string, deadline time.Time) (netaddr, error) {
afnet, _, err := parseNetwork(net)
if err != nil {
return nil, err
}
if op == "dial" && addr == "" {
return nil, errMissingAddress
}
switch afnet {
case "unix", "unixgram", "unixpacket":
return ResolveUnixAddr(afnet, addr)
}
return resolveInternetAddr(afnet, addr, deadline)
}
// Dial connects to the address on the named network.
//
// Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only),
// "udp", "udp4" (IPv4-only), "udp6" (IPv6-only), "ip", "ip4"
// (IPv4-only), "ip6" (IPv6-only), "unix", "unixgram" and
// "unixpacket".
//
// For TCP and UDP networks, addresses have the form host:port.
// If host is a literal IPv6 address it must be enclosed
// in square brackets as in "[::1]:80" or "[ipv6-host%zone]:80".
// The functions JoinHostPort and SplitHostPort manipulate addresses
// in this form.
//
// Examples:
// Dial("tcp", "12.34.56.78:80")
// Dial("tcp", "google.com:http")
// Dial("tcp", "[2001:db8::1]:http")
// Dial("tcp", "[fe80::1%lo0]:80")
//
// For IP networks, the network must be "ip", "ip4" or "ip6" followed
// by a colon and a protocol number or name and the addr must be a
// literal IP address.
//
// Examples:
// Dial("ip4:1", "127.0.0.1")
// Dial("ip6:ospf", "::1")
//
// For Unix networks, the address must be a file system path.
func Dial(network, address string) (Conn, error) {
var d Dialer
return d.Dial(network, address)
}
// DialTimeout acts like Dial but takes a timeout.
// The timeout includes name resolution, if required.
func DialTimeout(network, address string, timeout time.Duration) (Conn, error) {
d := Dialer{Timeout: timeout}
return d.Dial(network, address)
}
// Dial connects to the address on the named network.
//
// See func Dial for a description of the network and address
// parameters.
func (d *Dialer) Dial(network, address string) (Conn, error) {
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
ra, err := resolveAddr("dial", network, address, d.deadline())
if err != nil {
return nil, &OpError{Op: "dial", Net: network, Addr: nil, Err: err}
}
dialer := func(deadline time.Time) (Conn, error) {
return dialSingle(network, address, d.LocalAddr, ra.toAddr(), deadline)
}
if ras, ok := ra.(addrList); ok && d.DualStack && network == "tcp" {
dialer = func(deadline time.Time) (Conn, error) {
return dialMulti(network, address, d.LocalAddr, ras, deadline)
}
}
c, err := dial(network, ra.toAddr(), dialer, d.deadline())
if d.KeepAlive > 0 && err == nil {
if tc, ok := c.(*TCPConn); ok {
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(d.KeepAlive)
testHookSetKeepAlive()
}
}
return c, err
}
var testHookSetKeepAlive = func() {} // changed by dial_test.go
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
// dialMulti attempts to establish connections to each destination of
// the list of addresses. It will return the first established
// connection and close the other connections. Otherwise it returns
// error on the last attempt.
func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Conn, error) {
type racer struct {
Conn
error
}
// Sig controls the flow of dial results on lane. It passes a
// token to the next racer and also indicates the end of flow
// by using closed channel.
sig := make(chan bool, 1)
lane := make(chan racer, 1)
for _, ra := range ras {
go func(ra Addr) {
c, err := dialSingle(net, addr, la, ra, deadline)
if _, ok := <-sig; ok {
lane <- racer{c, 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
} else if err == nil {
// We have to return the resources
// that belong to the other
// connections here for avoiding
// unnecessary resource starvation.
c.Close()
}
}(ra.toAddr())
}
defer close(sig)
lastErr := errTimeout
nracers := len(ras)
for nracers > 0 {
sig <- true
racer := <-lane
if racer.error == nil {
return racer.Conn, 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
}
lastErr = racer.error
nracers--
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 nil, lastErr
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
}
// dialSingle attempts to establish and returns a single connection to
// the destination address.
func dialSingle(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) {
if la != nil && la.Network() != ra.Network() {
return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: errors.New("mismatched local address type " + la.Network())}
}
switch ra := ra.(type) {
case *TCPAddr:
la, _ := la.(*TCPAddr)
c, err = dialTCP(net, la, ra, deadline)
case *UDPAddr:
la, _ := la.(*UDPAddr)
c, err = dialUDP(net, la, ra, deadline)
case *IPAddr:
la, _ := la.(*IPAddr)
c, err = dialIP(net, la, ra, deadline)
case *UnixAddr:
la, _ := la.(*UnixAddr)
c, err = dialUnix(net, la, ra, deadline)
default:
return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: addr}}
}
if err != nil {
return nil, err // c is non-nil interface containing nil pointer
}
return c, nil
}
// Listen announces on the local network address laddr.
// The network net must be a stream-oriented network: "tcp", "tcp4",
// "tcp6", "unix" or "unixpacket".
// See Dial for the syntax of laddr.
func Listen(net, laddr string) (Listener, error) {
la, err := resolveAddr("listen", net, laddr, noDeadline)
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
}
var l Listener
switch la := la.toAddr().(type) {
case *TCPAddr:
l, err = ListenTCP(net, la)
case *UnixAddr:
l, err = ListenUnix(net, la)
default:
return nil, &OpError{Op: "listen", Net: net, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: laddr}}
}
if err != nil {
return nil, err // l is non-nil interface containing nil pointer
}
return l, nil
}
// ListenPacket announces on the local network address laddr.
// The network net must be a packet-oriented network: "udp", "udp4",
// "udp6", "ip", "ip4", "ip6" or "unixgram".
// See Dial for the syntax of laddr.
func ListenPacket(net, laddr string) (PacketConn, error) {
la, err := resolveAddr("listen", net, laddr, noDeadline)
if err != nil {
return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err}
}
var l PacketConn
switch la := la.toAddr().(type) {
case *UDPAddr:
l, err = ListenUDP(net, la)
case *IPAddr:
l, err = ListenIP(net, la)
case *UnixAddr:
l, err = ListenUnixgram(net, la)
default:
return nil, &OpError{Op: "listen", Net: net, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: laddr}}
}
if err != nil {
return nil, err // l is non-nil interface containing nil pointer
}
return l, nil
}