Integrate BACKBEAT SDK and resolve KACHING license validation
Major integrations and fixes: - Added BACKBEAT SDK integration for P2P operation timing - Implemented beat-aware status tracking for distributed operations - Added Docker secrets support for secure license management - Resolved KACHING license validation via HTTPS/TLS - Updated docker-compose configuration for clean stack deployment - Disabled rollback policies to prevent deployment failures - Added license credential storage (CHORUS-DEV-MULTI-001) Technical improvements: - BACKBEAT P2P operation tracking with phase management - Enhanced configuration system with file-based secrets - Improved error handling for license validation - Clean separation of KACHING and CHORUS deployment stacks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
23
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/config.go
generated
vendored
Normal file
23
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/config.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package quicreuse
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
var quicConfig = &quic.Config{
|
||||
MaxIncomingStreams: 256,
|
||||
MaxIncomingUniStreams: 5, // allow some unidirectional streams, in case we speak WebTransport
|
||||
MaxStreamReceiveWindow: 10 * (1 << 20), // 10 MB
|
||||
MaxConnectionReceiveWindow: 15 * (1 << 20), // 15 MB
|
||||
RequireAddressValidation: func(net.Addr) bool {
|
||||
// TODO(#1535): require source address validation when under load
|
||||
return false
|
||||
},
|
||||
KeepAlivePeriod: 15 * time.Second,
|
||||
Versions: []quic.VersionNumber{quic.Version1},
|
||||
// We don't use datagrams (yet), but this is necessary for WebTransport
|
||||
EnableDatagrams: true,
|
||||
}
|
||||
224
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/connmgr.go
generated
vendored
Normal file
224
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/connmgr.go
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
package quicreuse
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"github.com/quic-go/quic-go"
|
||||
quiclogging "github.com/quic-go/quic-go/logging"
|
||||
)
|
||||
|
||||
type ConnManager struct {
|
||||
reuseUDP4 *reuse
|
||||
reuseUDP6 *reuse
|
||||
enableReuseport bool
|
||||
enableMetrics bool
|
||||
|
||||
serverConfig *quic.Config
|
||||
clientConfig *quic.Config
|
||||
|
||||
quicListenersMu sync.Mutex
|
||||
quicListeners map[string]quicListenerEntry
|
||||
|
||||
srk quic.StatelessResetKey
|
||||
tokenKey quic.TokenGeneratorKey
|
||||
}
|
||||
|
||||
type quicListenerEntry struct {
|
||||
refCount int
|
||||
ln *quicListener
|
||||
}
|
||||
|
||||
func NewConnManager(statelessResetKey quic.StatelessResetKey, tokenKey quic.TokenGeneratorKey, opts ...Option) (*ConnManager, error) {
|
||||
cm := &ConnManager{
|
||||
enableReuseport: true,
|
||||
quicListeners: make(map[string]quicListenerEntry),
|
||||
srk: statelessResetKey,
|
||||
tokenKey: tokenKey,
|
||||
}
|
||||
for _, o := range opts {
|
||||
if err := o(cm); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
quicConf := quicConfig.Clone()
|
||||
|
||||
quicConf.Tracer = func(ctx context.Context, p quiclogging.Perspective, ci quic.ConnectionID) *quiclogging.ConnectionTracer {
|
||||
var tracer *quiclogging.ConnectionTracer
|
||||
if qlogTracerDir != "" {
|
||||
tracer = qloggerForDir(qlogTracerDir, p, ci)
|
||||
}
|
||||
return tracer
|
||||
}
|
||||
serverConfig := quicConf.Clone()
|
||||
|
||||
cm.clientConfig = quicConf
|
||||
cm.serverConfig = serverConfig
|
||||
if cm.enableReuseport {
|
||||
cm.reuseUDP4 = newReuse(&statelessResetKey, &tokenKey)
|
||||
cm.reuseUDP6 = newReuse(&statelessResetKey, &tokenKey)
|
||||
}
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
func (c *ConnManager) getReuse(network string) (*reuse, error) {
|
||||
switch network {
|
||||
case "udp4":
|
||||
return c.reuseUDP4, nil
|
||||
case "udp6":
|
||||
return c.reuseUDP6, nil
|
||||
default:
|
||||
return nil, errors.New("invalid network: must be either udp4 or udp6")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConnManager) ListenQUIC(addr ma.Multiaddr, tlsConf *tls.Config, allowWindowIncrease func(conn quic.Connection, delta uint64) bool) (Listener, error) {
|
||||
netw, host, err := manet.DialArgs(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
laddr, err := net.ResolveUDPAddr(netw, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.quicListenersMu.Lock()
|
||||
defer c.quicListenersMu.Unlock()
|
||||
|
||||
key := laddr.String()
|
||||
entry, ok := c.quicListeners[key]
|
||||
if !ok {
|
||||
tr, err := c.transportForListen(netw, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ln, err := newQuicListener(tr, c.serverConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key = tr.LocalAddr().String()
|
||||
entry = quicListenerEntry{ln: ln}
|
||||
}
|
||||
l, err := entry.ln.Add(tlsConf, allowWindowIncrease, func() { c.onListenerClosed(key) })
|
||||
if err != nil {
|
||||
if entry.refCount <= 0 {
|
||||
entry.ln.Close()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
entry.refCount++
|
||||
c.quicListeners[key] = entry
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (c *ConnManager) onListenerClosed(key string) {
|
||||
c.quicListenersMu.Lock()
|
||||
defer c.quicListenersMu.Unlock()
|
||||
|
||||
entry := c.quicListeners[key]
|
||||
entry.refCount = entry.refCount - 1
|
||||
if entry.refCount <= 0 {
|
||||
delete(c.quicListeners, key)
|
||||
entry.ln.Close()
|
||||
} else {
|
||||
c.quicListeners[key] = entry
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConnManager) transportForListen(network string, laddr *net.UDPAddr) (refCountedQuicTransport, error) {
|
||||
if c.enableReuseport {
|
||||
reuse, err := c.getReuse(network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reuse.TransportForListen(network, laddr)
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &singleOwnerTransport{
|
||||
packetConn: conn,
|
||||
Transport: quic.Transport{
|
||||
Conn: conn,
|
||||
StatelessResetKey: &c.srk,
|
||||
TokenGeneratorKey: &c.tokenKey,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *ConnManager) DialQUIC(ctx context.Context, raddr ma.Multiaddr, tlsConf *tls.Config, allowWindowIncrease func(conn quic.Connection, delta uint64) bool) (quic.Connection, error) {
|
||||
naddr, v, err := FromQuicMultiaddr(raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
netw, _, err := manet.DialArgs(raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
quicConf := c.clientConfig.Clone()
|
||||
quicConf.AllowConnectionWindowIncrease = allowWindowIncrease
|
||||
|
||||
if v == quic.Version1 {
|
||||
// The endpoint has explicit support for QUIC v1, so we'll only use that version.
|
||||
quicConf.Versions = []quic.VersionNumber{quic.Version1}
|
||||
} else {
|
||||
return nil, errors.New("unknown QUIC version")
|
||||
}
|
||||
|
||||
tr, err := c.TransportForDial(netw, naddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn, err := tr.Dial(ctx, naddr, tlsConf, quicConf)
|
||||
if err != nil {
|
||||
tr.DecreaseCount()
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *ConnManager) TransportForDial(network string, raddr *net.UDPAddr) (refCountedQuicTransport, error) {
|
||||
if c.enableReuseport {
|
||||
reuse, err := c.getReuse(network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return reuse.TransportForDial(network, raddr)
|
||||
}
|
||||
|
||||
var laddr *net.UDPAddr
|
||||
switch network {
|
||||
case "udp4":
|
||||
laddr = &net.UDPAddr{IP: net.IPv4zero, Port: 0}
|
||||
case "udp6":
|
||||
laddr = &net.UDPAddr{IP: net.IPv6zero, Port: 0}
|
||||
}
|
||||
conn, err := net.ListenUDP(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &singleOwnerTransport{Transport: quic.Transport{Conn: conn, StatelessResetKey: &c.srk}, packetConn: conn}, nil
|
||||
}
|
||||
|
||||
func (c *ConnManager) Protocols() []int {
|
||||
return []int{ma.P_QUIC_V1}
|
||||
}
|
||||
|
||||
func (c *ConnManager) Close() error {
|
||||
if !c.enableReuseport {
|
||||
return nil
|
||||
}
|
||||
if err := c.reuseUDP6.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return c.reuseUDP4.Close()
|
||||
}
|
||||
219
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/listener.go
generated
vendored
Normal file
219
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/listener.go
generated
vendored
Normal file
@@ -0,0 +1,219 @@
|
||||
package quicreuse
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/transport"
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
type Listener interface {
|
||||
Accept(context.Context) (quic.Connection, error)
|
||||
Addr() net.Addr
|
||||
Multiaddrs() []ma.Multiaddr
|
||||
io.Closer
|
||||
}
|
||||
|
||||
type protoConf struct {
|
||||
ln *listener
|
||||
tlsConf *tls.Config
|
||||
allowWindowIncrease func(conn quic.Connection, delta uint64) bool
|
||||
}
|
||||
|
||||
type quicListener struct {
|
||||
l *quic.Listener
|
||||
transport refCountedQuicTransport
|
||||
running chan struct{}
|
||||
addrs []ma.Multiaddr
|
||||
|
||||
protocolsMu sync.Mutex
|
||||
protocols map[string]protoConf
|
||||
}
|
||||
|
||||
func newQuicListener(tr refCountedQuicTransport, quicConfig *quic.Config) (*quicListener, error) {
|
||||
localMultiaddrs := make([]ma.Multiaddr, 0, 2)
|
||||
a, err := ToQuicMultiaddr(tr.LocalAddr(), quic.Version1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localMultiaddrs = append(localMultiaddrs, a)
|
||||
cl := &quicListener{
|
||||
protocols: map[string]protoConf{},
|
||||
running: make(chan struct{}),
|
||||
transport: tr,
|
||||
addrs: localMultiaddrs,
|
||||
}
|
||||
tlsConf := &tls.Config{
|
||||
SessionTicketsDisabled: true, // This is set for the config for client, but we set it here as well: https://github.com/quic-go/quic-go/issues/4029
|
||||
GetConfigForClient: func(info *tls.ClientHelloInfo) (*tls.Config, error) {
|
||||
cl.protocolsMu.Lock()
|
||||
defer cl.protocolsMu.Unlock()
|
||||
for _, proto := range info.SupportedProtos {
|
||||
if entry, ok := cl.protocols[proto]; ok {
|
||||
conf := entry.tlsConf
|
||||
if conf.GetConfigForClient != nil {
|
||||
return conf.GetConfigForClient(info)
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no supported protocol found. offered: %+v", info.SupportedProtos)
|
||||
},
|
||||
}
|
||||
quicConf := quicConfig.Clone()
|
||||
quicConf.AllowConnectionWindowIncrease = cl.allowWindowIncrease
|
||||
ln, err := tr.Listen(tlsConf, quicConf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cl.l = ln
|
||||
go cl.Run() // This go routine shuts down once the underlying quic.Listener is closed (or returns an error).
|
||||
return cl, nil
|
||||
}
|
||||
|
||||
func (l *quicListener) allowWindowIncrease(conn quic.Connection, delta uint64) bool {
|
||||
l.protocolsMu.Lock()
|
||||
defer l.protocolsMu.Unlock()
|
||||
|
||||
conf, ok := l.protocols[conn.ConnectionState().TLS.NegotiatedProtocol]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return conf.allowWindowIncrease(conn, delta)
|
||||
}
|
||||
|
||||
func (l *quicListener) Add(tlsConf *tls.Config, allowWindowIncrease func(conn quic.Connection, delta uint64) bool, onRemove func()) (Listener, error) {
|
||||
l.protocolsMu.Lock()
|
||||
defer l.protocolsMu.Unlock()
|
||||
|
||||
if len(tlsConf.NextProtos) == 0 {
|
||||
return nil, errors.New("no ALPN found in tls.Config")
|
||||
}
|
||||
|
||||
for _, proto := range tlsConf.NextProtos {
|
||||
if _, ok := l.protocols[proto]; ok {
|
||||
return nil, fmt.Errorf("already listening for protocol %s", proto)
|
||||
}
|
||||
}
|
||||
|
||||
ln := newSingleListener(l.l.Addr(), l.addrs, func() {
|
||||
l.protocolsMu.Lock()
|
||||
for _, proto := range tlsConf.NextProtos {
|
||||
delete(l.protocols, proto)
|
||||
}
|
||||
l.protocolsMu.Unlock()
|
||||
onRemove()
|
||||
}, l.running)
|
||||
for _, proto := range tlsConf.NextProtos {
|
||||
l.protocols[proto] = protoConf{
|
||||
ln: ln,
|
||||
tlsConf: tlsConf,
|
||||
allowWindowIncrease: allowWindowIncrease,
|
||||
}
|
||||
}
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
func (l *quicListener) Run() error {
|
||||
defer close(l.running)
|
||||
defer l.transport.DecreaseCount()
|
||||
for {
|
||||
conn, err := l.l.Accept(context.Background())
|
||||
if err != nil {
|
||||
if errors.Is(err, quic.ErrServerClosed) || strings.Contains(err.Error(), "use of closed network connection") {
|
||||
return transport.ErrListenerClosed
|
||||
}
|
||||
return err
|
||||
}
|
||||
proto := conn.ConnectionState().TLS.NegotiatedProtocol
|
||||
|
||||
l.protocolsMu.Lock()
|
||||
ln, ok := l.protocols[proto]
|
||||
if !ok {
|
||||
l.protocolsMu.Unlock()
|
||||
return fmt.Errorf("negotiated unknown protocol: %s", proto)
|
||||
}
|
||||
ln.ln.add(conn)
|
||||
l.protocolsMu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *quicListener) Close() error {
|
||||
err := l.l.Close()
|
||||
<-l.running // wait for Run to return
|
||||
return err
|
||||
}
|
||||
|
||||
const queueLen = 16
|
||||
|
||||
// A listener for a single ALPN protocol (set).
|
||||
type listener struct {
|
||||
queue chan quic.Connection
|
||||
acceptLoopRunning chan struct{}
|
||||
addr net.Addr
|
||||
addrs []ma.Multiaddr
|
||||
remove func()
|
||||
closeOnce sync.Once
|
||||
}
|
||||
|
||||
var _ Listener = &listener{}
|
||||
|
||||
func newSingleListener(addr net.Addr, addrs []ma.Multiaddr, remove func(), running chan struct{}) *listener {
|
||||
return &listener{
|
||||
queue: make(chan quic.Connection, queueLen),
|
||||
acceptLoopRunning: running,
|
||||
remove: remove,
|
||||
addr: addr,
|
||||
addrs: addrs,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) add(c quic.Connection) {
|
||||
select {
|
||||
case l.queue <- c:
|
||||
default:
|
||||
c.CloseWithError(1, "queue full")
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) Accept(ctx context.Context) (quic.Connection, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-l.acceptLoopRunning:
|
||||
return nil, transport.ErrListenerClosed
|
||||
case c, ok := <-l.queue:
|
||||
if !ok {
|
||||
return nil, transport.ErrListenerClosed
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l *listener) Addr() net.Addr {
|
||||
return l.addr
|
||||
}
|
||||
|
||||
func (l *listener) Multiaddrs() []ma.Multiaddr {
|
||||
return l.addrs
|
||||
}
|
||||
|
||||
func (l *listener) Close() error {
|
||||
l.closeOnce.Do(func() {
|
||||
l.remove()
|
||||
close(l.queue)
|
||||
// drain the queue
|
||||
for conn := range l.queue {
|
||||
conn.CloseWithError(1, "closing")
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
18
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/options.go
generated
vendored
Normal file
18
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/options.go
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package quicreuse
|
||||
|
||||
type Option func(*ConnManager) error
|
||||
|
||||
func DisableReuseport() Option {
|
||||
return func(m *ConnManager) error {
|
||||
m.enableReuseport = false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnableMetrics enables Prometheus metrics collection.
|
||||
func EnableMetrics() Option {
|
||||
return func(m *ConnManager) error {
|
||||
m.enableMetrics = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
58
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/quic_multiaddr.go
generated
vendored
Normal file
58
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/quic_multiaddr.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package quicreuse
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
ma "github.com/multiformats/go-multiaddr"
|
||||
manet "github.com/multiformats/go-multiaddr/net"
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
var (
|
||||
quicV1MA = ma.StringCast("/quic-v1")
|
||||
)
|
||||
|
||||
func ToQuicMultiaddr(na net.Addr, version quic.VersionNumber) (ma.Multiaddr, error) {
|
||||
udpMA, err := manet.FromNetAddr(na)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch version {
|
||||
case quic.Version1:
|
||||
return udpMA.Encapsulate(quicV1MA), nil
|
||||
default:
|
||||
return nil, errors.New("unknown QUIC version")
|
||||
}
|
||||
}
|
||||
|
||||
func FromQuicMultiaddr(addr ma.Multiaddr) (*net.UDPAddr, quic.VersionNumber, error) {
|
||||
var version quic.VersionNumber
|
||||
var partsBeforeQUIC []ma.Multiaddr
|
||||
ma.ForEach(addr, func(c ma.Component) bool {
|
||||
switch c.Protocol().Code {
|
||||
case ma.P_QUIC_V1:
|
||||
version = quic.Version1
|
||||
return false
|
||||
default:
|
||||
partsBeforeQUIC = append(partsBeforeQUIC, &c)
|
||||
return true
|
||||
}
|
||||
})
|
||||
if len(partsBeforeQUIC) == 0 {
|
||||
return nil, version, errors.New("no addr before QUIC component")
|
||||
}
|
||||
if version == 0 {
|
||||
// Not found
|
||||
return nil, version, errors.New("unknown QUIC version")
|
||||
}
|
||||
netAddr, err := manet.ToNetAddr(ma.Join(partsBeforeQUIC...))
|
||||
if err != nil {
|
||||
return nil, version, err
|
||||
}
|
||||
udpAddr, ok := netAddr.(*net.UDPAddr)
|
||||
if !ok {
|
||||
return nil, 0, errors.New("not a *net.UDPAddr")
|
||||
}
|
||||
return udpAddr, version, nil
|
||||
}
|
||||
353
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/reuse.go
generated
vendored
Normal file
353
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/reuse.go
generated
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
package quicreuse
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/gopacket/routing"
|
||||
"github.com/libp2p/go-netroute"
|
||||
"github.com/quic-go/quic-go"
|
||||
)
|
||||
|
||||
type refCountedQuicTransport interface {
|
||||
LocalAddr() net.Addr
|
||||
|
||||
// Used to send packets directly around QUIC. Useful for hole punching.
|
||||
WriteTo([]byte, net.Addr) (int, error)
|
||||
|
||||
Close() error
|
||||
|
||||
// count transport reference
|
||||
DecreaseCount()
|
||||
IncreaseCount()
|
||||
|
||||
Dial(ctx context.Context, addr net.Addr, tlsConf *tls.Config, conf *quic.Config) (quic.Connection, error)
|
||||
Listen(tlsConf *tls.Config, conf *quic.Config) (*quic.Listener, error)
|
||||
}
|
||||
|
||||
type singleOwnerTransport struct {
|
||||
quic.Transport
|
||||
|
||||
// Used to write packets directly around QUIC.
|
||||
packetConn net.PacketConn
|
||||
}
|
||||
|
||||
func (c *singleOwnerTransport) IncreaseCount() {}
|
||||
func (c *singleOwnerTransport) DecreaseCount() {
|
||||
c.Transport.Close()
|
||||
}
|
||||
|
||||
func (c *singleOwnerTransport) LocalAddr() net.Addr {
|
||||
return c.Transport.Conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *singleOwnerTransport) Close() error {
|
||||
// TODO(when we drop support for go 1.19) use errors.Join
|
||||
c.Transport.Close()
|
||||
return c.packetConn.Close()
|
||||
}
|
||||
|
||||
func (c *singleOwnerTransport) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
return c.Transport.WriteTo(b, addr)
|
||||
}
|
||||
|
||||
// Constant. Defined as variables to simplify testing.
|
||||
var (
|
||||
garbageCollectInterval = 30 * time.Second
|
||||
maxUnusedDuration = 10 * time.Second
|
||||
)
|
||||
|
||||
type refcountedTransport struct {
|
||||
quic.Transport
|
||||
|
||||
// Used to write packets directly around QUIC.
|
||||
packetConn net.PacketConn
|
||||
|
||||
mutex sync.Mutex
|
||||
refCount int
|
||||
unusedSince time.Time
|
||||
}
|
||||
|
||||
func (c *refcountedTransport) IncreaseCount() {
|
||||
c.mutex.Lock()
|
||||
c.refCount++
|
||||
c.unusedSince = time.Time{}
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (c *refcountedTransport) Close() error {
|
||||
// TODO(when we drop support for go 1.19) use errors.Join
|
||||
c.Transport.Close()
|
||||
return c.packetConn.Close()
|
||||
}
|
||||
|
||||
func (c *refcountedTransport) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
return c.Transport.WriteTo(b, addr)
|
||||
}
|
||||
|
||||
func (c *refcountedTransport) LocalAddr() net.Addr {
|
||||
return c.Transport.Conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *refcountedTransport) DecreaseCount() {
|
||||
c.mutex.Lock()
|
||||
c.refCount--
|
||||
if c.refCount == 0 {
|
||||
c.unusedSince = time.Now()
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
|
||||
func (c *refcountedTransport) ShouldGarbageCollect(now time.Time) bool {
|
||||
c.mutex.Lock()
|
||||
defer c.mutex.Unlock()
|
||||
return !c.unusedSince.IsZero() && c.unusedSince.Add(maxUnusedDuration).Before(now)
|
||||
}
|
||||
|
||||
type reuse struct {
|
||||
mutex sync.Mutex
|
||||
|
||||
closeChan chan struct{}
|
||||
gcStopChan chan struct{}
|
||||
|
||||
routes routing.Router
|
||||
unicast map[string] /* IP.String() */ map[int] /* port */ *refcountedTransport
|
||||
// globalListeners contains transports that are listening on 0.0.0.0 / ::
|
||||
globalListeners map[int]*refcountedTransport
|
||||
// globalDialers contains transports that we've dialed out from. These transports are listening on 0.0.0.0 / ::
|
||||
// On Dial, transports are reused from this map if no transport is available in the globalListeners
|
||||
// On Listen, transports are reused from this map if the requested port is 0, and then moved to globalListeners
|
||||
globalDialers map[int]*refcountedTransport
|
||||
|
||||
statelessResetKey *quic.StatelessResetKey
|
||||
tokenGeneratorKey *quic.TokenGeneratorKey
|
||||
}
|
||||
|
||||
func newReuse(srk *quic.StatelessResetKey, tokenKey *quic.TokenGeneratorKey) *reuse {
|
||||
r := &reuse{
|
||||
unicast: make(map[string]map[int]*refcountedTransport),
|
||||
globalListeners: make(map[int]*refcountedTransport),
|
||||
globalDialers: make(map[int]*refcountedTransport),
|
||||
closeChan: make(chan struct{}),
|
||||
gcStopChan: make(chan struct{}),
|
||||
statelessResetKey: srk,
|
||||
tokenGeneratorKey: tokenKey,
|
||||
}
|
||||
go r.gc()
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *reuse) gc() {
|
||||
defer func() {
|
||||
r.mutex.Lock()
|
||||
for _, tr := range r.globalListeners {
|
||||
tr.Close()
|
||||
}
|
||||
for _, tr := range r.globalDialers {
|
||||
tr.Close()
|
||||
}
|
||||
for _, trs := range r.unicast {
|
||||
for _, tr := range trs {
|
||||
tr.Close()
|
||||
}
|
||||
}
|
||||
r.mutex.Unlock()
|
||||
close(r.gcStopChan)
|
||||
}()
|
||||
ticker := time.NewTicker(garbageCollectInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-r.closeChan:
|
||||
return
|
||||
case <-ticker.C:
|
||||
now := time.Now()
|
||||
r.mutex.Lock()
|
||||
for key, tr := range r.globalListeners {
|
||||
if tr.ShouldGarbageCollect(now) {
|
||||
tr.Close()
|
||||
delete(r.globalListeners, key)
|
||||
}
|
||||
}
|
||||
for key, tr := range r.globalDialers {
|
||||
if tr.ShouldGarbageCollect(now) {
|
||||
tr.Close()
|
||||
delete(r.globalDialers, key)
|
||||
}
|
||||
}
|
||||
for ukey, trs := range r.unicast {
|
||||
for key, tr := range trs {
|
||||
if tr.ShouldGarbageCollect(now) {
|
||||
tr.Close()
|
||||
delete(trs, key)
|
||||
}
|
||||
}
|
||||
if len(trs) == 0 {
|
||||
delete(r.unicast, ukey)
|
||||
// If we've dropped all transports with a unicast binding,
|
||||
// assume our routes may have changed.
|
||||
if len(r.unicast) == 0 {
|
||||
r.routes = nil
|
||||
} else {
|
||||
// Ignore the error, there's nothing we can do about
|
||||
// it.
|
||||
r.routes, _ = netroute.New()
|
||||
}
|
||||
}
|
||||
}
|
||||
r.mutex.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reuse) TransportForDial(network string, raddr *net.UDPAddr) (*refcountedTransport, error) {
|
||||
var ip *net.IP
|
||||
|
||||
// Only bother looking up the source address if we actually _have_ non 0.0.0.0 listeners.
|
||||
// Otherwise, save some time.
|
||||
|
||||
r.mutex.Lock()
|
||||
router := r.routes
|
||||
r.mutex.Unlock()
|
||||
|
||||
if router != nil {
|
||||
_, _, src, err := router.Route(raddr.IP)
|
||||
if err == nil && !src.IsUnspecified() {
|
||||
ip = &src
|
||||
}
|
||||
}
|
||||
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
tr, err := r.transportForDialLocked(network, ip)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr.IncreaseCount()
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
func (r *reuse) transportForDialLocked(network string, source *net.IP) (*refcountedTransport, error) {
|
||||
if source != nil {
|
||||
// We already have at least one suitable transport...
|
||||
if trs, ok := r.unicast[source.String()]; ok {
|
||||
// ... we don't care which port we're dialing from. Just use the first.
|
||||
for _, tr := range trs {
|
||||
return tr, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use a transport listening on 0.0.0.0 (or ::).
|
||||
// Again, we don't care about the port number.
|
||||
for _, tr := range r.globalListeners {
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// Use a transport we've previously dialed from
|
||||
for _, tr := range r.globalDialers {
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// We don't have a transport that we can use for dialing.
|
||||
// Dial a new connection from a random port.
|
||||
var addr *net.UDPAddr
|
||||
switch network {
|
||||
case "udp4":
|
||||
addr = &net.UDPAddr{IP: net.IPv4zero, Port: 0}
|
||||
case "udp6":
|
||||
addr = &net.UDPAddr{IP: net.IPv6zero, Port: 0}
|
||||
}
|
||||
conn, err := net.ListenUDP(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr := &refcountedTransport{Transport: quic.Transport{
|
||||
Conn: conn,
|
||||
StatelessResetKey: r.statelessResetKey,
|
||||
TokenGeneratorKey: r.tokenGeneratorKey,
|
||||
}, packetConn: conn}
|
||||
r.globalDialers[conn.LocalAddr().(*net.UDPAddr).Port] = tr
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
func (r *reuse) TransportForListen(network string, laddr *net.UDPAddr) (*refcountedTransport, error) {
|
||||
r.mutex.Lock()
|
||||
defer r.mutex.Unlock()
|
||||
|
||||
// Check if we can reuse a transport we have already dialed out from.
|
||||
// We reuse a transport from globalDialers when the requested port is 0 or the requested
|
||||
// port is already in the globalDialers.
|
||||
// If we are reusing a transport from globalDialers, we move the globalDialers entry to
|
||||
// globalListeners
|
||||
if laddr.IP.IsUnspecified() {
|
||||
var rTr *refcountedTransport
|
||||
var localAddr *net.UDPAddr
|
||||
|
||||
if laddr.Port == 0 {
|
||||
// the requested port is 0, we can reuse any transport
|
||||
for _, tr := range r.globalDialers {
|
||||
rTr = tr
|
||||
localAddr = rTr.LocalAddr().(*net.UDPAddr)
|
||||
delete(r.globalDialers, localAddr.Port)
|
||||
break
|
||||
}
|
||||
} else if _, ok := r.globalDialers[laddr.Port]; ok {
|
||||
rTr = r.globalDialers[laddr.Port]
|
||||
localAddr = rTr.LocalAddr().(*net.UDPAddr)
|
||||
delete(r.globalDialers, localAddr.Port)
|
||||
}
|
||||
// found a match
|
||||
if rTr != nil {
|
||||
rTr.IncreaseCount()
|
||||
r.globalListeners[localAddr.Port] = rTr
|
||||
return rTr, nil
|
||||
}
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
tr := &refcountedTransport{
|
||||
Transport: quic.Transport{
|
||||
Conn: conn,
|
||||
StatelessResetKey: r.statelessResetKey,
|
||||
},
|
||||
packetConn: conn,
|
||||
}
|
||||
tr.IncreaseCount()
|
||||
|
||||
// Deal with listen on a global address
|
||||
if localAddr.IP.IsUnspecified() {
|
||||
// The kernel already checked that the laddr is not already listen
|
||||
// so we need not check here (when we create ListenUDP).
|
||||
r.globalListeners[localAddr.Port] = tr
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
// Deal with listen on a unicast address
|
||||
if _, ok := r.unicast[localAddr.IP.String()]; !ok {
|
||||
r.unicast[localAddr.IP.String()] = make(map[int]*refcountedTransport)
|
||||
// Assume the system's routes may have changed if we're adding a new listener.
|
||||
// Ignore the error, there's nothing we can do.
|
||||
r.routes, _ = netroute.New()
|
||||
}
|
||||
|
||||
// The kernel already checked that the laddr is not already listen
|
||||
// so we need not check here (when we create ListenUDP).
|
||||
r.unicast[localAddr.IP.String()][localAddr.Port] = tr
|
||||
return tr, nil
|
||||
}
|
||||
|
||||
func (r *reuse) Close() error {
|
||||
close(r.closeChan)
|
||||
<-r.gcStopChan
|
||||
return nil
|
||||
}
|
||||
94
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/tracer.go
generated
vendored
Normal file
94
vendor/github.com/libp2p/go-libp2p/p2p/transport/quicreuse/tracer.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
package quicreuse
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
golog "github.com/ipfs/go-log/v2"
|
||||
"github.com/klauspost/compress/zstd"
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/logging"
|
||||
"github.com/quic-go/quic-go/qlog"
|
||||
)
|
||||
|
||||
var log = golog.Logger("quic-utils")
|
||||
|
||||
// QLOGTracer holds a qlog tracer dir, if qlogging is enabled (enabled using the QLOGDIR environment variable).
|
||||
// Otherwise it is an empty string.
|
||||
var qlogTracerDir string
|
||||
|
||||
func init() {
|
||||
qlogTracerDir = os.Getenv("QLOGDIR")
|
||||
}
|
||||
|
||||
func qloggerForDir(qlogDir string, p logging.Perspective, ci quic.ConnectionID) *logging.ConnectionTracer {
|
||||
// create the QLOGDIR, if it doesn't exist
|
||||
if err := os.MkdirAll(qlogDir, 0777); err != nil {
|
||||
log.Errorf("creating the QLOGDIR failed: %s", err)
|
||||
return nil
|
||||
}
|
||||
return qlog.NewConnectionTracer(newQlogger(qlogDir, p, ci), p, ci)
|
||||
}
|
||||
|
||||
// The qlogger logs qlog events to a temporary file: .<name>.qlog.swp.
|
||||
// When it is closed, it compresses the temporary file and saves it as <name>.qlog.zst.
|
||||
// It is not possible to compress on the fly, as compression algorithms keep a lot of internal state,
|
||||
// which can easily exhaust the host system's memory when running a few hundred QUIC connections in parallel.
|
||||
type qlogger struct {
|
||||
f *os.File // QLOGDIR/.log_xxx.qlog.swp
|
||||
filename string // QLOGDIR/log_xxx.qlog.zst
|
||||
*bufio.Writer // buffering the f
|
||||
}
|
||||
|
||||
func newQlogger(qlogDir string, role logging.Perspective, connID quic.ConnectionID) io.WriteCloser {
|
||||
t := time.Now().UTC().Format("2006-01-02T15-04-05.999999999UTC")
|
||||
r := "server"
|
||||
if role == logging.PerspectiveClient {
|
||||
r = "client"
|
||||
}
|
||||
finalFilename := fmt.Sprintf("%s%clog_%s_%s_%s.qlog.zst", qlogDir, os.PathSeparator, t, r, connID)
|
||||
filename := fmt.Sprintf("%s%c.log_%s_%s_%s.qlog.swp", qlogDir, os.PathSeparator, t, r, connID)
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
log.Errorf("unable to create qlog file %s: %s", filename, err)
|
||||
return nil
|
||||
}
|
||||
return &qlogger{
|
||||
f: f,
|
||||
filename: finalFilename,
|
||||
// The size of a qlog file for a raw file download is ~2/3 of the amount of data transferred.
|
||||
// bufio.NewWriter creates a buffer with a buffer of only 4 kB, leading to a large number of syscalls.
|
||||
Writer: bufio.NewWriterSize(f, 128<<10),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *qlogger) Close() error {
|
||||
defer os.Remove(l.f.Name())
|
||||
defer l.f.Close()
|
||||
if err := l.Writer.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := l.f.Seek(0, io.SeekStart); err != nil { // set the read position to the beginning of the file
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(l.filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
buf := bufio.NewWriterSize(f, 128<<10)
|
||||
c, err := zstd.NewWriter(buf, zstd.WithEncoderLevel(zstd.SpeedFastest), zstd.WithWindowSize(32*1024))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(c, l.f); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return buf.Flush()
|
||||
}
|
||||
Reference in New Issue
Block a user