github.com/xmplusdev/xmcore@v1.8.11-0.20240412132628-5518b55526af/app/router/config.go (about)

     1  package router
     2  
     3  import (
     4  	"regexp"
     5  	"strings"
     6  
     7  	"github.com/xmplusdev/xmcore/common/net"
     8  	"github.com/xmplusdev/xmcore/features/outbound"
     9  	"github.com/xmplusdev/xmcore/features/routing"
    10  )
    11  
    12  type Rule struct {
    13  	Tag       string
    14  	RuleTag   string
    15  	Balancer  *Balancer
    16  	Condition Condition
    17  }
    18  
    19  func (r *Rule) GetTag() (string, error) {
    20  	if r.Balancer != nil {
    21  		return r.Balancer.PickOutbound()
    22  	}
    23  	return r.Tag, nil
    24  }
    25  
    26  // Apply checks rule matching of current routing context.
    27  func (r *Rule) Apply(ctx routing.Context) bool {
    28  	return r.Condition.Apply(ctx)
    29  }
    30  
    31  func (rr *RoutingRule) BuildCondition() (Condition, error) {
    32  	conds := NewConditionChan()
    33  
    34  	if len(rr.Domain) > 0 {
    35  		switch rr.DomainMatcher {
    36  		case "linear":
    37  			matcher, err := NewDomainMatcher(rr.Domain)
    38  			if err != nil {
    39  				return nil, newError("failed to build domain condition").Base(err)
    40  			}
    41  			conds.Add(matcher)
    42  		case "mph", "hybrid":
    43  			fallthrough
    44  		default:
    45  			matcher, err := NewMphMatcherGroup(rr.Domain)
    46  			if err != nil {
    47  				return nil, newError("failed to build domain condition with MphDomainMatcher").Base(err)
    48  			}
    49  			newError("MphDomainMatcher is enabled for ", len(rr.Domain), " domain rule(s)").AtDebug().WriteToLog()
    50  			conds.Add(matcher)
    51  		}
    52  	}
    53  
    54  	if len(rr.UserEmail) > 0 {
    55  		conds.Add(NewUserMatcher(rr.UserEmail))
    56  	}
    57  
    58  	if len(rr.InboundTag) > 0 {
    59  		conds.Add(NewInboundTagMatcher(rr.InboundTag))
    60  	}
    61  
    62  	if rr.PortList != nil {
    63  		conds.Add(NewPortMatcher(rr.PortList, false))
    64  	} else if rr.PortRange != nil {
    65  		conds.Add(NewPortMatcher(&net.PortList{Range: []*net.PortRange{rr.PortRange}}, false))
    66  	}
    67  
    68  	if rr.SourcePortList != nil {
    69  		conds.Add(NewPortMatcher(rr.SourcePortList, true))
    70  	}
    71  
    72  	if len(rr.Networks) > 0 {
    73  		conds.Add(NewNetworkMatcher(rr.Networks))
    74  	} else if rr.NetworkList != nil {
    75  		conds.Add(NewNetworkMatcher(rr.NetworkList.Network))
    76  	}
    77  
    78  	if len(rr.Geoip) > 0 {
    79  		cond, err := NewMultiGeoIPMatcher(rr.Geoip, false)
    80  		if err != nil {
    81  			return nil, err
    82  		}
    83  		conds.Add(cond)
    84  	} else if len(rr.Cidr) > 0 {
    85  		cond, err := NewMultiGeoIPMatcher([]*GeoIP{{Cidr: rr.Cidr}}, false)
    86  		if err != nil {
    87  			return nil, err
    88  		}
    89  		conds.Add(cond)
    90  	}
    91  
    92  	if len(rr.SourceGeoip) > 0 {
    93  		cond, err := NewMultiGeoIPMatcher(rr.SourceGeoip, true)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  		conds.Add(cond)
    98  	} else if len(rr.SourceCidr) > 0 {
    99  		cond, err := NewMultiGeoIPMatcher([]*GeoIP{{Cidr: rr.SourceCidr}}, true)
   100  		if err != nil {
   101  			return nil, err
   102  		}
   103  		conds.Add(cond)
   104  	}
   105  
   106  	if len(rr.Protocol) > 0 {
   107  		conds.Add(NewProtocolMatcher(rr.Protocol))
   108  	}
   109  
   110  	if len(rr.Attributes) > 0 {
   111  		configuredKeys := make(map[string]*regexp.Regexp)
   112  		for key, value := range rr.Attributes {
   113  			configuredKeys[strings.ToLower(key)] = regexp.MustCompile(value)
   114  		}
   115  		conds.Add(&AttributeMatcher{configuredKeys})
   116  	}
   117  
   118  	if conds.Len() == 0 {
   119  		return nil, newError("this rule has no effective fields").AtWarning()
   120  	}
   121  
   122  	return conds, nil
   123  }
   124  
   125  // Build builds the balancing rule
   126  func (br *BalancingRule) Build(ohm outbound.Manager, dispatcher routing.Dispatcher) (*Balancer, error) {
   127  	switch strings.ToLower(br.Strategy) {
   128  	case "leastping":
   129  		return &Balancer{
   130  			selectors:   br.OutboundSelector,
   131  			strategy:    &LeastPingStrategy{},
   132  			fallbackTag: br.FallbackTag,
   133  			ohm:         ohm,
   134  		}, nil
   135  	case "roundrobin":
   136  		return &Balancer{
   137  			selectors:   br.OutboundSelector,
   138  			strategy:    &RoundRobinStrategy{},
   139  			fallbackTag: br.FallbackTag,
   140  			ohm:         ohm,
   141  		}, nil
   142  	case "leastload":
   143  		i, err := br.StrategySettings.GetInstance()
   144  		if err != nil {
   145  			return nil, err
   146  		}
   147  		s, ok := i.(*StrategyLeastLoadConfig)
   148  		if !ok {
   149  			return nil, newError("not a StrategyLeastLoadConfig").AtError()
   150  		}
   151  		leastLoadStrategy := NewLeastLoadStrategy(s)
   152  		return &Balancer{
   153  			selectors:   br.OutboundSelector,
   154  			ohm:         ohm,
   155  			fallbackTag: br.FallbackTag,
   156  			strategy:    leastLoadStrategy,
   157  		}, nil
   158  	case "random":
   159  		fallthrough
   160  	case "":
   161  		return &Balancer{
   162  			selectors:   br.OutboundSelector,
   163  			ohm:         ohm,
   164  			fallbackTag: br.FallbackTag,
   165  			strategy:    &RandomStrategy{},
   166  		}, nil
   167  	default:
   168  		return nil, newError("unrecognized balancer type")
   169  	}
   170  }