105 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			105 lines
		
	
	
		
			2.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package chain
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
 | 
						|
	"github.com/go-gost/core/metadata"
 | 
						|
	"github.com/go-gost/core/selector"
 | 
						|
)
 | 
						|
 | 
						|
type Chainer interface {
 | 
						|
	Route(ctx context.Context, network, address string) Route
 | 
						|
}
 | 
						|
 | 
						|
type Chain struct {
 | 
						|
	name     string
 | 
						|
	groups   []*NodeGroup
 | 
						|
	marker   selector.Marker
 | 
						|
	metadata metadata.Metadata
 | 
						|
}
 | 
						|
 | 
						|
func NewChain(name string, groups ...*NodeGroup) *Chain {
 | 
						|
	return &Chain{
 | 
						|
		name:   name,
 | 
						|
		groups: groups,
 | 
						|
		marker: selector.NewFailMarker(),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (c *Chain) AddNodeGroup(group *NodeGroup) {
 | 
						|
	c.groups = append(c.groups, group)
 | 
						|
}
 | 
						|
 | 
						|
func (c *Chain) WithMetadata(md metadata.Metadata) {
 | 
						|
	c.metadata = md
 | 
						|
}
 | 
						|
 | 
						|
// Metadata implements metadata.Metadatable interface.
 | 
						|
func (c *Chain) Metadata() metadata.Metadata {
 | 
						|
	return c.metadata
 | 
						|
}
 | 
						|
 | 
						|
// Marker implements selector.Markable interface.
 | 
						|
func (c *Chain) Marker() selector.Marker {
 | 
						|
	return c.marker
 | 
						|
}
 | 
						|
 | 
						|
func (c *Chain) Route(ctx context.Context, network, address string) Route {
 | 
						|
	if c == nil || len(c.groups) == 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	rt := newRoute().WithChain(c)
 | 
						|
	for _, group := range c.groups {
 | 
						|
		// hop level bypass test
 | 
						|
		if group.bypass != nil && group.bypass.Contains(address) {
 | 
						|
			break
 | 
						|
		}
 | 
						|
 | 
						|
		node := group.FilterAddr(address).Next(ctx)
 | 
						|
		if node == nil {
 | 
						|
			return rt
 | 
						|
		}
 | 
						|
		if node.transport.Multiplex() {
 | 
						|
			tr := node.transport.
 | 
						|
				Copy().
 | 
						|
				WithRoute(rt)
 | 
						|
			node = node.Copy()
 | 
						|
			node.transport = tr
 | 
						|
			rt = newRoute()
 | 
						|
		}
 | 
						|
 | 
						|
		rt.addNode(node)
 | 
						|
	}
 | 
						|
	return rt
 | 
						|
}
 | 
						|
 | 
						|
type ChainGroup struct {
 | 
						|
	chains   []Chainer
 | 
						|
	selector selector.Selector[Chainer]
 | 
						|
}
 | 
						|
 | 
						|
func NewChainGroup(chains ...Chainer) *ChainGroup {
 | 
						|
	return &ChainGroup{chains: chains}
 | 
						|
}
 | 
						|
 | 
						|
func (p *ChainGroup) WithSelector(s selector.Selector[Chainer]) *ChainGroup {
 | 
						|
	p.selector = s
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func (p *ChainGroup) Route(ctx context.Context, network, address string) Route {
 | 
						|
	if chain := p.next(ctx); chain != nil {
 | 
						|
		return chain.Route(ctx, network, address)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (p *ChainGroup) next(ctx context.Context) Chainer {
 | 
						|
	if p == nil || len(p.chains) == 0 {
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	return p.selector.Select(ctx, p.chains...)
 | 
						|
}
 |