status-go/vendor/github.com/libp2p/go-tcp-transport/tcp.go

192 lines
5.0 KiB
Go
Raw Normal View History

package tcp
import (
"context"
2021-10-19 13:43:41 +00:00
"errors"
2019-06-09 07:24:20 +00:00
"net"
2021-10-19 13:43:41 +00:00
"os"
"runtime"
"time"
logging "github.com/ipfs/go-log"
2019-06-09 07:24:20 +00:00
"github.com/libp2p/go-libp2p-core/peer"
"github.com/libp2p/go-libp2p-core/transport"
tptu "github.com/libp2p/go-libp2p-transport-upgrader"
rtpt "github.com/libp2p/go-reuseport-transport"
2019-06-09 07:24:20 +00:00
ma "github.com/multiformats/go-multiaddr"
2019-06-09 07:24:20 +00:00
mafmt "github.com/multiformats/go-multiaddr-fmt"
2021-10-19 13:43:41 +00:00
manet "github.com/multiformats/go-multiaddr/net"
)
// DefaultConnectTimeout is the (default) maximum amount of time the TCP
// transport will spend on the initial TCP connect before giving up.
var DefaultConnectTimeout = 5 * time.Second
var log = logging.Logger("tcp-tpt")
2021-10-19 13:43:41 +00:00
const keepAlivePeriod = 30 * time.Second
type canKeepAlive interface {
SetKeepAlive(bool) error
SetKeepAlivePeriod(time.Duration) error
}
var _ canKeepAlive = &net.TCPConn{}
func tryKeepAlive(conn net.Conn, keepAlive bool) {
keepAliveConn, ok := conn.(canKeepAlive)
if !ok {
log.Errorf("Can't set TCP keepalives.")
return
}
if err := keepAliveConn.SetKeepAlive(keepAlive); err != nil {
// Sometimes we seem to get "invalid argument" results from this function on Darwin.
// This might be due to a closed connection, but I can't reproduce that on Linux.
//
// But there's nothing we can do about invalid arguments, so we'll drop this to a
// debug.
if errors.Is(err, os.ErrInvalid) {
log.Debugw("failed to enable TCP keepalive", "error", err)
} else {
log.Errorw("failed to enable TCP keepalive", "error", err)
}
return
}
if runtime.GOOS != "openbsd" {
if err := keepAliveConn.SetKeepAlivePeriod(keepAlivePeriod); err != nil {
log.Errorw("failed set keepalive period", "error", err)
}
}
}
2019-06-09 07:24:20 +00:00
// try to set linger on the connection, if possible.
func tryLinger(conn net.Conn, sec int) {
type canLinger interface {
SetLinger(int) error
}
if lingerConn, ok := conn.(canLinger); ok {
_ = lingerConn.SetLinger(sec)
}
}
2021-10-19 13:43:41 +00:00
type tcpListener struct {
2019-06-09 07:24:20 +00:00
manet.Listener
sec int
}
2021-10-19 13:43:41 +00:00
func (ll *tcpListener) Accept() (manet.Conn, error) {
2019-06-09 07:24:20 +00:00
c, err := ll.Listener.Accept()
if err != nil {
return nil, err
}
tryLinger(c, ll.sec)
2021-10-19 13:43:41 +00:00
tryKeepAlive(c, true)
2019-06-09 07:24:20 +00:00
return c, nil
}
// TcpTransport is the TCP transport.
type TcpTransport struct {
// Connection upgrader for upgrading insecure stream connections to
// secure multiplex connections.
Upgrader *tptu.Upgrader
// Explicitly disable reuseport.
DisableReuseport bool
// TCP connect timeout
ConnectTimeout time.Duration
reuse rtpt.Transport
}
2019-06-09 07:24:20 +00:00
var _ transport.Transport = &TcpTransport{}
// NewTCPTransport creates a tcp transport object that tracks dialers and listeners
// created. It represents an entire tcp stack (though it might not necessarily be)
func NewTCPTransport(upgrader *tptu.Upgrader) *TcpTransport {
return &TcpTransport{Upgrader: upgrader, ConnectTimeout: DefaultConnectTimeout}
}
var dialMatcher = mafmt.And(mafmt.IP, mafmt.Base(ma.P_TCP))
// CanDial returns true if this transport believes it can dial the given
// multiaddr.
func (t *TcpTransport) CanDial(addr ma.Multiaddr) bool {
return dialMatcher.Matches(addr)
}
func (t *TcpTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (manet.Conn, error) {
// Apply the deadline iff applicable
if t.ConnectTimeout > 0 {
deadline := time.Now().Add(t.ConnectTimeout)
if d, ok := ctx.Deadline(); !ok || deadline.Before(d) {
var cancel func()
ctx, cancel = context.WithDeadline(ctx, deadline)
defer cancel()
}
}
if t.UseReuseport() {
return t.reuse.DialContext(ctx, raddr)
}
var d manet.Dialer
return d.DialContext(ctx, raddr)
}
// Dial dials the peer at the remote address.
2019-06-09 07:24:20 +00:00
func (t *TcpTransport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (transport.CapableConn, error) {
conn, err := t.maDial(ctx, raddr)
if err != nil {
return nil, err
}
2019-06-09 07:24:20 +00:00
// Set linger to 0 so we never get stuck in the TIME-WAIT state. When
// linger is 0, connections are _reset_ instead of closed with a FIN.
// This means we can immediately reuse the 5-tuple and reconnect.
tryLinger(conn, 0)
2021-10-19 13:43:41 +00:00
tryKeepAlive(conn, true)
c, err := newTracingConn(conn, true)
if err != nil {
return nil, err
}
return t.Upgrader.UpgradeOutbound(ctx, t, c, p)
}
// UseReuseport returns true if reuseport is enabled and available.
func (t *TcpTransport) UseReuseport() bool {
return !t.DisableReuseport && ReuseportIsAvailable()
}
func (t *TcpTransport) maListen(laddr ma.Multiaddr) (manet.Listener, error) {
if t.UseReuseport() {
return t.reuse.Listen(laddr)
}
return manet.Listen(laddr)
}
// Listen listens on the given multiaddr.
2019-06-09 07:24:20 +00:00
func (t *TcpTransport) Listen(laddr ma.Multiaddr) (transport.Listener, error) {
list, err := t.maListen(laddr)
if err != nil {
return nil, err
}
2021-10-19 13:43:41 +00:00
list = newTracingListener(&tcpListener{list, 0})
return t.Upgrader.UpgradeListener(t, list), nil
}
// Protocols returns the list of terminal protocols this transport can dial.
func (t *TcpTransport) Protocols() []int {
return []int{ma.P_TCP}
}
// Proxy always returns false for the TCP transport.
func (t *TcpTransport) Proxy() bool {
return false
}
func (t *TcpTransport) String() string {
return "TCP"
}