memberlistquic

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 22, 2026 License: Apache-2.0 Imports: 12 Imported by: 2

README

memberlist-quic

QUIC transport for hashicorp/memberlist, replacing the default UDP+TCP transport with quic-go.

All traffic runs over a single UDP socket using TLS 1.3 mutual authentication, QUIC datagrams (RFC 9221) for probe packets, and multiplexed streams for state sync.

Features

  • Drop-in memberlist.Transport and NodeAwareTransport implementation
  • TLS 1.3 mutual authentication on all connections
  • QUIC datagrams for packet operations, with automatic stream fallback when payloads exceed the datagram MTU
  • Multiplexed bidirectional streams for push-pull state sync
  • Connection pool with dial-on-demand, idle eviction, and duplicate connection tiebreaking
  • Pool exposed for application-level multiplexing on the same peer connections

Quick Start

import (
    memberlistquic "github.com/wjordan/memberlist-quic"
    "github.com/wjordan/memberlist-quic/tlsutil"
    "github.com/hashicorp/memberlist"
)

// Generate a CA and node certificate
caCert, caKey, _ := tlsutil.GenerateCA("my-cluster", 365*24*time.Hour)
nodeCert, nodeKey, _ := tlsutil.GenerateNodeCert(caCert, caKey, "node-1", 365*24*time.Hour)
tlsCfg, _ := tlsutil.MutualTLSConfig(nodeCert, nodeKey, caCert)

// Create the QUIC transport
transport, _ := memberlistquic.New(memberlistquic.Config{
    BindAddr: "0.0.0.0",
    BindPort: 7946,
    TLS:      tlsCfg,
})

// Use it with memberlist
cfg := memberlist.DefaultLANConfig()
cfg.Transport = transport
cfg.Name = "node-1"
list, _ := memberlist.Create(cfg)
defer list.Shutdown()
defer transport.Shutdown()

TLS Setup

The tlsutil package provides helpers for generating certificates suitable for mutual TLS:

// Generate a self-signed CA
caCert, caKey, err := tlsutil.GenerateCA("my-org", 365*24*time.Hour)

// Generate a node certificate signed by the CA
// The node ID is set as the certificate Common Name
nodeCert, nodeKey, err := tlsutil.GenerateNodeCert(caCert, caKey, "node-1", 365*24*time.Hour)

// Build a mutual TLS config from the node cert and CA
tlsCfg, err := tlsutil.MutualTLSConfig(nodeCert, nodeKey, caCert)

In production, use your own CA and certificate management instead of the built-in helpers.

Connection Pool Sharing

The underlying QUIC connection pool is exposed so applications can open additional streams on the same peer connections:

pool := transport.ConnPool()

// Get an existing connection to a peer (does not dial)
conn := pool.GetConnection("10.0.0.2:7946")

// Or get-or-dial
conn, err := pool.GetOrDial(ctx, "10.0.0.2:7946")

// Open an application-level stream
stream, err := conn.OpenStream()

Configuration

Field Default Description
BindAddr (required) UDP address to listen on
BindPort (required) UDP port for listening and advertising
TLS (required) TLS config with mutual authentication
Logger log.Default() Logger for transport messages
MaxIdleTimeout 30s QUIC connection idle timeout
KeepAlivePeriod 10s QUIC keep-alive interval
PacketQueueSize 256 Inbound packet channel buffer size
StreamQueueSize 16 Inbound stream channel buffer size
MaxConnectionAge 0 (no limit) Max lifetime for pooled connections
PoolSweepInterval 30s How often idle/dead connections are reaped

Requirements

  • Go 1.24+
  • QUIC datagram support (RFC 9221) is used when available but not required — the transport falls back to streams automatically

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	BindAddr string
	BindPort int

	TLS *tls.Config

	Logger *log.Logger

	MaxIdleTimeout  time.Duration
	KeepAlivePeriod time.Duration

	PacketQueueSize int
	StreamQueueSize int

	MaxConnectionAge  time.Duration
	PoolSweepInterval time.Duration
}

Config configures the QUIC transport.

type ConnPool

type ConnPool struct {
	// contains filtered or unexported fields
}

ConnPool manages QUIC connections to peers.

func (*ConnPool) AddInbound added in v0.1.1

func (p *ConnPool) AddInbound(conn *quic.Conn)

AddInbound registers an inbound (or externally established) connection in the pool.

func (*ConnPool) CloseConnection

func (p *ConnPool) CloseConnection(addr string)

CloseConnection closes and removes the connection to addr.

func (*ConnPool) GetConnection

func (p *ConnPool) GetConnection(addr string) *quic.Conn

GetConnection returns an existing connection to the given address, or nil.

func (*ConnPool) GetOrDial

func (p *ConnPool) GetOrDial(ctx context.Context, addr string) (*quic.Conn, error)

GetOrDial returns an existing connection or dials a new one.

func (*ConnPool) Len

func (p *ConnPool) Len() int

Len returns the number of active connections.

func (*ConnPool) Range

func (p *ConnPool) Range(fn func(addr string, conn *quic.Conn) bool)

Range iterates over all live connections.

type Transport

type Transport struct {
	// contains filtered or unexported fields
}

Transport implements memberlist.Transport and memberlist.NodeAwareTransport over QUIC.

func New

func New(config Config) (*Transport, error)

New creates and starts a QUIC transport.

func (*Transport) ConnPool

func (t *Transport) ConnPool() *ConnPool

ConnPool returns the underlying connection pool.

func (*Transport) DialAddressTimeout

func (t *Transport) DialAddressTimeout(addr memberlist.Address, timeout time.Duration) (net.Conn, error)

DialAddressTimeout opens a stream to the given address.

func (*Transport) DialTimeout

func (t *Transport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error)

DialTimeout opens a stream to the given address.

func (*Transport) FinalAdvertiseAddr

func (t *Transport) FinalAdvertiseAddr(ip string, port int) (net.IP, int, error)

FinalAdvertiseAddr returns the IP and port to advertise.

func (*Transport) PacketCh

func (t *Transport) PacketCh() <-chan *memberlist.Packet

PacketCh returns the channel for inbound packets.

func (*Transport) RawTransport added in v0.1.1

func (t *Transport) RawTransport() *quic.Transport

RawTransport returns the underlying QUIC transport. This is needed for hole punching — outgoing dials must originate from the same UDP socket as the listener so the NAT mapping is preserved.

func (*Transport) Shutdown

func (t *Transport) Shutdown() error

Shutdown closes the transport.

func (*Transport) StreamCh

func (t *Transport) StreamCh() <-chan net.Conn

StreamCh returns the channel for inbound streams.

func (*Transport) WriteTo

func (t *Transport) WriteTo(b []byte, addr string) (time.Time, error)

WriteTo sends a packet to the given address.

func (*Transport) WriteToAddress

func (t *Transport) WriteToAddress(b []byte, addr memberlist.Address) (time.Time, error)

WriteToAddress sends a packet to the given address.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL