Skip to content

Commit 4e85c96

Browse files
authored
fix(sampledconn): Correctly handle slow bytes and closed conns (#3080)
1 parent b3209ef commit 4e85c96

File tree

4 files changed

+110
-73
lines changed

4 files changed

+110
-73
lines changed

p2p/transport/tcpreuse/internal/sampledconn/sampledconn_common.go renamed to p2p/transport/tcpreuse/internal/sampledconn/sampledconn.go

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,17 @@ const peekSize = 3
1414

1515
type PeekedBytes = [peekSize]byte
1616

17-
var errNotSupported = errors.New("not supported on this platform")
18-
1917
var ErrNotTCPConn = errors.New("passed conn is not a TCPConn")
2018

2119
func PeekBytes(conn manet.Conn) (PeekedBytes, manet.Conn, error) {
22-
if c, ok := conn.(syscall.Conn); ok {
23-
b, err := OSPeekConn(c)
24-
if err == nil {
25-
return b, conn, nil
26-
}
27-
if err != errNotSupported {
28-
return PeekedBytes{}, nil, err
29-
}
30-
// Fallback to wrapping the coonn
31-
}
32-
3320
if c, ok := conn.(ManetTCPConnInterface); ok {
34-
return newFallbackSampledConn(c)
21+
return newWrappedSampledConn(c)
3522
}
3623

3724
return PeekedBytes{}, nil, ErrNotTCPConn
3825
}
3926

40-
type fallbackPeekingConn struct {
27+
type wrappedSampledConn struct {
4128
ManetTCPConnInterface
4229
peekedBytes PeekedBytes
4330
bytesPeeked uint8
@@ -69,16 +56,19 @@ type ManetTCPConnInterface interface {
6956
tcpConnInterface
7057
}
7158

72-
func newFallbackSampledConn(conn ManetTCPConnInterface) (PeekedBytes, *fallbackPeekingConn, error) {
73-
s := &fallbackPeekingConn{ManetTCPConnInterface: conn}
74-
_, err := io.ReadFull(conn, s.peekedBytes[:])
59+
func newWrappedSampledConn(conn ManetTCPConnInterface) (PeekedBytes, *wrappedSampledConn, error) {
60+
s := &wrappedSampledConn{ManetTCPConnInterface: conn}
61+
n, err := io.ReadFull(conn, s.peekedBytes[:])
7562
if err != nil {
63+
if n == 0 && err == io.EOF {
64+
err = io.ErrUnexpectedEOF
65+
}
7666
return s.peekedBytes, nil, err
7767
}
7868
return s.peekedBytes, s, nil
7969
}
8070

81-
func (sc *fallbackPeekingConn) Read(b []byte) (int, error) {
71+
func (sc *wrappedSampledConn) Read(b []byte) (int, error) {
8272
if int(sc.bytesPeeked) != len(sc.peekedBytes) {
8373
red := copy(b, sc.peekedBytes[sc.bytesPeeked:])
8474
sc.bytesPeeked += uint8(red)

p2p/transport/tcpreuse/internal/sampledconn/sampledconn_other.go

Lines changed: 0 additions & 11 deletions
This file was deleted.

p2p/transport/tcpreuse/internal/sampledconn/sampledconn_test.go

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
manet "github.com/multiformats/go-multiaddr/net"
1111

1212
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
1314
)
1415

1516
func TestSampledConn(t *testing.T) {
@@ -63,7 +64,7 @@ func TestSampledConn(t *testing.T) {
6364
assert.Equal(t, "hello", string(buf))
6465
} else {
6566
// Wrap the client connection in SampledConn
66-
sample, sampledConn, err := newFallbackSampledConn(clientConn.(ManetTCPConnInterface))
67+
sample, sampledConn, err := newWrappedSampledConn(clientConn.(ManetTCPConnInterface))
6768
assert.NoError(t, err)
6869
assert.Equal(t, "hel", string(sample[:]))
6970

@@ -76,3 +77,102 @@ func TestSampledConn(t *testing.T) {
7677
})
7778
}
7879
}
80+
81+
func spawnServerAndClientConn(t *testing.T) (serverConn manet.Conn, clientConn manet.Conn) {
82+
serverConnChan := make(chan manet.Conn, 1)
83+
84+
listener, err := manet.Listen(ma.StringCast("/ip4/127.0.0.1/tcp/0"))
85+
assert.NoError(t, err)
86+
defer listener.Close()
87+
88+
serverAddr := listener.Multiaddr()
89+
90+
// Server goroutine
91+
go func() {
92+
conn, err := listener.Accept()
93+
assert.NoError(t, err)
94+
serverConnChan <- conn
95+
}()
96+
97+
// Give the server a moment to start
98+
time.Sleep(100 * time.Millisecond)
99+
100+
// Create a TCP client
101+
clientConn, err = manet.Dial(serverAddr)
102+
assert.NoError(t, err)
103+
104+
return <-serverConnChan, clientConn
105+
}
106+
107+
func TestHandleNoBytes(t *testing.T) {
108+
serverConn, clientConn := spawnServerAndClientConn(t)
109+
defer clientConn.Close()
110+
111+
// Server goroutine
112+
go func() {
113+
serverConn.Close()
114+
}()
115+
_, _, err := PeekBytes(clientConn.(interface {
116+
manet.Conn
117+
syscall.Conn
118+
}))
119+
assert.ErrorIs(t, err, io.ErrUnexpectedEOF)
120+
}
121+
122+
func TestHandle1ByteAndClose(t *testing.T) {
123+
serverConn, clientConn := spawnServerAndClientConn(t)
124+
defer clientConn.Close()
125+
126+
// Server goroutine
127+
go func() {
128+
defer serverConn.Close()
129+
_, err := serverConn.Write([]byte("h"))
130+
assert.NoError(t, err)
131+
}()
132+
_, _, err := PeekBytes(clientConn.(interface {
133+
manet.Conn
134+
syscall.Conn
135+
}))
136+
assert.ErrorIs(t, err, io.ErrUnexpectedEOF)
137+
}
138+
139+
func TestSlowBytes(t *testing.T) {
140+
serverConn, clientConn := spawnServerAndClientConn(t)
141+
142+
interval := 100 * time.Millisecond
143+
144+
// Server goroutine
145+
go func() {
146+
defer serverConn.Close()
147+
148+
time.Sleep(interval)
149+
_, err := serverConn.Write([]byte("h"))
150+
assert.NoError(t, err)
151+
time.Sleep(interval)
152+
_, err = serverConn.Write([]byte("e"))
153+
assert.NoError(t, err)
154+
time.Sleep(interval)
155+
_, err = serverConn.Write([]byte("l"))
156+
assert.NoError(t, err)
157+
time.Sleep(interval)
158+
_, err = serverConn.Write([]byte("lo"))
159+
assert.NoError(t, err)
160+
}()
161+
162+
defer clientConn.Close()
163+
164+
err := clientConn.SetReadDeadline(time.Now().Add(interval * 10))
165+
require.NoError(t, err)
166+
167+
peeked, clientConn, err := PeekBytes(clientConn.(interface {
168+
manet.Conn
169+
syscall.Conn
170+
}))
171+
assert.NoError(t, err)
172+
assert.Equal(t, "hel", string(peeked[:]))
173+
174+
buf := make([]byte, 5)
175+
_, err = io.ReadFull(clientConn, buf)
176+
assert.NoError(t, err)
177+
assert.Equal(t, "hello", string(buf))
178+
}

p2p/transport/tcpreuse/internal/sampledconn/sampledconn_unix.go

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)