docs(chain): add package and symbol doc comments
Cover the three main abstractions (Router, Chain, Transport), route traversal lifecycle (Dial → Handshake → Connect), and multiplexing sub-route splitting.
This commit is contained in:
+38
-1
@@ -1,3 +1,31 @@
|
||||
// Package chain implements the core routing infrastructure for GOST.
|
||||
//
|
||||
// It provides three key abstractions:
|
||||
//
|
||||
// - Router: top-level entry point that resolves addresses, selects routes
|
||||
// via a Chainer, retries on failure, and records telemetry.
|
||||
//
|
||||
// - Chain: a named sequence of proxy hops (nodes). Each hop selects a node
|
||||
// from its group, and the resulting Route carries traffic through every
|
||||
// selected node in order.
|
||||
//
|
||||
// - Transport: bundles a dialer and connector for a single chain node. It
|
||||
// handles Dial, Handshake, Connect, and Bind — the four steps needed to
|
||||
// move traffic through a proxy hop.
|
||||
//
|
||||
// # Route traversal
|
||||
//
|
||||
// For a chain of N nodes, the first node is reached via Dial → Handshake,
|
||||
// and each subsequent node via Connect → Handshake through the previous
|
||||
// connection. On failure, connections are cleaned up and nodes are marked
|
||||
// so selectors can deprioritize them.
|
||||
//
|
||||
// # Multiplexing
|
||||
//
|
||||
// When a node's transport supports multiplexing, Chain splits the route at
|
||||
// that point: nodes before the multiplex-capable node form a sub-route that is
|
||||
// copied into the transport, establishing a reusable tunnel for subsequent
|
||||
// connections.
|
||||
package chain
|
||||
|
||||
import (
|
||||
@@ -45,6 +73,7 @@ type Chain struct {
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewChain creates a new Chain with the given name and options.
|
||||
func NewChain(name string, opts ...ChainOption) *Chain {
|
||||
var options ChainOptions
|
||||
for _, opt := range opts {
|
||||
@@ -61,11 +90,14 @@ func NewChain(name string, opts ...ChainOption) *Chain {
|
||||
}
|
||||
}
|
||||
|
||||
// AddHop appends a hop to the chain. Hops are traversed in order during
|
||||
// route construction.
|
||||
func (c *Chain) AddHop(hop hop.Hop) {
|
||||
c.hops = append(c.hops, hop)
|
||||
}
|
||||
|
||||
// Metadata implements metadata.Metadatable interface.
|
||||
// Metadata returns the chain's metadata.
|
||||
// Implements metadata.Metadatable interface.
|
||||
func (c *Chain) Metadata() metadata.Metadata {
|
||||
return c.metadata
|
||||
}
|
||||
@@ -79,6 +111,9 @@ func (c *Chain) Name() string {
|
||||
return c.name
|
||||
}
|
||||
|
||||
// Route builds a route by selecting one node from each hop. If a node
|
||||
// supports multiplexing, the route is split — nodes before it form a
|
||||
// sub-route that is copied into the transport for reuse.
|
||||
func (c *Chain) Route(ctx context.Context, network, address string, opts ...chain.RouteOption) chain.Route {
|
||||
if c == nil || len(c.hops) == 0 {
|
||||
return nil
|
||||
@@ -117,6 +152,8 @@ type chainGroup struct {
|
||||
selector selector.Selector[chain.Chainer]
|
||||
}
|
||||
|
||||
// NewChainGroup creates a chain group that selects one Chainer from the
|
||||
// given list using the configured selector (round-robin by default).
|
||||
func NewChainGroup(chains ...chain.Chainer) *chainGroup {
|
||||
return &chainGroup{chains: chains}
|
||||
}
|
||||
|
||||
+8
-1
@@ -19,14 +19,18 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrEmptyRoute is returned when the configured chain produces a route
|
||||
// with no nodes.
|
||||
ErrEmptyRoute = errors.New("empty route")
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultRoute is a fallback route that dials the target directly
|
||||
// without going through any proxy nodes.
|
||||
DefaultRoute chain.Route = &defaultRoute{}
|
||||
)
|
||||
|
||||
// defaultRoute is a Route without nodes.
|
||||
// defaultRoute dials the target directly without any proxy nodes.
|
||||
type defaultRoute struct{}
|
||||
|
||||
func (*defaultRoute) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (net.Conn, error) {
|
||||
@@ -98,6 +102,8 @@ type RouteOptions struct {
|
||||
|
||||
type RouteOption func(*RouteOptions)
|
||||
|
||||
// ChainRouteOption sets the parent chain on a route, enabling error
|
||||
// tracking and metrics for the chain.
|
||||
func ChainRouteOption(c chain.Chainer) RouteOption {
|
||||
return func(o *RouteOptions) {
|
||||
o.Chain = c
|
||||
@@ -109,6 +115,7 @@ type chainRoute struct {
|
||||
options RouteOptions
|
||||
}
|
||||
|
||||
// NewRoute creates a new route with the given options.
|
||||
func NewRoute(opts ...RouteOption) *chainRoute {
|
||||
var options RouteOptions
|
||||
for _, opt := range opts {
|
||||
|
||||
@@ -15,10 +15,15 @@ import (
|
||||
xnet "github.com/go-gost/x/internal/net"
|
||||
)
|
||||
|
||||
// Router is the top-level routing entry point. It resolves addresses,
|
||||
// selects routes through the configured chain, retries on failure, and
|
||||
// records telemetry (address events, errors, metrics).
|
||||
type Router struct {
|
||||
options chain.RouterOptions
|
||||
}
|
||||
|
||||
// NewRouter creates a Router with the given options. Defaults: 15s timeout,
|
||||
// a logger with kind "router" if none is provided.
|
||||
func NewRouter(opts ...chain.RouterOption) *Router {
|
||||
r := &Router{}
|
||||
for _, opt := range opts {
|
||||
@@ -43,6 +48,11 @@ func (r *Router) Options() *chain.RouterOptions {
|
||||
return &r.options
|
||||
}
|
||||
|
||||
// Dial establishes a connection to the target address through the configured
|
||||
// chain. It resolves the address, records the dial event, retries up to
|
||||
// Retries+1 times, and returns the resulting connection.
|
||||
// For UDP networks the returned connection is wrapped as a PacketConn if the
|
||||
// underlying transport does not already implement net.PacketConn.
|
||||
func (r *Router) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (conn net.Conn, err error) {
|
||||
host := address
|
||||
if h, _, _ := net.SplitHostPort(address); h != "" {
|
||||
@@ -152,6 +162,8 @@ func (r *Router) dial(ctx context.Context, network, address string, log logger.L
|
||||
return
|
||||
}
|
||||
|
||||
// Bind creates a listener bound to the given address through the configured
|
||||
// chain. It retries up to Retries+1 times on failure.
|
||||
func (r *Router) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (ln net.Listener, err error) {
|
||||
count := r.options.Retries + 1
|
||||
if count <= 0 {
|
||||
|
||||
@@ -10,12 +10,16 @@ import (
|
||||
net_dialer "github.com/go-gost/x/internal/net/dialer"
|
||||
)
|
||||
|
||||
// Transport bundles a dialer and connector for a single chain node.
|
||||
// It implements chain.Transporter and handles the four operations in
|
||||
// the proxy chain lifecycle: Dial, Handshake, Connect, and Bind.
|
||||
type Transport struct {
|
||||
dialer dialer.Dialer
|
||||
connector connector.Connector
|
||||
options chain.TransportOptions
|
||||
}
|
||||
|
||||
// NewTransport creates a Transport with the given dialer and connector.
|
||||
func NewTransport(d dialer.Dialer, c connector.Connector, opts ...chain.TransportOption) *Transport {
|
||||
tr := &Transport{
|
||||
dialer: d,
|
||||
@@ -99,6 +103,9 @@ func (tr *Transport) Options() *chain.TransportOptions {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Copy returns a shallow copy of the transport. Used by Chain when
|
||||
// splitting a route at a multiplex-capable node so the sub-route can
|
||||
// be embedded in the transport without sharing state across calls.
|
||||
func (tr *Transport) Copy() chain.Transporter {
|
||||
tr2 := &Transport{}
|
||||
*tr2 = *tr
|
||||
|
||||
Reference in New Issue
Block a user