Skip to content

Commit 370a7c9

Browse files
committed
refactor: Modular inbound/outbound manager
1 parent 1504895 commit 370a7c9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1169
-737
lines changed

adapter/experimental.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515

1616
type ClashServer interface {
1717
Service
18-
PreStarter
18+
LegacyPreStarter
1919
Mode() string
2020
ModeList() []string
2121
HistoryStorage() *urltest.HistoryStorage
@@ -25,7 +25,7 @@ type ClashServer interface {
2525

2626
type CacheFile interface {
2727
Service
28-
PreStarter
28+
LegacyPreStarter
2929

3030
StoreFakeIP() bool
3131
FakeIPStorage

adapter/inbound.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,15 @@ type UDPInjectableInbound interface {
2828

2929
type InboundRegistry interface {
3030
option.InboundOptionsRegistry
31-
CreateInbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Inbound, error)
31+
Create(ctx context.Context, router Router, logger log.ContextLogger, tag string, inboundType string, options any) (Inbound, error)
32+
}
33+
34+
type InboundManager interface {
35+
NewService
36+
Inbounds() []Inbound
37+
Get(tag string) (Inbound, bool)
38+
Remove(tag string) error
39+
Create(ctx context.Context, router Router, logger log.ContextLogger, tag string, inboundType string, options any) error
3240
}
3341

3442
type InboundContext struct {

adapter/inbound/manager.go

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package inbound
2+
3+
import (
4+
"context"
5+
"os"
6+
"sync"
7+
8+
"github.com/sagernet/sing-box/adapter"
9+
"github.com/sagernet/sing-box/common/taskmonitor"
10+
C "github.com/sagernet/sing-box/constant"
11+
"github.com/sagernet/sing-box/log"
12+
"github.com/sagernet/sing/common"
13+
E "github.com/sagernet/sing/common/exceptions"
14+
)
15+
16+
var _ adapter.InboundManager = (*Manager)(nil)
17+
18+
type Manager struct {
19+
logger log.ContextLogger
20+
registry adapter.InboundRegistry
21+
access sync.Mutex
22+
started bool
23+
stage adapter.StartStage
24+
inbounds []adapter.Inbound
25+
inboundByTag map[string]adapter.Inbound
26+
}
27+
28+
func NewManager(logger log.ContextLogger, registry adapter.InboundRegistry) *Manager {
29+
return &Manager{
30+
logger: logger,
31+
registry: registry,
32+
inboundByTag: make(map[string]adapter.Inbound),
33+
}
34+
}
35+
36+
func (m *Manager) Start(stage adapter.StartStage) error {
37+
m.access.Lock()
38+
defer m.access.Unlock()
39+
if m.started && m.stage >= stage {
40+
panic("already started")
41+
}
42+
m.started = true
43+
m.stage = stage
44+
for _, inbound := range m.inbounds {
45+
err := adapter.LegacyStart(inbound, stage)
46+
if err != nil {
47+
return E.Cause(err, stage.Action(), " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
48+
}
49+
}
50+
return nil
51+
}
52+
53+
func (m *Manager) Close() error {
54+
m.access.Lock()
55+
if !m.started {
56+
panic("not started")
57+
}
58+
m.started = false
59+
inbounds := m.inbounds
60+
m.inbounds = nil
61+
m.access.Unlock()
62+
monitor := taskmonitor.New(m.logger, C.StopTimeout)
63+
var err error
64+
for _, inbound := range inbounds {
65+
monitor.Start("close inbound/", inbound.Type(), "[", inbound.Tag(), "]")
66+
err = E.Append(err, inbound.Close(), func(err error) error {
67+
return E.Cause(err, "close inbound/", inbound.Type(), "[", inbound.Tag(), "]")
68+
})
69+
monitor.Finish()
70+
}
71+
return nil
72+
}
73+
74+
func (m *Manager) Inbounds() []adapter.Inbound {
75+
m.access.Lock()
76+
defer m.access.Unlock()
77+
return m.inbounds
78+
}
79+
80+
func (m *Manager) Get(tag string) (adapter.Inbound, bool) {
81+
m.access.Lock()
82+
defer m.access.Unlock()
83+
inbound, found := m.inboundByTag[tag]
84+
return inbound, found
85+
}
86+
87+
func (m *Manager) Remove(tag string) error {
88+
m.access.Lock()
89+
inbound, found := m.inboundByTag[tag]
90+
if !found {
91+
m.access.Unlock()
92+
return os.ErrInvalid
93+
}
94+
delete(m.inboundByTag, tag)
95+
index := common.Index(m.inbounds, func(it adapter.Inbound) bool {
96+
return it == inbound
97+
})
98+
if index == -1 {
99+
panic("invalid inbound index")
100+
}
101+
m.inbounds = append(m.inbounds[:index], m.inbounds[index+1:]...)
102+
started := m.started
103+
m.access.Unlock()
104+
if started {
105+
return inbound.Close()
106+
}
107+
return nil
108+
}
109+
110+
func (m *Manager) Create(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) error {
111+
inbound, err := m.registry.Create(ctx, router, logger, tag, outboundType, options)
112+
if err != nil {
113+
return err
114+
}
115+
m.access.Lock()
116+
defer m.access.Unlock()
117+
if m.started {
118+
for _, stage := range adapter.ListStartStages {
119+
err = adapter.LegacyStart(inbound, stage)
120+
if err != nil {
121+
return E.Cause(err, stage.Action(), " inbound/", inbound.Type(), "[", inbound.Tag(), "]")
122+
}
123+
}
124+
}
125+
if existsInbound, loaded := m.inboundByTag[tag]; loaded {
126+
if m.started {
127+
err = existsInbound.Close()
128+
if err != nil {
129+
return E.Cause(err, "close inbound/", existsInbound.Type(), "[", existsInbound.Tag(), "]")
130+
}
131+
}
132+
existsIndex := common.Index(m.inbounds, func(it adapter.Inbound) bool {
133+
return it == existsInbound
134+
})
135+
if existsIndex == -1 {
136+
panic("invalid inbound index")
137+
}
138+
m.inbounds = append(m.inbounds[:existsIndex], m.inbounds[existsIndex+1:]...)
139+
}
140+
m.inbounds = append(m.inbounds, inbound)
141+
m.inboundByTag[tag] = inbound
142+
return nil
143+
}

adapter/inbound/registry.go

+24-20
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ type ConstructorFunc[T any] func(ctx context.Context, router adapter.Router, log
1515
func Register[Options any](registry *Registry, outboundType string, constructor ConstructorFunc[Options]) {
1616
registry.register(outboundType, func() any {
1717
return new(Options)
18-
}, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options any) (adapter.Inbound, error) {
19-
return constructor(ctx, router, logger, tag, common.PtrValueOrDefault(options.(*Options)))
18+
}, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, rawOptions any) (adapter.Inbound, error) {
19+
var options *Options
20+
if rawOptions != nil {
21+
options = rawOptions.(*Options)
22+
}
23+
return constructor(ctx, router, logger, tag, common.PtrValueOrDefault(options))
2024
})
2125
}
2226

@@ -28,41 +32,41 @@ type (
2832
)
2933

3034
type Registry struct {
31-
access sync.Mutex
32-
optionsType map[string]optionsConstructorFunc
33-
constructors map[string]constructorFunc
35+
access sync.Mutex
36+
optionsType map[string]optionsConstructorFunc
37+
constructor map[string]constructorFunc
3438
}
3539

3640
func NewRegistry() *Registry {
3741
return &Registry{
38-
optionsType: make(map[string]optionsConstructorFunc),
39-
constructors: make(map[string]constructorFunc),
42+
optionsType: make(map[string]optionsConstructorFunc),
43+
constructor: make(map[string]constructorFunc),
4044
}
4145
}
4246

43-
func (r *Registry) CreateOptions(outboundType string) (any, bool) {
44-
r.access.Lock()
45-
defer r.access.Unlock()
46-
optionsConstructor, loaded := r.optionsType[outboundType]
47+
func (m *Registry) CreateOptions(outboundType string) (any, bool) {
48+
m.access.Lock()
49+
defer m.access.Unlock()
50+
optionsConstructor, loaded := m.optionsType[outboundType]
4751
if !loaded {
4852
return nil, false
4953
}
5054
return optionsConstructor(), true
5155
}
5256

53-
func (r *Registry) CreateInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Inbound, error) {
54-
r.access.Lock()
55-
defer r.access.Unlock()
56-
constructor, loaded := r.constructors[outboundType]
57+
func (m *Registry) Create(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, outboundType string, options any) (adapter.Inbound, error) {
58+
m.access.Lock()
59+
defer m.access.Unlock()
60+
constructor, loaded := m.constructor[outboundType]
5761
if !loaded {
5862
return nil, E.New("outbound type not found: " + outboundType)
5963
}
6064
return constructor(ctx, router, logger, tag, options)
6165
}
6266

63-
func (r *Registry) register(outboundType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) {
64-
r.access.Lock()
65-
defer r.access.Unlock()
66-
r.optionsType[outboundType] = optionsConstructor
67-
r.constructors[outboundType] = constructor
67+
func (m *Registry) register(outboundType string, optionsConstructor optionsConstructorFunc, constructor constructorFunc) {
68+
m.access.Lock()
69+
defer m.access.Unlock()
70+
m.optionsType[outboundType] = optionsConstructor
71+
m.constructor[outboundType] = constructor
6872
}

adapter/lifecycle.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package adapter
2+
3+
type StartStage uint8
4+
5+
const (
6+
StartStateInitialize StartStage = iota
7+
StartStateStart
8+
StartStatePostStart
9+
StartStateStarted
10+
)
11+
12+
var ListStartStages = []StartStage{
13+
StartStateInitialize,
14+
StartStateStart,
15+
StartStatePostStart,
16+
StartStateStarted,
17+
}
18+
19+
func (s StartStage) Action() string {
20+
switch s {
21+
case StartStateInitialize:
22+
return "initialize"
23+
case StartStateStart:
24+
return "start"
25+
case StartStatePostStart:
26+
return "post-start"
27+
case StartStateStarted:
28+
return "start-after-started"
29+
default:
30+
panic("unknown stage")
31+
}
32+
}
33+
34+
type NewService interface {
35+
NewStarter
36+
Close() error
37+
}
38+
39+
type NewStarter interface {
40+
Start(stage StartStage) error
41+
}

adapter/lifecycle_legacy.go

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package adapter
2+
3+
type LegacyPreStarter interface {
4+
PreStart() error
5+
}
6+
7+
type LegacyPostStarter interface {
8+
PostStart() error
9+
}
10+
11+
func LegacyStart(starter any, stage StartStage) error {
12+
switch stage {
13+
case StartStateInitialize:
14+
if preStarter, isPreStarter := starter.(interface {
15+
PreStart() error
16+
}); isPreStarter {
17+
return preStarter.PreStart()
18+
}
19+
case StartStateStart:
20+
if starter, isStarter := starter.(interface {
21+
Start() error
22+
}); isStarter {
23+
return starter.Start()
24+
}
25+
case StartStatePostStart:
26+
if postStarter, isPostStarter := starter.(interface {
27+
PostStart() error
28+
}); isPostStarter {
29+
return postStarter.PostStart()
30+
}
31+
}
32+
return nil
33+
}

adapter/outbound.go

+9
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,12 @@ type OutboundRegistry interface {
2222
option.OutboundOptionsRegistry
2323
CreateOutbound(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) (Outbound, error)
2424
}
25+
26+
type OutboundManager interface {
27+
NewService
28+
Outbounds() []Outbound
29+
Outbound(tag string) (Outbound, bool)
30+
Default() Outbound
31+
Remove(tag string) error
32+
Create(ctx context.Context, router Router, logger log.ContextLogger, tag string, outboundType string, options any) error
33+
}

0 commit comments

Comments
 (0)