-
Notifications
You must be signed in to change notification settings - Fork 18.7k
Description
What version of Go are you using (go version)?
go version go1.9.1 linux/amd64
Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (go env)?
I suspect any OS, but here's what I'm using:
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/ivan/go"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
What did you do?
Run the following program:
package main
import (
"log"
"net"
"time"
)
func main() {
d := net.Dialer{
Timeout: 8 * time.Second,
}
s := time.Now()
// IPv4 is example.com, IPv6 is a blackhole
//
// $ host broken-ipv6-working-ipv4.bobrik.name
// broken-ipv6-working-ipv4.bobrik.name has address 93.184.216.34
// broken-ipv6-working-ipv4.bobrik.name has IPv6 address 100::1
c, err := d.Dial("tcp", "broken-ipv6-working-ipv4.bobrik.name:80")
if err != nil {
log.Fatal(err)
}
log.Printf("Dial to %v completed in %v", c.RemoteAddr(), time.Since(s))
}What did you expect to see?
Connection succeeding to IPv4 address with no human perceptible delay, working IPv6 or not.
What did you see instead?
2017/10/12 02:39:42 Dial to 93.184.216.34:80 completed in 4.016407843s
This is the half of net.Dialer.Timeout.
What happened here is that my custom dialer did not set net.Dialer.DualStack to true explicitly.
Current docs: https://github.com/golang/go/blob/d2826d3e06/src/net/dial.go#L46-L51
// DualStack enables RFC 6555-compliant "Happy Eyeballs"
// dialing when the network is "tcp" and the host in the
// address parameter resolves to both IPv4 and IPv6 addresses.
// This allows a client to tolerate networks where one address
// family is silently broken.
DualStack boolThis setting is off by default in zero-value net.Dialer, net.Dial() and friends. However, it was turned on in http.DefaultTransport: #15324.
We fixed an issue in Grafana that caused 15s stalls for us:
Wikipedia says:
Implementations of Happy Eyeballs stacks exist in Google's Chrome web browser, Opera 12.10, Firefox version 13, OS X, and cURL.
I think Go should also have this on by default. This protects users from indefinite (half of default infinite connect timeout is still infinity) stalls if resolver returns both A and AAAA (or even AAAAA), but the network drops fancy IPv6 packets on the floor.
Current name DualStack is also slightly confusing, because Go dials both stacks in either case. Setting only controls Happy Eyeballs part, so having NoHappyEyeballs instead would allow zero-valued dialers to work seamlessly with broken IPv6.
If we end up not enabling Happy Eyeballs by default, this issue will at least hold the reason why.
cc @bradfitz