Go proposal: Context-aware Dialer methods

Part of the Accepted! series, explaining the upcoming Go changes in simple terms.

Add context-aware, network-specific methods to the net.Dialer type.

Ver. 1.26 • Stdlib • Low impact

Summary

The net.Dialer type connects to the address using a given network (protocol) — TCP, UDP, IP, or Unix sockets.

The new context-aware Dialer methods (DialTCP, DialUDP, DialIP, and DialUnix) combine the efficiency of the existing network-specific net functions (which skip address resolution and dispatch) with the cancellation capabilities of Dialer.DialContext.

Motivation

The net package already has top-level functions for different networks (DialTCP, DialUDP, DialIP, and DialUnix), but these were made before context.Context was introduced, so they don't support cancellation:

func DialTCP(network string, laddr, raddr *TCPAddr) (*TCPConn, error)
func DialUDP(network string, laddr, raddr *UDPAddr) (*UDPConn, error)
func DialIP(network string, laddr, raddr *IPAddr) (*IPConn, error)
func DialUnix(network string, laddr, raddr *UnixAddr) (*UnixConn, error)

On the other hand, the net.Dialer type has a general-purpose DialContext method. It supports cancellation and can be used to connect to any of the known networks:

func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error)

However, if you already know the network type and address, using DialContext is a bit less efficient than network-specific functions like DialTCP due to:

  • Address resolution overhead: DialContext handles address resolution internally (like DNS lookups and converting to net.TCPAddr or net.UDPAddr) using the network and address strings you provide. Network-specific functions accept a pre-resolved address object, so they skip this step.

  • Network type dispatch: DialContext must route the call to the protocol-specific dialer. Network-specific functions already know which protocol to use, so they skip this step.

So, network-specific functions in the net package are more efficient, but they don't support cancellation. The Dialer type supports cancellation, but it's less efficient. This proposal aims to solve the mismatch by adding context-aware, network-specific methods to the Dialer type.

Also, adding new methods to the Dialer lets you use the newer address types from the netip package (like netip.AddrPort instead of net.TCPAddr), which are preferred in modern Go code.

Description

Add four new methods to the net.Dialer:

DialTCP(ctx context.Context, network string, laddr, raddr netip.AddrPort) (*TCPConn, error)
DialUDP(ctx context.Context, network string, laddr, raddr netip.AddrPort) (*UDPConn, error)
DialIP(ctx context.Context, network string, laddr, raddr netip.Addr) (*IPConn, error)
DialUnix(ctx context.Context, network string, laddr, raddr *UnixAddr) (*UnixConn, error)

The method signatures are similar to the existing top-level net functions, but they also accept a context and use the newer address types from the netip package.

Example

Use the DialTCP method to connect to a TCP server:

var d net.Dialer
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// Dialing will fail because the server isn't running.
raddr := netip.MustParseAddrPort("127.0.0.1:12345")
conn, err := d.DialTCP(ctx, "tcp", netip.AddrPort{}, raddr)
if err != nil {
    log.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()

if _, err := conn.Write([]byte("Hello, World!")); err != nil {
    log.Fatal(err)
}
Failed to dial: dial tcp 127.0.0.1:12345: connect: connection refused (exit status 1)

Use the DialUnix method to connect to a Unix socket:

var d net.Dialer
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// Dialing will fail because the server isn't running.
raddr := &net.UnixAddr{Name: "/path/to/unix.sock", Net: "unix"}
conn, err := d.DialUnix(ctx, "unix", nil, raddr)
if err != nil {
    log.Fatalf("Failed to dial: %v", err)
}
defer conn.Close()

if _, err := conn.Write([]byte("Hello, socket!")); err != nil {
    log.Fatal(err)
}
Failed to dial: dial unix /path/to/unix.sock: connect: no such file or directory (exit status 1)

In both cases, the dialing fails because I didn't bother to start the server in the playground :)

Further reading

𝗣 49097 • 𝗖𝗟 657296

★ Subscribe to keep up with new posts.