github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/app/proxyman/outbound/outbound.go (about)

     1  package outbound
     2  
     3  //go:generate go run github.com/xtls/xray-core/common/errors/errorgen
     4  
     5  import (
     6  	"context"
     7  	"sort"
     8  	"strings"
     9  	"sync"
    10  
    11  	"github.com/xtls/xray-core/app/proxyman"
    12  	"github.com/xtls/xray-core/common"
    13  	"github.com/xtls/xray-core/common/errors"
    14  	"github.com/xtls/xray-core/core"
    15  	"github.com/xtls/xray-core/features/outbound"
    16  )
    17  
    18  // Manager is to manage all outbound handlers.
    19  type Manager struct {
    20  	access           sync.RWMutex
    21  	defaultHandler   outbound.Handler
    22  	taggedHandler    map[string]outbound.Handler
    23  	untaggedHandlers []outbound.Handler
    24  	running          bool
    25  	tagsCache        *sync.Map
    26  }
    27  
    28  // New creates a new Manager.
    29  func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) {
    30  	m := &Manager{
    31  		taggedHandler: make(map[string]outbound.Handler),
    32  		tagsCache:     &sync.Map{},
    33  	}
    34  	return m, nil
    35  }
    36  
    37  // Type implements common.HasType.
    38  func (m *Manager) Type() interface{} {
    39  	return outbound.ManagerType()
    40  }
    41  
    42  // Start implements core.Feature
    43  func (m *Manager) Start() error {
    44  	m.access.Lock()
    45  	defer m.access.Unlock()
    46  
    47  	m.running = true
    48  
    49  	for _, h := range m.taggedHandler {
    50  		if err := h.Start(); err != nil {
    51  			return err
    52  		}
    53  	}
    54  
    55  	for _, h := range m.untaggedHandlers {
    56  		if err := h.Start(); err != nil {
    57  			return err
    58  		}
    59  	}
    60  
    61  	return nil
    62  }
    63  
    64  // Close implements core.Feature
    65  func (m *Manager) Close() error {
    66  	m.access.Lock()
    67  	defer m.access.Unlock()
    68  
    69  	m.running = false
    70  
    71  	var errs []error
    72  	for _, h := range m.taggedHandler {
    73  		errs = append(errs, h.Close())
    74  	}
    75  
    76  	for _, h := range m.untaggedHandlers {
    77  		errs = append(errs, h.Close())
    78  	}
    79  
    80  	return errors.Combine(errs...)
    81  }
    82  
    83  // GetDefaultHandler implements outbound.Manager.
    84  func (m *Manager) GetDefaultHandler() outbound.Handler {
    85  	m.access.RLock()
    86  	defer m.access.RUnlock()
    87  
    88  	if m.defaultHandler == nil {
    89  		return nil
    90  	}
    91  	return m.defaultHandler
    92  }
    93  
    94  // GetHandler implements outbound.Manager.
    95  func (m *Manager) GetHandler(tag string) outbound.Handler {
    96  	m.access.RLock()
    97  	defer m.access.RUnlock()
    98  	if handler, found := m.taggedHandler[tag]; found {
    99  		return handler
   100  	}
   101  	return nil
   102  }
   103  
   104  // AddHandler implements outbound.Manager.
   105  func (m *Manager) AddHandler(ctx context.Context, handler outbound.Handler) error {
   106  	m.access.Lock()
   107  	defer m.access.Unlock()
   108  
   109  	m.tagsCache = &sync.Map{}
   110  
   111  	if m.defaultHandler == nil {
   112  		m.defaultHandler = handler
   113  	}
   114  
   115  	tag := handler.Tag()
   116  	if len(tag) > 0 {
   117  		if _, found := m.taggedHandler[tag]; found {
   118  			return newError("existing tag found: " + tag)
   119  		}
   120  		m.taggedHandler[tag] = handler
   121  	} else {
   122  		m.untaggedHandlers = append(m.untaggedHandlers, handler)
   123  	}
   124  
   125  	if m.running {
   126  		return handler.Start()
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  // RemoveHandler implements outbound.Manager.
   133  func (m *Manager) RemoveHandler(ctx context.Context, tag string) error {
   134  	if tag == "" {
   135  		return common.ErrNoClue
   136  	}
   137  	m.access.Lock()
   138  	defer m.access.Unlock()
   139  
   140  	m.tagsCache = &sync.Map{}
   141  
   142  	delete(m.taggedHandler, tag)
   143  	if m.defaultHandler != nil && m.defaultHandler.Tag() == tag {
   144  		m.defaultHandler = nil
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  // Select implements outbound.HandlerSelector.
   151  func (m *Manager) Select(selectors []string) []string {
   152  
   153  	key := strings.Join(selectors, ",")
   154  	if cache, ok := m.tagsCache.Load(key); ok {
   155  		return cache.([]string)
   156  	}
   157  
   158  	m.access.RLock()
   159  	defer m.access.RUnlock()
   160  
   161  	tags := make([]string, 0, len(selectors))
   162  
   163  	for tag := range m.taggedHandler {
   164  		for _, selector := range selectors {
   165  			if strings.HasPrefix(tag, selector) {
   166  				tags = append(tags, tag)
   167  				break
   168  			}
   169  		}
   170  	}
   171  
   172  	sort.Strings(tags)
   173  	m.tagsCache.Store(key, tags)
   174  
   175  	return tags
   176  }
   177  
   178  func init() {
   179  	common.Must(common.RegisterConfig((*proxyman.OutboundConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   180  		return New(ctx, config.(*proxyman.OutboundConfig))
   181  	}))
   182  	common.Must(common.RegisterConfig((*core.OutboundHandlerConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   183  		return NewHandler(ctx, config.(*core.OutboundHandlerConfig))
   184  	}))
   185  }