mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
net: add Dialer.Cancel to cancel pending dials
Dialer.Cancel is a new optional <-chan struct{} channel whose closure
indicates that the dial should be canceled. It is compatible with the
x/net/context and http.Request.Cancel types.
Tested by hand with:
package main
import (
"log"
"net"
"time"
)
func main() {
log.Printf("start.")
var d net.Dialer
cancel := make(chan struct{})
time.AfterFunc(2*time.Second, func() {
log.Printf("timeout firing")
close(cancel)
})
d.Cancel = cancel
c, err := d.Dial("tcp", "192.168.0.1:22")
if err != nil {
log.Print(err)
return
}
log.Fatalf("unexpected connect: %v", c)
}
Which says:
2015/12/14 22:24:58 start.
2015/12/14 22:25:00 timeout firing
2015/12/14 22:25:00 dial tcp 192.168.0.1:22: operation was canceled
Fixes #11225
Change-Id: I2ef39e3a540e29fe6bfec03ab7a629a6b187fcb3
Reviewed-on: https://go-review.googlesource.com/17821
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
parent
479c47e478
commit
24a83d3545
12 changed files with 147 additions and 28 deletions
|
|
@ -5,6 +5,7 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"net/internal/socktest"
|
||||
"runtime"
|
||||
|
|
@ -236,8 +237,8 @@ const (
|
|||
// In some environments, the slow IPs may be explicitly unreachable, and fail
|
||||
// more quickly than expected. This test hook prevents dialTCP from returning
|
||||
// before the deadline.
|
||||
func slowDialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
|
||||
c, err := dialTCP(net, laddr, raddr, deadline)
|
||||
func slowDialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time, cancel <-chan struct{}) (*TCPConn, error) {
|
||||
c, err := dialTCP(net, laddr, raddr, deadline, cancel)
|
||||
if ParseIP(slowDst4).Equal(raddr.IP) || ParseIP(slowDst6).Equal(raddr.IP) {
|
||||
time.Sleep(deadline.Sub(time.Now()))
|
||||
}
|
||||
|
|
@ -716,3 +717,64 @@ func TestDialerKeepAlive(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialCancel(t *testing.T) {
|
||||
if runtime.GOOS == "plan9" || runtime.GOOS == "nacl" {
|
||||
// plan9 is not implemented and nacl doesn't have
|
||||
// external network access.
|
||||
t.Skip("skipping on %s", runtime.GOOS)
|
||||
}
|
||||
onGoBuildFarm := testenv.Builder() != ""
|
||||
if testing.Short() && !onGoBuildFarm {
|
||||
t.Skip("skipping in short mode")
|
||||
}
|
||||
|
||||
blackholeIPPort := JoinHostPort(slowDst4, "1234")
|
||||
if !supportsIPv4 {
|
||||
blackholeIPPort = JoinHostPort(slowDst6, "1234")
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(10 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
const cancelTick = 5 // the timer tick we cancel the dial at
|
||||
const timeoutTick = 100
|
||||
|
||||
var d Dialer
|
||||
cancel := make(chan struct{})
|
||||
d.Cancel = cancel
|
||||
errc := make(chan error, 1)
|
||||
connc := make(chan Conn, 1)
|
||||
go func() {
|
||||
if c, err := d.Dial("tcp", blackholeIPPort); err != nil {
|
||||
errc <- err
|
||||
} else {
|
||||
connc <- c
|
||||
}
|
||||
}()
|
||||
ticks := 0
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
ticks++
|
||||
if ticks == cancelTick {
|
||||
close(cancel)
|
||||
}
|
||||
if ticks == timeoutTick {
|
||||
t.Fatal("timeout waiting for dial to fail")
|
||||
}
|
||||
case c := <-connc:
|
||||
c.Close()
|
||||
t.Fatal("unexpected successful connection")
|
||||
case err := <-errc:
|
||||
if ticks < cancelTick {
|
||||
t.Fatalf("dial error after %d ticks (%d before cancel sent): %v",
|
||||
ticks, cancelTick-ticks, err)
|
||||
}
|
||||
if oe, ok := err.(*OpError); !ok || oe.Err != errCanceled {
|
||||
t.Fatalf("dial error = %v (%T); want OpError with Err == errCanceled", err, err)
|
||||
}
|
||||
return // success.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue