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
|
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
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user