From 64605fc47af71de42fdb295080b9da7d424d5d58 Mon Sep 17 00:00:00 2001 From: Shtorm <108103062+shtorm-7@users.noreply.github.com> Date: Sat, 5 Sep 2026 04:30:03 +0300 Subject: [PATCH] protocol: Forward lifecycle Start stage to nested outbounds in vpn, failover and limiters --- protocol/failover/outbound.go | 22 +++++++++++++++++----- protocol/limiter/bandwidth/outbound.go | 24 +++++++++++++----------- protocol/limiter/connection/outbound.go | 24 +++++++++++++----------- protocol/limiter/rate/outbound.go | 22 +++++++++++----------- protocol/limiter/traffic/outbound.go | 24 +++++++++++++----------- protocol/vpn/client.go | 4 ++++ 6 files changed, 71 insertions(+), 49 deletions(-) diff --git a/protocol/failover/outbound.go b/protocol/failover/outbound.go index d2cc4dc3..a3f46fee 100644 --- a/protocol/failover/outbound.go +++ b/protocol/failover/outbound.go @@ -27,6 +27,7 @@ type Failover struct { outbound.Adapter ctx context.Context outbound adapter.OutboundManager + outbounds []adapter.Outbound logger logger.ContextLogger dial DialStrategy uotClient *uot.Client @@ -50,11 +51,12 @@ func NewFailover(ctx context.Context, router adapter.Router, logger log.ContextL return nil, err } outbound := &Failover{ - Adapter: outbound.NewAdapter(C.TypeFailover, tag, []string{N.NetworkTCP, N.NetworkUDP}, []string{}), - ctx: ctx, - outbound: service.FromContext[adapter.OutboundManager](ctx), - logger: logger, - dial: dial, + Adapter: outbound.NewAdapter(C.TypeFailover, tag, []string{N.NetworkTCP, N.NetworkUDP}, []string{}), + ctx: ctx, + outbound: service.FromContext[adapter.OutboundManager](ctx), + outbounds: outbounds, + logger: logger, + dial: dial, } outbound.uotClient = &uot.Client{ Dialer: outbound, @@ -63,6 +65,16 @@ func NewFailover(ctx context.Context, router adapter.Router, logger log.ContextL return outbound, nil } +func (f *Failover) Start(stage adapter.StartStage) error { + for _, outbound := range f.outbounds { + err := adapter.LegacyStart(outbound, stage) + if err != nil { + return err + } + } + return nil +} + func (f *Failover) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if N.NetworkName(network) == N.NetworkUDP { return f.uotClient.DialContext(ctx, network, destination) diff --git a/protocol/limiter/bandwidth/outbound.go b/protocol/limiter/bandwidth/outbound.go index f29b06e1..d1d03574 100644 --- a/protocol/limiter/bandwidth/outbound.go +++ b/protocol/limiter/bandwidth/outbound.go @@ -24,6 +24,8 @@ func RegisterOutbound(registry *outbound.Registry) { outbound.Register[option.BandwidthLimiterOutboundOptions](registry, C.TypeBandwidthLimiter, NewOutbound) } +var _ adapter.Lifecycle = (*Outbound)(nil) + type Outbound struct { outbound.Adapter ctx context.Context @@ -87,19 +89,19 @@ func (h *Outbound) Network() []string { return []string{N.NetworkTCP, N.NetworkUDP} } -func (h *Outbound) Start() error { - detour, loaded := h.outbound.Outbound(h.outboundTag) - if !loaded { - return E.New("outbound not found: ", h.outboundTag) - } - h.detour = detour - for _, stage := range []adapter.StartStage{adapter.StartStateStart, adapter.StartStatePostStart, adapter.StartStateStarted} { - err := h.router.Start(stage) - if err != nil { - return err +func (h *Outbound) Start(stage adapter.StartStage) error { + if stage == adapter.StartStateStart { + detour, loaded := h.outbound.Outbound(h.outboundTag) + if !loaded { + return E.New("outbound not found: ", h.outboundTag) } + h.detour = detour } - return nil + return h.router.Start(stage) +} + +func (h *Outbound) Close() error { + return h.router.Close() } func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { diff --git a/protocol/limiter/connection/outbound.go b/protocol/limiter/connection/outbound.go index 8cfed936..3d42484a 100644 --- a/protocol/limiter/connection/outbound.go +++ b/protocol/limiter/connection/outbound.go @@ -24,6 +24,8 @@ func RegisterOutbound(registry *outbound.Registry) { outbound.Register[option.ConnectionLimiterOutboundOptions](registry, C.TypeConnectionLimiter, NewOutbound) } +var _ adapter.Lifecycle = (*Outbound)(nil) + type Outbound struct { outbound.Adapter ctx context.Context @@ -87,19 +89,19 @@ func (h *Outbound) Network() []string { return []string{N.NetworkTCP, N.NetworkUDP} } -func (h *Outbound) Start() error { - detour, loaded := h.outbound.Outbound(h.outboundTag) - if !loaded { - return E.New("outbound not found: ", h.outboundTag) - } - h.detour = detour - for _, stage := range []adapter.StartStage{adapter.StartStateStart, adapter.StartStatePostStart, adapter.StartStateStarted} { - err := h.router.Start(stage) - if err != nil { - return err +func (h *Outbound) Start(stage adapter.StartStage) error { + if stage == adapter.StartStateStart { + detour, loaded := h.outbound.Outbound(h.outboundTag) + if !loaded { + return E.New("outbound not found: ", h.outboundTag) } + h.detour = detour } - return nil + return h.router.Start(stage) +} + +func (h *Outbound) Close() error { + return h.router.Close() } func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { diff --git a/protocol/limiter/rate/outbound.go b/protocol/limiter/rate/outbound.go index acb30234..4e60cc07 100644 --- a/protocol/limiter/rate/outbound.go +++ b/protocol/limiter/rate/outbound.go @@ -85,19 +85,19 @@ func (h *Outbound) Network() []string { return []string{N.NetworkTCP, N.NetworkUDP} } -func (h *Outbound) Start() error { - detour, loaded := h.outbound.Outbound(h.outboundTag) - if !loaded { - return E.New("outbound not found: ", h.outboundTag) - } - h.detour = detour - for _, stage := range []adapter.StartStage{adapter.StartStateStart, adapter.StartStatePostStart, adapter.StartStateStarted} { - err := h.router.Start(stage) - if err != nil { - return err +func (h *Outbound) Start(stage adapter.StartStage) error { + if stage == adapter.StartStateStart { + detour, loaded := h.outbound.Outbound(h.outboundTag) + if !loaded { + return E.New("outbound not found: ", h.outboundTag) } + h.detour = detour } - return nil + return h.router.Start(stage) +} + +func (h *Outbound) Close() error { + return h.router.Close() } func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { diff --git a/protocol/limiter/traffic/outbound.go b/protocol/limiter/traffic/outbound.go index eef734c4..098222d2 100644 --- a/protocol/limiter/traffic/outbound.go +++ b/protocol/limiter/traffic/outbound.go @@ -23,6 +23,8 @@ func RegisterOutbound(registry *outbound.Registry) { outbound.Register[option.TrafficLimiterOutboundOptions](registry, C.TypeTrafficLimiter, NewOutbound) } +var _ adapter.Lifecycle = (*Outbound)(nil) + type Outbound struct { outbound.Adapter ctx context.Context @@ -63,19 +65,19 @@ func (h *Outbound) Network() []string { return []string{N.NetworkTCP, N.NetworkUDP} } -func (h *Outbound) Start() error { - detour, loaded := h.outbound.Outbound(h.outboundTag) - if !loaded { - return E.New("outbound not found: ", h.outboundTag) - } - h.detour = detour - for _, stage := range []adapter.StartStage{adapter.StartStateStart, adapter.StartStatePostStart, adapter.StartStateStarted} { - err := h.router.Start(stage) - if err != nil { - return err +func (h *Outbound) Start(stage adapter.StartStage) error { + if stage == adapter.StartStateStart { + detour, loaded := h.outbound.Outbound(h.outboundTag) + if !loaded { + return E.New("outbound not found: ", h.outboundTag) } + h.detour = detour } - return nil + return h.router.Start(stage) +} + +func (h *Outbound) Close() error { + return h.router.Close() } func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { diff --git a/protocol/vpn/client.go b/protocol/vpn/client.go index bee1187e..5282622c 100644 --- a/protocol/vpn/client.go +++ b/protocol/vpn/client.go @@ -78,6 +78,10 @@ func NewClientEndpoint(ctx context.Context, router adapter.Router, logger log.Co } func (c *ClientEndpoint) Start(stage adapter.StartStage) error { + err := adapter.LegacyStart(c.outbound, stage) + if err != nil { + return err + } if stage != adapter.StartStatePostStart { return nil }