add chain

This commit is contained in:
ginuerzh
2021-10-26 21:07:46 +08:00
parent ce13b2a82a
commit 3351aa5974
78 changed files with 917 additions and 185 deletions

33
pkg/chain/chain.go Normal file
View File

@ -0,0 +1,33 @@
package chain
type Chain struct {
groups []*NodeGroup
}
func (c *Chain) AddNodeGroup(group *NodeGroup) {
c.groups = append(c.groups, group)
}
func (c *Chain) GetRoute() (r *Route) {
if c == nil || len(c.groups) == 0 {
return
}
r = &Route{}
for _, group := range c.groups {
node := group.Next()
if node == nil {
return
}
// TODO: bypass
if node.Transport().IsMultiplex() {
tr := node.Transport().WithRoute(r)
node = node.WithTransport(tr)
r = &Route{}
}
r.AddNode(node)
}
return r
}

59
pkg/chain/node.go Normal file
View File

@ -0,0 +1,59 @@
package chain
type Node struct {
name string
addr string
transport *Transport
}
func NewNode(name, addr string) *Node {
return &Node{
name: name,
addr: addr,
}
}
func (node *Node) Name() string {
return node.name
}
func (node *Node) Addr() string {
return node.addr
}
func (node *Node) Transport() *Transport {
return node.transport
}
func (node *Node) WithTransport(tr *Transport) *Node {
node.transport = tr
return node
}
type NodeGroup struct {
nodes []*Node
selector Selector
}
func NewNodeGroup(nodes ...*Node) *NodeGroup {
return &NodeGroup{
nodes: nodes,
}
}
func (g *NodeGroup) AddNode(node *Node) {
g.nodes = append(g.nodes, node)
}
func (g *NodeGroup) WithSelector(selector Selector) {
g.selector = selector
}
func (g *NodeGroup) Next() *Node {
selector := g.selector
if selector == nil {
// selector = defaultSelector
return g.nodes[0]
}
return selector.Select(g.nodes...)
}

93
pkg/chain/route.go Normal file
View File

@ -0,0 +1,93 @@
package chain
import (
"context"
"errors"
"net"
)
type Route struct {
nodes []*Node
}
func (r *Route) AddNode(node *Node) {
r.nodes = append(r.nodes, node)
}
func (r *Route) Connect(ctx context.Context) (conn net.Conn, err error) {
if r.IsEmpty() {
return nil, errors.New("empty route")
}
node := r.nodes[0]
cc, err := node.Transport().Dial(ctx, r.nodes[0].Addr())
if err != nil {
return
}
cn, err := node.Transport().Handshake(ctx, cc)
if err != nil {
cc.Close()
return
}
preNode := node
for _, node := range r.nodes[1:] {
cc, err = preNode.Transport().Connect(ctx, cn, "tcp", node.Addr())
if err != nil {
cn.Close()
return
}
cc, err = node.transport.Handshake(ctx, cc)
if err != nil {
cn.Close()
}
cn = cc
preNode = node
}
conn = cn
return
}
func (r *Route) Dial(ctx context.Context, network, address string) (net.Conn, error) {
if r.IsEmpty() {
return r.dialDirect(ctx, network, address)
}
conn, err := r.Connect(ctx)
if err != nil {
return nil, err
}
cc, err := r.Last().Transport().Connect(ctx, conn, network, address)
if err != nil {
conn.Close()
return nil, err
}
return cc, nil
}
func (r *Route) dialDirect(ctx context.Context, network, address string) (net.Conn, error) {
switch network {
case "udp", "udp4", "udp6":
if address == "" {
return net.ListenUDP(network, nil)
}
default:
}
d := &net.Dialer{}
return d.DialContext(ctx, network, address)
}
func (r *Route) IsEmpty() bool {
return r == nil || len(r.nodes) == 0
}
func (r Route) Last() *Node {
if r.IsEmpty() {
return nil
}
return r.nodes[len(r.nodes)-1]
}

41
pkg/chain/selector.go Normal file
View File

@ -0,0 +1,41 @@
package chain
var (
defaultSelector Selector = NewSelector(nil)
)
type Filter interface {
Filter(nodes ...*Node) []*Node
String() string
}
type Strategy interface {
Apply(nodes ...*Node) *Node
String() string
}
type Selector interface {
Select(nodes ...*Node) *Node
}
type selector struct {
strategy Strategy
filters []Filter
}
func NewSelector(strategy Strategy, filters ...Filter) Selector {
return &selector{
filters: filters,
strategy: strategy,
}
}
func (s *selector) Select(nodes ...*Node) *Node {
for _, filter := range s.filters {
nodes = filter.Filter(nodes...)
}
if len(nodes) == 0 {
return nil
}
return s.strategy.Apply(nodes...)
}

66
pkg/chain/transport.go Normal file
View File

@ -0,0 +1,66 @@
package chain
import (
"context"
"net"
"github.com/go-gost/gost/pkg/components/connector"
"github.com/go-gost/gost/pkg/components/dialer"
)
type Transport struct {
route *Route
dialer dialer.Dialer
connector connector.Connector
}
func (tr *Transport) WithDialer(dialer dialer.Dialer) *Transport {
tr.dialer = dialer
return tr
}
func (tr *Transport) WithConnector(connector connector.Connector) *Transport {
tr.connector = connector
return tr
}
func (tr *Transport) Dial(ctx context.Context, addr string) (net.Conn, error) {
return tr.dialer.Dial(ctx, addr, tr.dialOptions()...)
}
func (tr *Transport) dialOptions() []dialer.DialOption {
var opts []dialer.DialOption
if tr.route != nil {
opts = append(opts,
dialer.DialFuncDialOption(
func(ctx context.Context, addr string) (net.Conn, error) {
return tr.route.Dial(ctx, "tcp", addr)
},
),
)
}
return opts
}
func (tr *Transport) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
if hs, ok := tr.dialer.(dialer.Handshaker); ok {
return hs.Handshake(ctx, conn)
}
return conn, nil
}
func (tr *Transport) Connect(ctx context.Context, conn net.Conn, network, address string) (net.Conn, error) {
return tr.connector.Connect(ctx, conn, network, address)
}
func (tr *Transport) IsMultiplex() bool {
if mux, ok := tr.dialer.(dialer.Multiplexer); ok {
return mux.IsMultiplex()
}
return false
}
func (tr *Transport) WithRoute(r *Route) *Transport {
tr.route = r
return tr
}