Skip to content

Commit 1dd9954

Browse files
committed
feat(network): Add ConnAs
ConnAs works in a similar way to errors.As. It allows a user to cut through the interface layers and extract a specific type of connection if available. This serves as a sort of escape hatch to allow users to leverage some connection specific feature without having to support that feature for all connections. Getting RTT information is one example. It also allows us, within the library, to get specific types of connections out of the interface box. This would have been useful in the recent changes in tcpreuse. See #3181 and #3142. Getting access to the underlying type can lead to hard to debug issues. For example, if a user mutates connection state on the underlying type, hooks that relied on only mutating that state from the wrapped connection would never be called. It is up to the user to ensure they are using this safely.
1 parent 7b7c3ed commit 1dd9954

File tree

7 files changed

+93
-0
lines changed

7 files changed

+93
-0
lines changed

core/network/conn.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,10 @@ type ConnScoper interface {
141141
// Scope returns the user view of this connection's resource scope
142142
Scope() ConnScope
143143
}
144+
145+
func ConnAs(c Conn, target any) bool {
146+
if x, ok := c.(interface{ As(any) bool }); ok && x.As(target) {
147+
return true
148+
}
149+
return false
150+
}

libp2p_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import (
4040
libp2pwebrtc "github.com/libp2p/go-libp2p/p2p/transport/webrtc"
4141
"github.com/libp2p/go-libp2p/p2p/transport/websocket"
4242
webtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport"
43+
"github.com/libp2p/go-yamux/v5"
44+
quicgo "github.com/quic-go/quic-go"
4345
"go.uber.org/goleak"
4446

4547
ma "github.com/multiformats/go-multiaddr"
@@ -825,3 +827,49 @@ func BenchmarkAllAddrs(b *testing.B) {
825827
addrsHost.AllAddrs()
826828
}
827829
}
830+
831+
func TestConnAs(t *testing.T) {
832+
t.Run("quic conns", func(t *testing.T) {
833+
h1, err := New(ListenAddrStrings(
834+
"/ip4/0.0.0.0/udp/0/quic-v1",
835+
))
836+
require.NoError(t, err)
837+
defer h1.Close()
838+
h2, err := New(ListenAddrStrings(
839+
"/ip4/0.0.0.0/udp/0/quic-v1",
840+
))
841+
require.NoError(t, err)
842+
defer h2.Close()
843+
err = h1.Connect(context.Background(), peer.AddrInfo{
844+
ID: h2.ID(),
845+
Addrs: h2.Addrs(),
846+
})
847+
require.NoError(t, err)
848+
c := h1.Network().ConnsToPeer(h2.ID())[0]
849+
850+
var quicConn *quicgo.Conn
851+
require.True(t, network.ConnAs(c, &quicConn))
852+
})
853+
854+
t.Run("TCP conn with metrics", func(t *testing.T) {
855+
h1, err := New(ListenAddrStrings(
856+
"/ip4/0.0.0.0/tcp/0",
857+
), Transport(tcp.NewTCPTransport, tcp.WithMetrics()))
858+
require.NoError(t, err)
859+
defer h1.Close()
860+
h2, err := New(ListenAddrStrings(
861+
"/ip4/0.0.0.0/tcp/0",
862+
), Transport(tcp.NewTCPTransport, tcp.WithMetrics()))
863+
require.NoError(t, err)
864+
defer h2.Close()
865+
err = h1.Connect(context.Background(), peer.AddrInfo{
866+
ID: h2.ID(),
867+
Addrs: h2.Addrs(),
868+
})
869+
require.NoError(t, err)
870+
c := h1.Network().ConnsToPeer(h2.ID())[0]
871+
872+
var yamuxSession *yamux.Session
873+
require.True(t, network.ConnAs(c, &yamuxSession))
874+
})
875+
}

p2p/muxer/yamux/conn.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ type conn yamux.Session
1313

1414
var _ network.MuxedConn = &conn{}
1515

16+
func (c *conn) As(target any) bool {
17+
if t, ok := target.(**yamux.Session); ok {
18+
*t = (*yamux.Session)(c)
19+
return true
20+
}
21+
return false
22+
}
23+
1624
// NewMuxedConn constructs a new MuxedConn from a yamux.Session.
1725
func NewMuxedConn(m *yamux.Session) network.MuxedConn {
1826
return (*conn)(m)

p2p/net/swarm/swarm.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,13 @@ func wrapWithMetrics(capableConn transport.CapableConn, metricsTracer MetricsTra
833833
return c
834834
}
835835

836+
func (c *connWithMetrics) As(target any) bool {
837+
if x, ok := c.CapableConn.(interface{ As(any) bool }); ok && x.As(target) {
838+
return true
839+
}
840+
return false
841+
}
842+
836843
func (c *connWithMetrics) completedHandshake() {
837844
c.metricsTracer.CompletedHandshake(time.Since(c.opened), c.ConnState(), c.LocalMultiaddr())
838845
}

p2p/net/swarm/swarm_conn.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ type Conn struct {
4242

4343
var _ network.Conn = &Conn{}
4444

45+
func (c *Conn) As(target any) bool {
46+
if x, ok := c.conn.(interface{ As(any) bool }); ok && x.As(target) {
47+
return true
48+
}
49+
return false
50+
}
51+
4552
func (c *Conn) IsClosed() bool {
4653
return c.conn.IsClosed()
4754
}

p2p/net/upgrader/conn.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ type transportConn struct {
2323

2424
var _ transport.CapableConn = &transportConn{}
2525

26+
func (c *transportConn) As(target any) bool {
27+
if x, ok := c.MuxedConn.(interface{ As(any) bool }); ok && x.As(target) {
28+
return true
29+
}
30+
return false
31+
}
32+
2633
func (t *transportConn) Transport() transport.Transport {
2734
return t.transport
2835
}

p2p/transport/quic/conn.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ type conn struct {
2525
remoteMultiaddr ma.Multiaddr
2626
}
2727

28+
func (c *conn) As(target any) bool {
29+
if t, ok := target.(**quic.Conn); ok {
30+
*t = c.quicConn
31+
return true
32+
}
33+
34+
return false
35+
}
36+
2837
var _ tpt.CapableConn = &conn{}
2938

3039
// Close closes the connection.

0 commit comments

Comments
 (0)