Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions conn_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
package vsock

import (
"time"

"golang.org/x/sys/unix"
)

Expand Down Expand Up @@ -32,6 +34,22 @@ func dial(cid, port uint32) (*Conn, error) {
return dialLinux(cfd, cid, port)
}

// dialTimeout acts like dial but takes a timeout.
func dialTimeout(cid, port uint32, timeout time.Duration) (*Conn, error) {
cfd, err := newConnFD()
if err != nil {
return nil, err
}

tv := unix.NsecToTimeval(timeout.Nanoseconds())
err = unix.SetsockoptTimeval(cfd.fd, unix.AF_VSOCK, unix.SO_VM_SOCKETS_CONNECT_TIMEOUT, &tv)
if err != nil {
return nil, err
}

return dialLinux(cfd, cid, port)
}

// dialLinux is the entry point for tests on Linux.
func dialLinux(cfd connFD, cid, port uint32) (c *Conn, err error) {
defer func() {
Expand Down
35 changes: 35 additions & 0 deletions integration_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,38 @@ func makeLocalPipe(
}
}
}

func makeVSockPipeWithTimeout(timeout time.Duration) nettest.MakePipe {
return makeLocalPipe(
func() (net.Listener, error) { return vsock.Listen(0) },
func(addr net.Addr) (net.Conn, error) {
a := addr.(*vsock.Addr)
return vsock.DialTimeout(a.ContextID, a.Port, timeout)
},
)
}

func TestIntegrationConnTimeout(t *testing.T) {
vsutil.SkipHostIntegration(t)

timer := time.AfterFunc(10*time.Second, func() {
panic("test took too long")
})
defer timer.Stop()

mp0 := makeVSockPipeWithTimeout(time.Microsecond)
_, _, stop0, err0 := mp0()
if err0 == nil {
defer stop0()
}
if !os.IsTimeout(err0) {
t.Fatalf("connect with a very short dial timeout expect `ETIMEDOUT`, but got %v", err0)
}

mp1 := makeVSockPipeWithTimeout(time.Second * 2)
_, _, stop1, err1 := mp1()
if err1 != nil {
t.Fatalf("failed to make pipe: %v", err1)
}
defer stop1()
}
14 changes: 14 additions & 0 deletions vsock.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,20 @@ func Dial(contextID, port uint32) (*Conn, error) {
return c, nil
}

// DialTimeout acts like Dial but takes a timeout.
func DialTimeout(contextID, port uint32, timeout time.Duration) (*Conn, error) {
c, err := dialTimeout(contextID, port, timeout)
if err != nil {
// No local address, but we have a remote address we can return.
return nil, opError(opDial, err, nil, &Addr{
ContextID: contextID,
Port: port,
})
}

return c, nil
}

var _ net.Conn = &Conn{}
var _ syscall.Conn = &Conn{}

Expand Down
3 changes: 2 additions & 1 deletion vsock_others.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ func (*listener) Addr() net.Addr { return nil }
func (*listener) Close() error { return errUnimplemented }
func (*listener) SetDeadline(_ time.Time) error { return errUnimplemented }

func dial(_, _ uint32) (*Conn, error) { return nil, errUnimplemented }
func dial(_, _ uint32) (*Conn, error) { return nil, errUnimplemented }
func dialTimeout(_, _ uint32, _ time.Duration) (*Conn, error) { return nil, errUnimplemented }

type connFD struct{}

Expand Down