mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-05-14 00:51:12 +03:00
refactor: Modular inbound/outbound manager
This commit is contained in:
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
type ClashServer interface {
|
||||
Service
|
||||
PreStarter
|
||||
LegacyPreStarter
|
||||
Mode() string
|
||||
ModeList() []string
|
||||
HistoryStorage() *urltest.HistoryStorage
|
||||
@@ -25,7 +25,7 @@ type ClashServer interface {
|
||||
|
||||
type CacheFile interface {
|
||||
Service
|
||||
PreStarter
|
||||
LegacyPreStarter
|
||||
|
||||
StoreFakeIP() bool
|
||||
FakeIPStorage
|
||||
|
||||
@@ -28,7 +28,15 @@ type UDPInjectableInbound interface {
|
||||
|
||||
type InboundRegistry interface {
|
||||
option.InboundOptionsRegistry
|
||||
CreateInbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Inbound, error)
|
||||
Create(ctx context.Context, router Router, logger log.ContextLogger, tag string, inboundType string, options any) (Inbound, error)
|
||||
}
|
||||
|
||||
type InboundManager interface {
|
||||
NewService
|
||||
Inbounds() []Inbound
|
||||
Get(tag string) (Inbound, bool)
|
||||
Remove(tag string) error
|
||||
Create(ctx context.Context, router Router, logger log.ContextLogger, tag string, inboundType string, options any) error
|
||||
}
|
||||
|
||||
type InboundContext struct {
|
||||
|
||||
143
adapter/inbound/manager.go
Normal file
143
adapter/inbound/manager.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package inbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
)
|
||||
|
||||
var _ adapter.InboundManager = (*Manager)(nil)
|
||||
|
||||
type Manager struct {
|
||||
logger log.ContextLogger
|
||||
registry adapter.InboundRegistry
|
||||
access sync.Mutex
|
||||
started bool
|
||||
stage adapter.StartStage
|
||||
inbounds []adapter.Inbound
|
||||
inboundByTag map[string]adapter.Inbound
|
||||
}
|
||||
|
||||
func NewManager(logger log.ContextLogger, registry adapter.InboundRegistry) *Manager {
|
||||
return &Manager{
|
||||
logger: logger,
|
||||
registry: registry,
|
||||
inboundByTag: make(map[string]adapter.Inbound),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started && m.stage >= stage {
|
||||
panic("already started")
|
||||
}
|
||||
m.started = true
|
||||
m.stage = stage
|
||||
for _, inbound := range m.inbounds {
|
||||
err := adapter.LegacyStart(inbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.Action(), " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Close() error {
|
||||
m.access.Lock()
|
||||
if !m.started {
|
||||
panic("not started")
|
||||
}
|
||||
m.started = false
|
||||
inbounds := m.inbounds
|
||||
m.inbounds = nil
|
||||
m.access.Unlock()
|
||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||
var err error
|
||||
for _, inbound := range inbounds {
|
||||
monitor.Start("close inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
err = E.Append(err, inbound.Close(), func(err error) error {
|
||||
return E.Cause(err, "close inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Inbounds() []adapter.Inbound {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
return m.inbounds
|
||||
}
|
||||
|
||||
func (m *Manager) Get(tag string) (adapter.Inbound, bool) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
inbound, found := m.inboundByTag[tag]
|
||||
return inbound, found
|
||||
}
|
||||
|
||||
func (m *Manager) Remove(tag string) error {
|
||||
m.access.Lock()
|
||||
inbound, found := m.inboundByTag[tag]
|
||||
if !found {
|
||||
m.access.Unlock()
|
||||
return os.ErrInvalid
|
||||
}
|
||||
delete(m.inboundByTag, tag)
|
||||
index := common.Index(m.inbounds, func(it adapter.Inbound) bool {
|
||||
return it == inbound
|
||||
})
|
||||
if index == -1 {
|
||||
panic("invalid inbound index")
|
||||
}
|
||||
m.inbounds = append(m.inbounds[:index], m.inbounds[index+1:]...)
|
||||
started := m.started
|
||||
m.access.Unlock()
|
||||
if started {
|
||||
return inbound.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) error {
|
||||
inbound, err := m.registry.Create(ctx, router, logger, tag, outboundType, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started {
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
err = adapter.LegacyStart(inbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.Action(), " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
if existsInbound, loaded := m.inboundByTag[tag]; loaded {
|
||||
if m.started {
|
||||
err = existsInbound.Close()
|
||||
if err != nil {
|
||||
return E.Cause(err, "close inbound/", existsInbound.Type(), "[", existsInbound.Tag(), "]")
|
||||
}
|
||||
}
|
||||
existsIndex := common.Index(m.inbounds, func(it adapter.Inbound) bool {
|
||||
return it == existsInbound
|
||||
})
|
||||
if existsIndex == -1 {
|
||||
panic("invalid inbound index")
|
||||
}
|
||||
m.inbounds = append(m.inbounds[:existsIndex], m.inbounds[existsIndex+1:]...)
|
||||
}
|
||||
m.inbounds = append(m.inbounds, inbound)
|
||||
m.inboundByTag[tag] = inbound
|
||||
return nil
|
||||
}
|
||||
@@ -15,8 +15,12 @@ type ConstructorFunc[T any] func(ctx context.Context, router adapter.Router, log
|
||||
func Register[Options any](registry *Registry, outboundType string, constructor ConstructorFunc[Options]) {
|
||||
registry.register(outboundType, func() any {
|
||||
return new(Options)
|
||||
}, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options any) (adapter.Inbound, error) {
|
||||
return constructor(ctx, router, logger, tag, common.PtrValueOrDefault(options.(*Options)))
|
||||
}, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, rawOptions any) (adapter.Inbound, error) {
|
||||
var options *Options
|
||||
if rawOptions != nil {
|
||||
options = rawOptions.(*Options)
|
||||
}
|
||||
return constructor(ctx, router, logger, tag, common.PtrValueOrDefault(options))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,41 +32,41 @@ type (
|
||||
)
|
||||
|
||||
type Registry struct {
|
||||
access sync.Mutex
|
||||
optionsType map[string]optionsConstructorFunc
|
||||
constructors map[string]constructorFunc
|
||||
access sync.Mutex
|
||||
optionsType map[string]optionsConstructorFunc
|
||||
constructor map[string]constructorFunc
|
||||
}
|
||||
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
optionsType: make(map[string]optionsConstructorFunc),
|
||||
constructors: make(map[string]constructorFunc),
|
||||
optionsType: make(map[string]optionsConstructorFunc),
|
||||
constructor: make(map[string]constructorFunc),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Registry) CreateOptions(outboundType string) (any, bool) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
optionsConstructor, loaded := r.optionsType[outboundType]
|
||||
func (m *Registry) CreateOptions(outboundType string) (any, bool) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
optionsConstructor, loaded := m.optionsType[outboundType]
|
||||
if !loaded {
|
||||
return nil, false
|
||||
}
|
||||
return optionsConstructor(), true
|
||||
}
|
||||
|
||||
func (r *Registry) CreateInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Inbound, error) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
constructor, loaded := r.constructors[outboundType]
|
||||
func (m *Registry) Create(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Inbound, error) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
constructor, loaded := m.constructor[outboundType]
|
||||
if !loaded {
|
||||
return nil, E.New("outbound type not found: " + outboundType)
|
||||
}
|
||||
return constructor(ctx, router, logger, tag, options)
|
||||
}
|
||||
|
||||
func (r *Registry) register(outboundType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) {
|
||||
r.access.Lock()
|
||||
defer r.access.Unlock()
|
||||
r.optionsType[outboundType] = optionsConstructor
|
||||
r.constructors[outboundType] = constructor
|
||||
func (m *Registry) register(outboundType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
m.optionsType[outboundType] = optionsConstructor
|
||||
m.constructor[outboundType] = constructor
|
||||
}
|
||||
|
||||
41
adapter/lifecycle.go
Normal file
41
adapter/lifecycle.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package adapter
|
||||
|
||||
type StartStage uint8
|
||||
|
||||
const (
|
||||
StartStateInitialize StartStage = iota
|
||||
StartStateStart
|
||||
StartStatePostStart
|
||||
StartStateStarted
|
||||
)
|
||||
|
||||
var ListStartStages = []StartStage{
|
||||
StartStateInitialize,
|
||||
StartStateStart,
|
||||
StartStatePostStart,
|
||||
StartStateStarted,
|
||||
}
|
||||
|
||||
func (s StartStage) Action() string {
|
||||
switch s {
|
||||
case StartStateInitialize:
|
||||
return "initialize"
|
||||
case StartStateStart:
|
||||
return "start"
|
||||
case StartStatePostStart:
|
||||
return "post-start"
|
||||
case StartStateStarted:
|
||||
return "start-after-started"
|
||||
default:
|
||||
panic("unknown stage")
|
||||
}
|
||||
}
|
||||
|
||||
type NewService interface {
|
||||
NewStarter
|
||||
Close() error
|
||||
}
|
||||
|
||||
type NewStarter interface {
|
||||
Start(stage StartStage) error
|
||||
}
|
||||
33
adapter/lifecycle_legacy.go
Normal file
33
adapter/lifecycle_legacy.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package adapter
|
||||
|
||||
type LegacyPreStarter interface {
|
||||
PreStart() error
|
||||
}
|
||||
|
||||
type LegacyPostStarter interface {
|
||||
PostStart() error
|
||||
}
|
||||
|
||||
func LegacyStart(starter any, stage StartStage) error {
|
||||
switch stage {
|
||||
case StartStateInitialize:
|
||||
if preStarter, isPreStarter := starter.(interface {
|
||||
PreStart() error
|
||||
}); isPreStarter {
|
||||
return preStarter.PreStart()
|
||||
}
|
||||
case StartStateStart:
|
||||
if starter, isStarter := starter.(interface {
|
||||
Start() error
|
||||
}); isStarter {
|
||||
return starter.Start()
|
||||
}
|
||||
case StartStatePostStart:
|
||||
if postStarter, isPostStarter := starter.(interface {
|
||||
PostStart() error
|
||||
}); isPostStarter {
|
||||
return postStarter.PostStart()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -22,3 +22,12 @@ type OutboundRegistry interface {
|
||||
option.OutboundOptionsRegistry
|
||||
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
|
||||
}
|
||||
|
||||
type OutboundManager interface {
|
||||
NewService
|
||||
Outbounds() []Outbound
|
||||
Outbound(tag string) (Outbound, bool)
|
||||
Default() Outbound
|
||||
Remove(tag string) error
|
||||
Create(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) error
|
||||
}
|
||||
|
||||
265
adapter/outbound/manager.go
Normal file
265
adapter/outbound/manager.go
Normal file
@@ -0,0 +1,265 @@
|
||||
package outbound
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
var _ adapter.OutboundManager = (*Manager)(nil)
|
||||
|
||||
type Manager struct {
|
||||
logger log.ContextLogger
|
||||
registry adapter.OutboundRegistry
|
||||
defaultTag string
|
||||
access sync.Mutex
|
||||
started bool
|
||||
stage adapter.StartStage
|
||||
outbounds []adapter.Outbound
|
||||
outboundByTag map[string]adapter.Outbound
|
||||
dependByTag map[string][]string
|
||||
defaultOutbound adapter.Outbound
|
||||
defaultOutboundFallback adapter.Outbound
|
||||
}
|
||||
|
||||
func NewManager(logger logger.ContextLogger, registry adapter.OutboundRegistry, defaultTag string) *Manager {
|
||||
return &Manager{
|
||||
logger: logger,
|
||||
registry: registry,
|
||||
defaultTag: defaultTag,
|
||||
outboundByTag: make(map[string]adapter.Outbound),
|
||||
dependByTag: make(map[string][]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Initialize(defaultOutboundFallback adapter.Outbound) {
|
||||
m.defaultOutboundFallback = defaultOutboundFallback
|
||||
}
|
||||
|
||||
func (m *Manager) Start(stage adapter.StartStage) error {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started && m.stage >= stage {
|
||||
panic("already started")
|
||||
}
|
||||
m.started = true
|
||||
m.stage = stage
|
||||
if stage == adapter.StartStateStart {
|
||||
m.startOutbounds()
|
||||
} else {
|
||||
for _, outbound := range m.outbounds {
|
||||
err := adapter.LegacyStart(outbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.Action(), " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) startOutbounds() error {
|
||||
monitor := taskmonitor.New(m.logger, C.StartTimeout)
|
||||
started := make(map[string]bool)
|
||||
for {
|
||||
canContinue := false
|
||||
startOne:
|
||||
for _, outboundToStart := range m.outbounds {
|
||||
outboundTag := outboundToStart.Tag()
|
||||
if started[outboundTag] {
|
||||
continue
|
||||
}
|
||||
dependencies := outboundToStart.Dependencies()
|
||||
for _, dependency := range dependencies {
|
||||
if !started[dependency] {
|
||||
continue startOne
|
||||
}
|
||||
}
|
||||
started[outboundTag] = true
|
||||
canContinue = true
|
||||
if starter, isStarter := outboundToStart.(interface {
|
||||
Start() error
|
||||
}); isStarter {
|
||||
monitor.Start("start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
err := starter.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "start outbound/", outboundToStart.Type(), "[", outboundTag, "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(started) == len(m.outbounds) {
|
||||
break
|
||||
}
|
||||
if canContinue {
|
||||
continue
|
||||
}
|
||||
currentOutbound := common.Find(m.outbounds, func(it adapter.Outbound) bool {
|
||||
return !started[it.Tag()]
|
||||
})
|
||||
var lintOutbound func(oTree []string, oCurrent adapter.Outbound) error
|
||||
lintOutbound = func(oTree []string, oCurrent adapter.Outbound) error {
|
||||
problemOutboundTag := common.Find(oCurrent.Dependencies(), func(it string) bool {
|
||||
return !started[it]
|
||||
})
|
||||
if common.Contains(oTree, problemOutboundTag) {
|
||||
return E.New("circular outbound dependency: ", strings.Join(oTree, " -> "), " -> ", problemOutboundTag)
|
||||
}
|
||||
problemOutbound := m.outboundByTag[problemOutboundTag]
|
||||
if problemOutbound == nil {
|
||||
return E.New("dependency[", problemOutboundTag, "] not found for outbound[", oCurrent.Tag(), "]")
|
||||
}
|
||||
return lintOutbound(append(oTree, problemOutboundTag), problemOutbound)
|
||||
}
|
||||
return lintOutbound([]string{currentOutbound.Tag()}, currentOutbound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Close() error {
|
||||
monitor := taskmonitor.New(m.logger, C.StopTimeout)
|
||||
m.access.Lock()
|
||||
if !m.started {
|
||||
panic("not started")
|
||||
}
|
||||
m.started = false
|
||||
outbounds := m.outbounds
|
||||
m.outbounds = nil
|
||||
m.access.Unlock()
|
||||
var err error
|
||||
for _, outbound := range outbounds {
|
||||
if closer, isCloser := outbound.(io.Closer); isCloser {
|
||||
monitor.Start("close outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
err = E.Append(err, closer.Close(), func(err error) error {
|
||||
return E.Cause(err, "close outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Outbounds() []adapter.Outbound {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
return m.outbounds
|
||||
}
|
||||
|
||||
func (m *Manager) Outbound(tag string) (adapter.Outbound, bool) {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
outbound, found := m.outboundByTag[tag]
|
||||
return outbound, found
|
||||
}
|
||||
|
||||
func (m *Manager) Default() adapter.Outbound {
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.defaultOutbound != nil {
|
||||
return m.defaultOutbound
|
||||
} else {
|
||||
return m.defaultOutboundFallback
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Remove(tag string) error {
|
||||
m.access.Lock()
|
||||
outbound, found := m.outboundByTag[tag]
|
||||
if !found {
|
||||
m.access.Unlock()
|
||||
return os.ErrInvalid
|
||||
}
|
||||
delete(m.outboundByTag, tag)
|
||||
index := common.Index(m.outbounds, func(it adapter.Outbound) bool {
|
||||
return it == outbound
|
||||
})
|
||||
if index == -1 {
|
||||
panic("invalid inbound index")
|
||||
}
|
||||
m.outbounds = append(m.outbounds[:index], m.outbounds[index+1:]...)
|
||||
started := m.started
|
||||
if m.defaultOutbound == outbound {
|
||||
if len(m.outbounds) > 0 {
|
||||
m.defaultOutbound = m.outbounds[0]
|
||||
m.logger.Info("updated default outbound to ", m.defaultOutbound.Tag())
|
||||
} else {
|
||||
m.defaultOutbound = nil
|
||||
}
|
||||
}
|
||||
dependBy := m.dependByTag[tag]
|
||||
if len(dependBy) > 0 {
|
||||
return E.New("outbound[", tag, "] is depended by ", strings.Join(dependBy, ", "))
|
||||
}
|
||||
dependencies := outbound.Dependencies()
|
||||
for _, dependency := range dependencies {
|
||||
if len(m.dependByTag[dependency]) == 1 {
|
||||
delete(m.dependByTag, dependency)
|
||||
} else {
|
||||
m.dependByTag[dependency] = common.Filter(m.dependByTag[dependency], func(it string) bool {
|
||||
return it != tag
|
||||
})
|
||||
}
|
||||
}
|
||||
m.access.Unlock()
|
||||
if started {
|
||||
return common.Close(outbound)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, inboundType string, options any) error {
|
||||
if tag == "" {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
outbound, err := m.registry.CreateOutbound(ctx, router, logger, tag, inboundType, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.access.Lock()
|
||||
defer m.access.Unlock()
|
||||
if m.started {
|
||||
for _, stage := range adapter.ListStartStages {
|
||||
err = adapter.LegacyStart(outbound, stage)
|
||||
if err != nil {
|
||||
return E.Cause(err, stage.Action(), " outbound/", outbound.Type(), "[", outbound.Tag(), "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
if existsOutbound, loaded := m.outboundByTag[tag]; loaded {
|
||||
if m.started {
|
||||
err = common.Close(existsOutbound)
|
||||
if err != nil {
|
||||
return E.Cause(err, "close outbound/", existsOutbound.Type(), "[", existsOutbound.Tag(), "]")
|
||||
}
|
||||
}
|
||||
existsIndex := common.Index(m.outbounds, func(it adapter.Outbound) bool {
|
||||
return it == existsOutbound
|
||||
})
|
||||
if existsIndex == -1 {
|
||||
panic("invalid inbound index")
|
||||
}
|
||||
m.outbounds = append(m.outbounds[:existsIndex], m.outbounds[existsIndex+1:]...)
|
||||
}
|
||||
m.outbounds = append(m.outbounds, outbound)
|
||||
m.outboundByTag[tag] = outbound
|
||||
dependencies := outbound.Dependencies()
|
||||
for _, dependency := range dependencies {
|
||||
m.dependByTag[dependency] = append(m.dependByTag[dependency], tag)
|
||||
}
|
||||
if tag == m.defaultTag || (m.defaultTag == "" && m.defaultOutbound == nil) {
|
||||
m.defaultOutbound = outbound
|
||||
if m.started {
|
||||
m.logger.Info("updated default outbound to ", outbound.Tag())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -15,8 +15,12 @@ type ConstructorFunc[T any] func(ctx context.Context, router adapter.Router, log
|
||||
func Register[Options any](registry *Registry, outboundType string, constructor ConstructorFunc[Options]) {
|
||||
registry.register(outboundType, func() any {
|
||||
return new(Options)
|
||||
}, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options any) (adapter.Outbound, error) {
|
||||
return constructor(ctx, router, logger, tag, common.PtrValueOrDefault(options.(*Options)))
|
||||
}, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, rawOptions any) (adapter.Outbound, error) {
|
||||
var options *Options
|
||||
if rawOptions != nil {
|
||||
options = rawOptions.(*Options)
|
||||
}
|
||||
return constructor(ctx, router, logger, tag, common.PtrValueOrDefault(options))
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1 @@
|
||||
package adapter
|
||||
|
||||
type PreStarter interface {
|
||||
PreStart() error
|
||||
}
|
||||
|
||||
type PostStarter interface {
|
||||
PostStart() error
|
||||
}
|
||||
|
||||
@@ -15,21 +15,13 @@ import (
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
"github.com/sagernet/sing/service"
|
||||
|
||||
mdns "github.com/miekg/dns"
|
||||
"go4.org/netipx"
|
||||
)
|
||||
|
||||
type Router interface {
|
||||
Service
|
||||
PreStarter
|
||||
PostStarter
|
||||
Cleanup() error
|
||||
|
||||
Outbounds() []Outbound
|
||||
Outbound(tag string) (Outbound, bool)
|
||||
DefaultOutbound(network string) (Outbound, error)
|
||||
NewService
|
||||
|
||||
FakeIPStore() FakeIPStore
|
||||
|
||||
@@ -84,14 +76,6 @@ type ConnectionRouterEx interface {
|
||||
RoutePacketConnectionEx(ctx context.Context, conn N.PacketConn, metadata InboundContext, onClose N.CloseHandlerFunc)
|
||||
}
|
||||
|
||||
func ContextWithRouter(ctx context.Context, router Router) context.Context {
|
||||
return service.ContextWith(ctx, router)
|
||||
}
|
||||
|
||||
func RouterFromContext(ctx context.Context) Router {
|
||||
return service.FromContext[Router](ctx)
|
||||
}
|
||||
|
||||
type RuleSet interface {
|
||||
Name() string
|
||||
StartContext(ctx context.Context, startContext *HTTPStartContext) error
|
||||
|
||||
Reference in New Issue
Block a user