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:
ginuerzh
2026-05-23 16:36:47 +08:00
parent 569572d6f7
commit c7f58a400b
4 changed files with 65 additions and 2 deletions
+38 -1
View File
@@ -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 package chain
import ( import (
@@ -45,6 +73,7 @@ type Chain struct {
logger logger.Logger logger logger.Logger
} }
// NewChain creates a new Chain with the given name and options.
func NewChain(name string, opts ...ChainOption) *Chain { func NewChain(name string, opts ...ChainOption) *Chain {
var options ChainOptions var options ChainOptions
for _, opt := range opts { 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) { func (c *Chain) AddHop(hop hop.Hop) {
c.hops = append(c.hops, 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 { func (c *Chain) Metadata() metadata.Metadata {
return c.metadata return c.metadata
} }
@@ -79,6 +111,9 @@ func (c *Chain) Name() string {
return c.name 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 { func (c *Chain) Route(ctx context.Context, network, address string, opts ...chain.RouteOption) chain.Route {
if c == nil || len(c.hops) == 0 { if c == nil || len(c.hops) == 0 {
return nil return nil
@@ -117,6 +152,8 @@ type chainGroup struct {
selector selector.Selector[chain.Chainer] 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 { func NewChainGroup(chains ...chain.Chainer) *chainGroup {
return &chainGroup{chains: chains} return &chainGroup{chains: chains}
} }
+8 -1
View File
@@ -19,14 +19,18 @@ import (
) )
var ( var (
// ErrEmptyRoute is returned when the configured chain produces a route
// with no nodes.
ErrEmptyRoute = errors.New("empty route") ErrEmptyRoute = errors.New("empty route")
) )
var ( var (
// DefaultRoute is a fallback route that dials the target directly
// without going through any proxy nodes.
DefaultRoute chain.Route = &defaultRoute{} DefaultRoute chain.Route = &defaultRoute{}
) )
// defaultRoute is a Route without nodes. // defaultRoute dials the target directly without any proxy nodes.
type defaultRoute struct{} type defaultRoute struct{}
func (*defaultRoute) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (net.Conn, error) { 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) 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 { func ChainRouteOption(c chain.Chainer) RouteOption {
return func(o *RouteOptions) { return func(o *RouteOptions) {
o.Chain = c o.Chain = c
@@ -109,6 +115,7 @@ type chainRoute struct {
options RouteOptions options RouteOptions
} }
// NewRoute creates a new route with the given options.
func NewRoute(opts ...RouteOption) *chainRoute { func NewRoute(opts ...RouteOption) *chainRoute {
var options RouteOptions var options RouteOptions
for _, opt := range opts { for _, opt := range opts {
+12
View File
@@ -15,10 +15,15 @@ import (
xnet "github.com/go-gost/x/internal/net" 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 { type Router struct {
options chain.RouterOptions 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 { func NewRouter(opts ...chain.RouterOption) *Router {
r := &Router{} r := &Router{}
for _, opt := range opts { for _, opt := range opts {
@@ -43,6 +48,11 @@ func (r *Router) Options() *chain.RouterOptions {
return &r.options 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) { func (r *Router) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (conn net.Conn, err error) {
host := address host := address
if h, _, _ := net.SplitHostPort(address); h != "" { if h, _, _ := net.SplitHostPort(address); h != "" {
@@ -152,6 +162,8 @@ func (r *Router) dial(ctx context.Context, network, address string, log logger.L
return 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) { func (r *Router) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (ln net.Listener, err error) {
count := r.options.Retries + 1 count := r.options.Retries + 1
if count <= 0 { if count <= 0 {
+7
View File
@@ -10,12 +10,16 @@ import (
net_dialer "github.com/go-gost/x/internal/net/dialer" 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 { type Transport struct {
dialer dialer.Dialer dialer dialer.Dialer
connector connector.Connector connector connector.Connector
options chain.TransportOptions 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 { func NewTransport(d dialer.Dialer, c connector.Connector, opts ...chain.TransportOption) *Transport {
tr := &Transport{ tr := &Transport{
dialer: d, dialer: d,
@@ -99,6 +103,9 @@ func (tr *Transport) Options() *chain.TransportOptions {
return nil 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 { func (tr *Transport) Copy() chain.Transporter {
tr2 := &Transport{} tr2 := &Transport{}
*tr2 = *tr *tr2 = *tr