Skip to content

net: enable happy eyeballs by default #22225

@bobrik

Description

@bobrik

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 bool

This 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions