From c7f58a400be26e7108c3b972575ada06eb96134b Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Sat, 23 May 2026 16:36:47 +0800 Subject: [PATCH] docs(chain): add package and symbol doc comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover the three main abstractions (Router, Chain, Transport), route traversal lifecycle (Dial → Handshake → Connect), and multiplexing sub-route splitting. --- chain/chain.go | 39 ++++++++++++++++++++++++++++++++++++++- chain/route.go | 9 ++++++++- chain/router.go | 12 ++++++++++++ chain/transport.go | 7 +++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/chain/chain.go b/chain/chain.go index 458045ad..fe971701 100644 --- a/chain/chain.go +++ b/chain/chain.go @@ -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} } diff --git a/chain/route.go b/chain/route.go index 811efba2..e582a9f4 100644 --- a/chain/route.go +++ b/chain/route.go @@ -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 { diff --git a/chain/router.go b/chain/router.go index 2943d409..1a10c76e 100644 --- a/chain/router.go +++ b/chain/router.go @@ -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 { diff --git a/chain/transport.go b/chain/transport.go index a1db527d..55b5cde7 100644 --- a/chain/transport.go +++ b/chain/transport.go @@ -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