Skip to content

Commit a8f545c

Browse files
Merge pull request #4 from stealthrocket/conn-and-listen
nettest: exercise end-to-end connection lifecycle
2 parents 4958b60 + e4b8ef4 commit a8f545c

File tree

10 files changed

+372
-112
lines changed

10 files changed

+372
-112
lines changed

.github/workflows/build.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: build
2+
on:
3+
push:
4+
tags:
5+
- v*
6+
branches:
7+
- main
8+
pull_request:
9+
branches:
10+
- main
11+
12+
concurrency:
13+
group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
14+
cancel-in-progress: true
15+
16+
jobs:
17+
test:
18+
name: Go Test
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v3
22+
- uses: actions/setup-go@v4
23+
with:
24+
go-version-file: go.mod
25+
check-latest: true
26+
27+
- name: Install Go tip
28+
run: |
29+
curl -sL https://storage.googleapis.com/go-build-snap/go/linux-amd64/$(git ls-remote https://github.com/golang/go.git HEAD | awk '{print $1;}').tar.gz -o gotip.tar.gz
30+
ls -lah gotip.tar.gz
31+
mkdir -p $HOME/gotip
32+
tar -C $HOME/gotip -xzf gotip.tar.gz
33+
- run: make test GO=$HOME/gotip/bin/go GOPATH=$HOME/gotip

.gitignore

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
wasirun
8+
9+
# Test binary, built with `go test -c`
10+
*.test
11+
12+
# Output of the go coverage tool, specifically when used with LiteIDE
13+
*.out
14+
15+
# Dependency directories (remove the comment below to include it)
16+
# vendor/
17+
18+
# Emacs
19+
*~

Makefile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.PHONY: test lint wasirun
2+
3+
GOPATH ?= $(shell $(GO) env GOPATH)
4+
wasirun = $(GOPATH)/bin/wasirun
5+
6+
wasip1.test: go.mod $(wildcard wasip1/*.go)
7+
GOARCH=wasm GOOS=wasip1 $(GO) test -c ./wasip1
8+
9+
test: wasirun wasip1.test
10+
$(wasirun) wasip1.test -test.v
11+
12+
wasirun: $(wasirun)
13+
14+
$(wasirun):
15+
$(GO) install github.com/stealthrocket/wasi-go/cmd/wasirun@latest

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
module github.com/stealthrocket/net
22

33
go 1.20
4+
5+
require golang.org/x/net v0.10.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
2+
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=

wasip1/dial_wasip1.go

Lines changed: 54 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -9,37 +9,33 @@ import (
99

1010
// Dial connects to the address on the named network.
1111
func Dial(network, address string) (net.Conn, error) {
12+
return DialContext(context.Background(), network, address)
13+
}
14+
15+
// DialContext is a variant of Dial that accepts a context.
16+
func DialContext(ctx context.Context, network, address string) (net.Conn, error) {
1217
addr, err := lookupAddr("dial", network, address)
1318
if err != nil {
1419
addr := &netAddr{network, address}
1520
return nil, dialErr(addr, err)
1621
}
17-
conn, err := dialAddr(addr)
22+
conn, err := dialAddr(ctx, addr)
1823
if err != nil {
1924
return nil, dialErr(addr, err)
2025
}
2126
return conn, nil
2227
}
2328

24-
// DialContext is a variant of Dial that accepts a context.
25-
func DialContext(ctx context.Context, network, address string) (net.Conn, error) {
26-
select {
27-
case <-ctx.Done():
28-
addr := &netAddr{network, address}
29-
return nil, dialErr(addr, context.Cause(ctx))
30-
default:
31-
return Dial(network, address)
32-
}
33-
}
34-
3529
func dialErr(addr net.Addr, err error) error {
3630
return newOpError("dial", addr, err)
3731
}
3832

39-
func dialAddr(addr net.Addr) (net.Conn, error) {
33+
func dialAddr(ctx context.Context, addr net.Addr) (net.Conn, error) {
4034
proto := family(addr)
41-
sotype := socketType(addr)
42-
35+
sotype, err := socketType(addr)
36+
if err != nil {
37+
return nil, os.NewSyscallError("socket", err)
38+
}
4339
fd, err := socket(proto, sotype, 0)
4440
if err != nil {
4541
return nil, os.NewSyscallError("socket", err)
@@ -49,7 +45,6 @@ func dialAddr(addr net.Addr) (net.Conn, error) {
4945
syscall.Close(fd)
5046
return nil, os.NewSyscallError("setnonblock", err)
5147
}
52-
5348
if sotype == SOCK_DGRAM && proto != AF_UNIX {
5449
if err := setsockopt(fd, SOL_SOCKET, SO_BROADCAST, 1); err != nil {
5550
syscall.Close(fd)
@@ -61,7 +56,6 @@ func dialAddr(addr net.Addr) (net.Conn, error) {
6156
if err != nil {
6257
return nil, os.NewSyscallError("connect", err)
6358
}
64-
6559
var inProgress bool
6660
switch err := connect(fd, connectAddr); err {
6761
case nil:
@@ -80,96 +74,56 @@ func dialAddr(addr net.Addr) (net.Conn, error) {
8074
if err != nil {
8175
return nil, err
8276
}
83-
rawConnErr := rawConn.Write(func(fd uintptr) bool {
84-
var value int
85-
value, err = getsockopt(int(fd), SOL_SOCKET, SO_ERROR)
86-
if err != nil {
87-
return true // done
77+
78+
errch := make(chan error)
79+
go func() {
80+
var err error
81+
rawConnErr := rawConn.Write(func(fd uintptr) bool {
82+
var value int
83+
value, err = getsockopt(int(fd), SOL_SOCKET, SO_ERROR)
84+
if err != nil {
85+
return true // done
86+
}
87+
switch syscall.Errno(value) {
88+
case syscall.EINPROGRESS, syscall.EINTR:
89+
return false // continue
90+
case syscall.EISCONN:
91+
err = nil
92+
return true
93+
case syscall.Errno(0):
94+
// The net poller can wake up spuriously. Check that we are
95+
// are really connected.
96+
_, err := getpeername(int(fd))
97+
return err == nil
98+
default:
99+
return true
100+
}
101+
})
102+
if err == nil {
103+
err = rawConnErr
88104
}
89-
switch syscall.Errno(value) {
90-
case syscall.EINPROGRESS, syscall.EINTR:
91-
return false // continue
92-
case syscall.EISCONN:
93-
err = nil
94-
return true
95-
case syscall.Errno(0):
96-
// The net poller can wake up spuriously. Check that we are
97-
// are really connected.
98-
_, err := getpeername(int(fd))
99-
return err == nil
100-
default:
101-
return true
105+
errch <- err
106+
}()
107+
108+
select {
109+
case err := <-errch:
110+
if err != nil {
111+
return nil, os.NewSyscallError("connect", err)
102112
}
103-
})
104-
if err == nil {
105-
err = rawConnErr
106-
}
107-
if err != nil {
108-
return nil, os.NewSyscallError("connect", err)
113+
case <-ctx.Done():
114+
// This should interrupt the async connect operation handled by the
115+
// goroutine.
116+
f.Close()
117+
// Wait for the goroutine to complete, we can safely discard the
118+
// error here because we don't care about the socket anymore.
119+
<-errch
120+
return nil, context.Cause(ctx)
109121
}
110122
}
111123

112124
c, err := net.FileConn(f)
113125
if err != nil {
114126
return nil, err
115127
}
116-
117-
// TODO: get local+peer address; wrap FileConn to implement LocalAddr() and RemoteAddr()
118-
return c, nil
119-
}
120-
121-
func family(addr net.Addr) int {
122-
var ip net.IP
123-
switch a := addr.(type) {
124-
case *net.UnixAddr:
125-
return AF_UNIX
126-
case *net.TCPAddr:
127-
ip = a.IP
128-
case *net.UDPAddr:
129-
ip = a.IP
130-
case *net.IPAddr:
131-
ip = a.IP
132-
}
133-
if ip.To4() != nil {
134-
return AF_INET
135-
} else if len(ip) == net.IPv6len {
136-
return AF_INET6
137-
}
138-
return AF_INET
139-
}
140-
141-
func socketType(addr net.Addr) int {
142-
switch addr.Network() {
143-
case "tcp", "unix":
144-
return SOCK_STREAM
145-
case "udp", "unixgram":
146-
return SOCK_DGRAM
147-
default:
148-
panic("not implemented")
149-
}
150-
}
151-
152-
func socketAddress(addr net.Addr) (sockaddr, error) {
153-
var ip net.IP
154-
var port int
155-
switch a := addr.(type) {
156-
case *net.UnixAddr:
157-
return &sockaddrUnix{name: a.Name}, nil
158-
case *net.TCPAddr:
159-
ip, port = a.IP, a.Port
160-
case *net.UDPAddr:
161-
ip, port = a.IP, a.Port
162-
case *net.IPAddr:
163-
ip = a.IP
164-
}
165-
if ipv4 := ip.To4(); ipv4 != nil {
166-
return &sockaddrInet4{addr: ([4]byte)(ipv4), port: port}, nil
167-
} else if len(ip) == net.IPv6len {
168-
return &sockaddrInet6{addr: ([16]byte)(ip), port: port}, nil
169-
} else {
170-
return nil, &net.AddrError{
171-
Err: "unsupported address type",
172-
Addr: addr.String(),
173-
}
174-
}
128+
return makeConn(c)
175129
}

wasip1/listen_wasip1.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ func listenErr(addr net.Addr, err error) error {
2525
}
2626

2727
func listenAddr(addr net.Addr) (net.Listener, error) {
28-
fd, err := socket(family(addr), socketType(addr), 0)
28+
sotype, err := socketType(addr)
29+
if err != nil {
30+
return nil, os.NewSyscallError("socket", err)
31+
}
32+
fd, err := socket(family(addr), sotype, 0)
2933
if err != nil {
3034
return nil, os.NewSyscallError("socket", err)
3135
}
@@ -39,29 +43,39 @@ func listenAddr(addr net.Addr) (net.Listener, error) {
3943
return nil, os.NewSyscallError("setsockopt", err)
4044
}
4145

42-
listenAddr, err := socketAddress(addr)
46+
bindAddr, err := socketAddress(addr)
4347
if err != nil {
4448
return nil, os.NewSyscallError("bind", err)
4549
}
46-
47-
if err := bind(fd, listenAddr); err != nil {
50+
if err := bind(fd, bindAddr); err != nil {
4851
syscall.Close(fd)
4952
return nil, os.NewSyscallError("bind", err)
5053
}
51-
5254
const backlog = 64 // TODO: configurable?
5355
if err := listen(fd, backlog); err != nil {
5456
syscall.Close(fd)
5557
return nil, os.NewSyscallError("listen", err)
5658
}
5759

60+
sockaddr, err := getsockname(fd)
61+
if err != nil {
62+
syscall.Close(fd)
63+
return nil, os.NewSyscallError("getsockname", err)
64+
}
65+
5866
f := os.NewFile(uintptr(fd), "")
5967
defer f.Close()
6068

6169
l, err := net.FileListener(f)
6270
if err != nil {
6371
return nil, err
6472
}
73+
switch l.(type) {
74+
case *net.UnixListener:
75+
addr = sockaddrToUnixAddr(sockaddr)
76+
case *net.TCPListener:
77+
addr = sockaddrToTCPAddr(sockaddr)
78+
}
6579
return &listener{l, addr}, nil
6680
}
6781

@@ -75,8 +89,7 @@ func (l *listener) Accept() (net.Conn, error) {
7589
if err != nil {
7690
return nil, err
7791
}
78-
// TODO: get local+peer address; wrap Conn to implement LocalAddr() and RemoteAddr()
79-
return c, nil
92+
return makeConn(c)
8093
}
8194

8295
func (l *listener) Addr() net.Addr {

0 commit comments

Comments
 (0)