]> Cypherpunks repositories - gostls13.git/commitdiff
net: delete DialOpt and DialOption; add struct Dialer
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 2 Apr 2013 20:24:16 +0000 (13:24 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 2 Apr 2013 20:24:16 +0000 (13:24 -0700)
Per discussions on golang-nuts and golang-dev:
"Some concerns with DialOpt"
https://groups.google.com/d/msg/golang-nuts/Hfh9aqhXyUw/W3uYi8lOdKcJ
https://groups.google.com/d/msg/golang-dev/37omSQeWv4Y/KASGIfPpXh0J

R=golang-dev, google, r
CC=golang-dev
https://golang.org/cl/8274043

doc/go1.1.html
src/pkg/net/dial.go
src/pkg/net/dial_gen.go [new file with mode: 0644]
src/pkg/net/fd_plan9.go
src/pkg/net/fd_unix.go
src/pkg/net/fd_windows.go

index 75eb02d45c8bb7e1b31107a5ce0d9b2a9e039eb3..e2db58adcc0f1e0396f17bad4843425002edc91c 100644 (file)
@@ -779,16 +779,9 @@ Since this API change fixes a bug, it is permitted by the Go 1 compatibility rul
 </li>
 
 <li>
-The <a href="/pkg/net/"><code>net</code></a> package includes a new function,
-<a href="/pkg/net/#DialOpt"><code>DialOpt</code></a>, to supply options to
-<a href="/pkg/net/#Dial"><code>Dial</code></a>.
-Each option is represented by a new
-<a href="/pkg/net/#DialOption"><code>DialOption</code></a> interface.
-The new functions
-<a href="/pkg/net/#Deadline"><code>Deadline</code></a>,
-<a href="/pkg/net/#Timeout"><code>Timeout</code></a>,
-<a href="/pkg/net/#Network"><code>Network</code></a>, and
-<a href="/pkg/net/#LocalAddress"><code>LocalAddress</code></a> return a <code>DialOption</code>.
+The <a href="/pkg/net/"><code>net</code></a> package includes a new type,
+<a href="/pkg/net/#Dialer"><code>Dialer</code></a>, to supply options to
+<a href="/pkg/net/#Dialer.Dial"><code>Dial</code></a>.
 </li>
 
 <li>
index da5f7e30204afd1dbcdee1a9dbece43923752d29..b18d283626c71203d4884422756ef96dcd84d17e 100644 (file)
@@ -9,112 +9,48 @@ import (
        "time"
 )
 
-// A DialOption modifies a DialOpt call.
-type DialOption interface {
-       setDialOpt(*dialOpts)
-}
-
-var noLocalAddr Addr // nil
-
-// dialOpts holds all the dial options, populated by a DialOption's
-// setDialOpt.
+// A Dialer contains options for connecting to an address.
 //
-// All fields may be their zero value.
-type dialOpts struct {
-       deadline        time.Time
-       localAddr       Addr
-       network         string // if empty, "tcp"
-       deferredConnect bool
-}
-
-func (o *dialOpts) net() string {
-       if o.network == "" {
-               return "tcp"
+// 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
+}
+
+// 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
        }
-       return o.network
-}
-
-var (
-       // TCP is a dial option to dial with TCP (over IPv4 or IPv6).
-       TCP = Network("tcp")
-
-       // UDP is a dial option to dial with UDP (over IPv4 or IPv6).
-       UDP = Network("udp")
-)
-
-// Network returns a DialOption to dial using the given 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 IP networks, net must be "ip", "ip4" or "ip6" followed
-// by a colon and a protocol number or name, such as
-// "ipv4:1" or "ip6:ospf".
-func Network(net string) DialOption {
-       return dialNetwork(net)
-}
-
-type dialNetwork string
-
-func (s dialNetwork) setDialOpt(o *dialOpts) {
-       o.network = string(s)
-}
-
-// Deadline returns a DialOption to fail a dial that doesn't
-// complete before t.
-func Deadline(t time.Time) DialOption {
-       return dialDeadline(t)
-}
-
-type dialDeadline time.Time
-
-func (t dialDeadline) setDialOpt(o *dialOpts) {
-       o.deadline = time.Time(t)
-}
-
-// Timeout returns a DialOption to fail a dial that doesn't
-// complete within the provided duration.
-func Timeout(d time.Duration) DialOption {
-       return dialTimeoutOpt(d)
-}
-
-type dialTimeoutOpt time.Duration
-
-func (d dialTimeoutOpt) setDialOpt(o *dialOpts) {
-       o.deadline = time.Now().Add(time.Duration(d))
-}
-
-type tcpFastOpen struct{}
-
-func (tcpFastOpen) setDialOpt(o *dialOpts) {
-       o.deferredConnect = true
-}
-
-// TODO(bradfitz): implement this (golang.org/issue/4842) and unexport this.
-//
-// TCPFastTimeout returns an option to use TCP Fast Open (TFO) when
-// doing this dial. It is only valid for use with TCP connections.
-// Data sent over a TFO connection may be processed by the peer
-// multiple times, so should be used with caution.
-func todo_TCPFastTimeout() DialOption {
-       return tcpFastOpen{}
-}
-
-type localAddrOption struct {
-       la Addr
-}
-
-func (a localAddrOption) setDialOpt(o *dialOpts) {
-       o.localAddr = a.la
-}
-
-// LocalAddress returns a dial option to perform a dial with the
-// provided local address. The address must be of a compatible type
-// for the network being dialed.
-func LocalAddress(addr Addr) DialOption {
-       return localAddrOption{addr}
 }
 
 func parseNetwork(net string) (afnet string, proto int, err error) {
@@ -161,7 +97,7 @@ func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) {
        return resolveInternetAddr(afnet, addr, deadline)
 }
 
-// Dial connects to the address addr on the network net.
+// 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"
@@ -181,32 +117,33 @@ func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) {
 //     Dial("tcp", "[2001:db8::1]:http")
 //     Dial("tcp", "[fe80::1%lo0]:80")
 //
-// For IP networks, the net 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.
+// 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 addr must be a file system path.
-func Dial(net, addr string) (Conn, error) {
-       return DialOpt(addr, dialNetwork(net))
+// 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)
 }
 
-// DialOpt dials addr using the provided options.
-// If no options are provided, DialOpt(addr) is equivalent
-// to Dial("tcp", addr). See Dial for the syntax of addr.
-func DialOpt(addr string, opts ...DialOption) (Conn, error) {
-       var o dialOpts
-       for _, opt := range opts {
-               opt.setDialOpt(&o)
-       }
-       ra, err := resolveAddr("dial", o.net(), addr, o.deadline)
-       if err != nil {
-               return nil, err
-       }
-       return dial(o.net(), addr, o.localAddr, ra, o.deadline)
+// 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) {
+       return resolveAndDial(network, address, d.LocalAddr, d.deadline())
 }
 
 func dial(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) {
@@ -235,58 +172,6 @@ func dial(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error)
        return
 }
 
-// DialTimeout acts like Dial but takes a timeout.
-// The timeout includes name resolution, if required.
-func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
-       return dialTimeout(net, addr, timeout)
-}
-
-// dialTimeoutRace is the old implementation of DialTimeout, still used
-// on operating systems where the deadline hasn't been pushed down
-// into the pollserver.
-// TODO: fix this on plan9.
-func dialTimeoutRace(net, addr string, timeout time.Duration) (Conn, error) {
-       t := time.NewTimer(timeout)
-       defer t.Stop()
-       type pair struct {
-               Conn
-               error
-       }
-       ch := make(chan pair, 1)
-       resolvedAddr := make(chan Addr, 1)
-       go func() {
-               ra, err := resolveAddr("dial", net, addr, noDeadline)
-               if err != nil {
-                       ch <- pair{nil, err}
-                       return
-               }
-               resolvedAddr <- ra // in case we need it for OpError
-               c, err := dial(net, addr, noLocalAddr, ra, noDeadline)
-               ch <- pair{c, err}
-       }()
-       select {
-       case <-t.C:
-               // Try to use the real Addr in our OpError, if we resolved it
-               // before the timeout. Otherwise we just use stringAddr.
-               var ra Addr
-               select {
-               case a := <-resolvedAddr:
-                       ra = a
-               default:
-                       ra = &stringAddr{net, addr}
-               }
-               err := &OpError{
-                       Op:   "dial",
-                       Net:  net,
-                       Addr: ra,
-                       Err:  &timeoutError{},
-               }
-               return nil, err
-       case p := <-ch:
-               return p.Conn, p.error
-       }
-}
-
 type stringAddr struct {
        net, addr string
 }
diff --git a/src/pkg/net/dial_gen.go b/src/pkg/net/dial_gen.go
new file mode 100644 (file)
index 0000000..0a3277d
--- /dev/null
@@ -0,0 +1,61 @@
+// Copyright 2012 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.
+
+// +build windows plan9
+
+package net
+
+import (
+       "time"
+)
+
+// resolveAndDialChannel is the simple pure-Go implementation of
+// resolveAndDial, still used on operating systems where the deadline
+// hasn't been pushed down into the pollserver. (Plan 9 and some old
+// versions of Windows)
+func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
+       timeout := deadline.Sub(time.Now())
+       if timeout < 0 {
+               timeout = 0
+       }
+       t := time.NewTimer(timeout)
+       defer t.Stop()
+       type pair struct {
+               Conn
+               error
+       }
+       ch := make(chan pair, 1)
+       resolvedAddr := make(chan Addr, 1)
+       go func() {
+               ra, err := resolveAddr("dial", net, addr, noDeadline)
+               if err != nil {
+                       ch <- pair{nil, err}
+                       return
+               }
+               resolvedAddr <- ra // in case we need it for OpError
+               c, err := dial(net, addr, localAddr, ra, noDeadline)
+               ch <- pair{c, err}
+       }()
+       select {
+       case <-t.C:
+               // Try to use the real Addr in our OpError, if we resolved it
+               // before the timeout. Otherwise we just use stringAddr.
+               var ra Addr
+               select {
+               case a := <-resolvedAddr:
+                       ra = a
+               default:
+                       ra = &stringAddr{net, addr}
+               }
+               err := &OpError{
+                       Op:   "dial",
+                       Net:  net,
+                       Addr: ra,
+                       Err:  &timeoutError{},
+               }
+               return nil, err
+       case p := <-ch:
+               return p.Conn, p.error
+       }
+}
index 169087999d50570906740c7ac59f7c9959e66b4a..e9527a3743bd334e2a5eaebecd20a8698c3fa13c 100644 (file)
@@ -23,10 +23,10 @@ var canCancelIO = true // used for testing current package
 func sysInit() {
 }
 
-func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
+func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
        // On plan9, use the relatively inefficient
        // goroutine-racing implementation.
-       return dialTimeoutRace(net, addr, timeout)
+       return resolveAndDialChannel(net, addr, localAddr, deadline)
 }
 
 func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) *netFD {
index 2b418a86812149cedcb90a247e3932c96fc129ce..f52c98a6c6e0fc33966d859b790e4c80f50f318a 100644 (file)
@@ -41,13 +41,12 @@ type netFD struct {
        pd pollDesc
 }
 
-func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
-       deadline := time.Now().Add(timeout)
+func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
        ra, err := resolveAddr("dial", net, addr, deadline)
        if err != nil {
                return nil, err
        }
-       return dial(net, addr, noLocalAddr, ra, deadline)
+       return dial(net, addr, localAddr, ra, deadline)
 }
 
 func newFD(fd, family, sotype int, net string) (*netFD, error) {
index cacee627b622e44c385b9f9803e8031c07e84895..3a16d8ae7b866e9aadbb9f986c2e238ef30627c9 100644 (file)
@@ -54,18 +54,17 @@ func canUseConnectEx(net string) bool {
        return syscall.LoadConnectEx() == nil
 }
 
-func dialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
+func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) {
        if !canUseConnectEx(net) {
                // Use the relatively inefficient goroutine-racing
                // implementation of DialTimeout.
-               return dialTimeoutRace(net, addr, timeout)
+               return resolveAndDialChannel(net, addr, localAddr, deadline)
        }
-       deadline := time.Now().Add(timeout)
        ra, err := resolveAddr("dial", net, addr, deadline)
        if err != nil {
                return nil, err
        }
-       return dial(net, addr, noLocalAddr, ra, deadline)
+       return dial(net, addr, localAddr, ra, deadline)
 }
 
 // Interface for all IO operations.