22650b3020
The URL test history update hook and the Clash mode update hook were single-slot: the API service's attached service overwrote the hook set by the daemon, so clients stopped receiving group updates. Replace both with multicast hook lists. Also share a single URL test history storage via context: Clash API looked it up under a key nobody registered and fell back to its own empty storage, so dashboards showed no delay once an API service was configured. Selector changes now notify through the shared storage, covering selections made from any API surface.
650 lines
22 KiB
Go
650 lines
22 KiB
Go
package box
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"runtime/debug"
|
|
"time"
|
|
|
|
"github.com/sagernet/sing-box/adapter"
|
|
boxCertificate "github.com/sagernet/sing-box/adapter/certificate"
|
|
"github.com/sagernet/sing-box/adapter/endpoint"
|
|
"github.com/sagernet/sing-box/adapter/inbound"
|
|
"github.com/sagernet/sing-box/adapter/outbound"
|
|
boxService "github.com/sagernet/sing-box/adapter/service"
|
|
"github.com/sagernet/sing-box/common/certificate"
|
|
"github.com/sagernet/sing-box/common/dialer"
|
|
"github.com/sagernet/sing-box/common/httpclient"
|
|
"github.com/sagernet/sing-box/common/taskmonitor"
|
|
"github.com/sagernet/sing-box/common/tls"
|
|
"github.com/sagernet/sing-box/common/trafficcontrol"
|
|
"github.com/sagernet/sing-box/common/urltest"
|
|
C "github.com/sagernet/sing-box/constant"
|
|
"github.com/sagernet/sing-box/dns"
|
|
"github.com/sagernet/sing-box/experimental"
|
|
"github.com/sagernet/sing-box/experimental/cachefile"
|
|
"github.com/sagernet/sing-box/experimental/deprecated"
|
|
"github.com/sagernet/sing-box/log"
|
|
"github.com/sagernet/sing-box/option"
|
|
"github.com/sagernet/sing-box/protocol/direct"
|
|
"github.com/sagernet/sing-box/route"
|
|
"github.com/sagernet/sing/common"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
F "github.com/sagernet/sing/common/format"
|
|
"github.com/sagernet/sing/common/ntp"
|
|
"github.com/sagernet/sing/service"
|
|
"github.com/sagernet/sing/service/pause"
|
|
)
|
|
|
|
var _ adapter.SimpleLifecycle = (*Box)(nil)
|
|
|
|
type Box struct {
|
|
createdAt time.Time
|
|
logFactory log.Factory
|
|
logger log.ContextLogger
|
|
network *route.NetworkManager
|
|
endpoint *endpoint.Manager
|
|
inbound *inbound.Manager
|
|
outbound *outbound.Manager
|
|
service *boxService.Manager
|
|
certificateProvider *boxCertificate.Manager
|
|
dnsTransport *dns.TransportManager
|
|
dnsRouter *dns.Router
|
|
connection *route.ConnectionManager
|
|
router *route.Router
|
|
httpClientService adapter.LifecycleService
|
|
internalService []adapter.LifecycleService
|
|
done chan struct{}
|
|
}
|
|
|
|
type Options struct {
|
|
option.Options
|
|
Context context.Context
|
|
PlatformLogWriter log.PlatformWriter
|
|
}
|
|
|
|
func Context(
|
|
ctx context.Context,
|
|
inboundRegistry adapter.InboundRegistry,
|
|
outboundRegistry adapter.OutboundRegistry,
|
|
endpointRegistry adapter.EndpointRegistry,
|
|
dnsTransportRegistry adapter.DNSTransportRegistry,
|
|
serviceRegistry adapter.ServiceRegistry,
|
|
certificateProviderRegistry adapter.CertificateProviderRegistry,
|
|
) context.Context {
|
|
if service.FromContext[option.InboundOptionsRegistry](ctx) == nil ||
|
|
service.FromContext[adapter.InboundRegistry](ctx) == nil {
|
|
ctx = service.ContextWith[option.InboundOptionsRegistry](ctx, inboundRegistry)
|
|
ctx = service.ContextWith[adapter.InboundRegistry](ctx, inboundRegistry)
|
|
}
|
|
if service.FromContext[option.OutboundOptionsRegistry](ctx) == nil ||
|
|
service.FromContext[adapter.OutboundRegistry](ctx) == nil {
|
|
ctx = service.ContextWith[option.OutboundOptionsRegistry](ctx, outboundRegistry)
|
|
ctx = service.ContextWith[adapter.OutboundRegistry](ctx, outboundRegistry)
|
|
}
|
|
if service.FromContext[option.EndpointOptionsRegistry](ctx) == nil ||
|
|
service.FromContext[adapter.EndpointRegistry](ctx) == nil {
|
|
ctx = service.ContextWith[option.EndpointOptionsRegistry](ctx, endpointRegistry)
|
|
ctx = service.ContextWith[adapter.EndpointRegistry](ctx, endpointRegistry)
|
|
}
|
|
if service.FromContext[adapter.DNSTransportRegistry](ctx) == nil {
|
|
ctx = service.ContextWith[option.DNSTransportOptionsRegistry](ctx, dnsTransportRegistry)
|
|
ctx = service.ContextWith[adapter.DNSTransportRegistry](ctx, dnsTransportRegistry)
|
|
}
|
|
if service.FromContext[adapter.ServiceRegistry](ctx) == nil {
|
|
ctx = service.ContextWith[option.ServiceOptionsRegistry](ctx, serviceRegistry)
|
|
ctx = service.ContextWith[adapter.ServiceRegistry](ctx, serviceRegistry)
|
|
}
|
|
if service.FromContext[adapter.CertificateProviderRegistry](ctx) == nil {
|
|
ctx = service.ContextWith[option.CertificateProviderOptionsRegistry](ctx, certificateProviderRegistry)
|
|
ctx = service.ContextWith[adapter.CertificateProviderRegistry](ctx, certificateProviderRegistry)
|
|
}
|
|
return ctx
|
|
}
|
|
|
|
func New(options Options) (*Box, error) {
|
|
createdAt := time.Now()
|
|
ctx := options.Context
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
ctx = service.ContextWithDefaultRegistry(ctx)
|
|
|
|
endpointRegistry := service.FromContext[adapter.EndpointRegistry](ctx)
|
|
inboundRegistry := service.FromContext[adapter.InboundRegistry](ctx)
|
|
outboundRegistry := service.FromContext[adapter.OutboundRegistry](ctx)
|
|
dnsTransportRegistry := service.FromContext[adapter.DNSTransportRegistry](ctx)
|
|
serviceRegistry := service.FromContext[adapter.ServiceRegistry](ctx)
|
|
certificateProviderRegistry := service.FromContext[adapter.CertificateProviderRegistry](ctx)
|
|
|
|
if endpointRegistry == nil {
|
|
return nil, E.New("missing endpoint registry in context")
|
|
}
|
|
if inboundRegistry == nil {
|
|
return nil, E.New("missing inbound registry in context")
|
|
}
|
|
if outboundRegistry == nil {
|
|
return nil, E.New("missing outbound registry in context")
|
|
}
|
|
if dnsTransportRegistry == nil {
|
|
return nil, E.New("missing DNS transport registry in context")
|
|
}
|
|
if serviceRegistry == nil {
|
|
return nil, E.New("missing service registry in context")
|
|
}
|
|
if certificateProviderRegistry == nil {
|
|
return nil, E.New("missing certificate provider registry in context")
|
|
}
|
|
|
|
ctx = pause.WithDefaultManager(ctx)
|
|
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
|
|
err := applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var needCacheFile bool
|
|
var needClashAPI bool
|
|
var needV2RayAPI bool
|
|
if experimentalOptions.CacheFile != nil && experimentalOptions.CacheFile.Enabled || options.PlatformLogWriter != nil {
|
|
needCacheFile = true
|
|
}
|
|
if experimentalOptions.ClashAPI != nil || options.PlatformLogWriter != nil {
|
|
needClashAPI = true
|
|
}
|
|
if experimentalOptions.V2RayAPI != nil && experimentalOptions.V2RayAPI.Listen != "" {
|
|
needV2RayAPI = true
|
|
}
|
|
needAPIService := common.Any(options.Services, func(it option.Service) bool {
|
|
return it.Type == C.TypeAPI
|
|
})
|
|
if service.PtrFromContext[urltest.HistoryStorage](ctx) == nil {
|
|
ctx = service.ContextWithPtr(ctx, urltest.NewHistoryStorage())
|
|
}
|
|
platformInterface := service.FromContext[adapter.PlatformInterface](ctx)
|
|
var defaultLogWriter io.Writer
|
|
if platformInterface != nil {
|
|
defaultLogWriter = io.Discard
|
|
}
|
|
logFactory, err := log.New(log.Options{
|
|
Context: ctx,
|
|
Options: common.PtrValueOrDefault(options.Log),
|
|
Observable: needClashAPI || needAPIService,
|
|
DefaultWriter: defaultLogWriter,
|
|
BaseTime: createdAt,
|
|
PlatformWriter: options.PlatformLogWriter,
|
|
})
|
|
if err != nil {
|
|
return nil, E.Cause(err, "create log factory")
|
|
}
|
|
service.MustRegister[log.Factory](ctx, logFactory)
|
|
|
|
var internalServices []adapter.LifecycleService
|
|
routeOptions := common.PtrValueOrDefault(options.Route)
|
|
certificateOptions := common.PtrValueOrDefault(options.Certificate)
|
|
if C.IsAndroid || certificateOptions.Store != "" && certificateOptions.Store != C.CertificateStoreSystem ||
|
|
len(certificateOptions.Certificate) > 0 ||
|
|
len(certificateOptions.CertificatePath) > 0 ||
|
|
len(certificateOptions.CertificateDirectoryPath) > 0 {
|
|
certificateStore, err := certificate.NewStore(ctx, logFactory.NewLogger("certificate"), certificateOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
service.MustRegister[adapter.CertificateStore](ctx, certificateStore)
|
|
internalServices = append(internalServices, certificateStore)
|
|
}
|
|
dnsOptions := common.PtrValueOrDefault(options.DNS)
|
|
endpointManager := endpoint.NewManager(logFactory.NewLogger("endpoint"), endpointRegistry)
|
|
inboundManager := inbound.NewManager(logFactory.NewLogger("inbound"), inboundRegistry, endpointManager)
|
|
outboundManager := outbound.NewManager(logFactory.NewLogger("outbound"), outboundRegistry, endpointManager, routeOptions.Final)
|
|
dnsTransportManager := dns.NewTransportManager(logFactory.NewLogger("dns/transport"), dnsTransportRegistry, outboundManager, dnsOptions.Final)
|
|
serviceManager := boxService.NewManager(logFactory.NewLogger("service"), serviceRegistry)
|
|
certificateProviderManager := boxCertificate.NewManager(logFactory.NewLogger("certificate-provider"), certificateProviderRegistry)
|
|
service.MustRegister[adapter.EndpointManager](ctx, endpointManager)
|
|
service.MustRegister[adapter.InboundManager](ctx, inboundManager)
|
|
service.MustRegister[adapter.OutboundManager](ctx, outboundManager)
|
|
service.MustRegister[adapter.DNSTransportManager](ctx, dnsTransportManager)
|
|
service.MustRegister[adapter.ServiceManager](ctx, serviceManager)
|
|
service.MustRegister[adapter.CertificateProviderManager](ctx, certificateProviderManager)
|
|
dnsRouter, err := dns.NewRouter(ctx, logFactory, dnsOptions)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize DNS router")
|
|
}
|
|
service.MustRegister[adapter.DNSRouter](ctx, dnsRouter)
|
|
service.MustRegister[adapter.DNSRuleSetUpdateValidator](ctx, dnsRouter)
|
|
networkManager, err := route.NewNetworkManager(ctx, logFactory.NewLogger("network"), routeOptions, dnsOptions)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize network manager")
|
|
}
|
|
service.MustRegister[adapter.NetworkManager](ctx, networkManager)
|
|
connectionManager := route.NewConnectionManager(logFactory.NewLogger("connection"))
|
|
service.MustRegister[adapter.ConnectionManager](ctx, connectionManager)
|
|
// Must register after ConnectionManager: the Apple HTTP engine's proxy bridge reads it from the context when Manager.Start resolves the default client.
|
|
httpClientManager := httpclient.NewManager(ctx, logFactory.NewLogger("httpclient"), options.HTTPClients, routeOptions.DefaultHTTPClient)
|
|
service.MustRegister[adapter.HTTPClientManager](ctx, httpClientManager)
|
|
httpClientService := adapter.LifecycleService(httpClientManager)
|
|
router := route.NewRouter(ctx, logFactory, routeOptions, dnsOptions)
|
|
service.MustRegister[adapter.Router](ctx, router)
|
|
err = router.Initialize(routeOptions.Rules, routeOptions.RuleSet)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize router")
|
|
}
|
|
if needClashAPI || needAPIService {
|
|
trafficManager := trafficcontrol.NewManager(outboundManager)
|
|
service.MustRegisterPtr(ctx, trafficManager)
|
|
router.AppendTracker(trafficManager)
|
|
internalServices = append(internalServices, trafficManager)
|
|
}
|
|
ntpOptions := common.PtrValueOrDefault(options.NTP)
|
|
var timeService *tls.TimeServiceWrapper
|
|
if ntpOptions.Enabled {
|
|
timeService = new(tls.TimeServiceWrapper)
|
|
service.MustRegister[ntp.TimeService](ctx, timeService)
|
|
}
|
|
for i, transportOptions := range dnsOptions.Servers {
|
|
var tag string
|
|
if transportOptions.Tag != "" {
|
|
tag = transportOptions.Tag
|
|
} else {
|
|
tag = F.ToString(i)
|
|
}
|
|
err = dnsTransportManager.Create(
|
|
ctx,
|
|
logFactory.NewLogger(F.ToString("dns/", transportOptions.Type, "[", tag, "]")),
|
|
tag,
|
|
transportOptions.Type,
|
|
transportOptions.Options,
|
|
)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize DNS server[", i, "]")
|
|
}
|
|
}
|
|
err = dnsRouter.Initialize(dnsOptions.Rules)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize dns router")
|
|
}
|
|
for i, endpointOptions := range options.Endpoints {
|
|
var tag string
|
|
if endpointOptions.Tag != "" {
|
|
tag = endpointOptions.Tag
|
|
} else {
|
|
tag = F.ToString(i)
|
|
}
|
|
endpointCtx := ctx
|
|
if tag != "" {
|
|
// TODO: remove this
|
|
endpointCtx = adapter.WithContext(endpointCtx, &adapter.InboundContext{
|
|
Outbound: tag,
|
|
})
|
|
}
|
|
err = endpointManager.Create(
|
|
endpointCtx,
|
|
router,
|
|
logFactory.NewLogger(F.ToString("endpoint/", endpointOptions.Type, "[", tag, "]")),
|
|
tag,
|
|
endpointOptions.Type,
|
|
endpointOptions.Options,
|
|
)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize endpoint[", i, "]")
|
|
}
|
|
}
|
|
for i, inboundOptions := range options.Inbounds {
|
|
var tag string
|
|
if inboundOptions.Tag != "" {
|
|
tag = inboundOptions.Tag
|
|
} else {
|
|
tag = F.ToString(i)
|
|
}
|
|
err = inboundManager.Create(
|
|
ctx,
|
|
router,
|
|
logFactory.NewLogger(F.ToString("inbound/", inboundOptions.Type, "[", tag, "]")),
|
|
tag,
|
|
inboundOptions.Type,
|
|
inboundOptions.Options,
|
|
)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize inbound[", i, "]")
|
|
}
|
|
}
|
|
for i, serviceOptions := range options.Services {
|
|
var tag string
|
|
if serviceOptions.Tag != "" {
|
|
tag = serviceOptions.Tag
|
|
} else {
|
|
tag = F.ToString(i)
|
|
}
|
|
err = serviceManager.Create(
|
|
ctx,
|
|
logFactory.NewLogger(F.ToString("service/", serviceOptions.Type, "[", tag, "]")),
|
|
tag,
|
|
serviceOptions.Type,
|
|
serviceOptions.Options,
|
|
)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize service[", i, "]")
|
|
}
|
|
}
|
|
for i, outboundOptions := range options.Outbounds {
|
|
var tag string
|
|
if outboundOptions.Tag != "" {
|
|
tag = outboundOptions.Tag
|
|
} else {
|
|
tag = F.ToString(i)
|
|
}
|
|
outboundCtx := ctx
|
|
if tag != "" {
|
|
// TODO: remove this
|
|
outboundCtx = adapter.WithContext(outboundCtx, &adapter.InboundContext{
|
|
Outbound: tag,
|
|
})
|
|
}
|
|
err = outboundManager.Create(
|
|
outboundCtx,
|
|
router,
|
|
logFactory.NewLogger(F.ToString("outbound/", outboundOptions.Type, "[", tag, "]")),
|
|
tag,
|
|
outboundOptions.Type,
|
|
outboundOptions.Options,
|
|
)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize outbound[", i, "]")
|
|
}
|
|
}
|
|
for i, certificateProviderOptions := range options.CertificateProviders {
|
|
var tag string
|
|
if certificateProviderOptions.Tag != "" {
|
|
tag = certificateProviderOptions.Tag
|
|
} else {
|
|
tag = F.ToString(i)
|
|
}
|
|
err = certificateProviderManager.Create(
|
|
ctx,
|
|
logFactory.NewLogger(F.ToString("certificate-provider/", certificateProviderOptions.Type, "[", tag, "]")),
|
|
tag,
|
|
certificateProviderOptions.Type,
|
|
certificateProviderOptions.Options,
|
|
)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize certificate provider[", i, "]")
|
|
}
|
|
}
|
|
outboundManager.Initialize(func() (adapter.Outbound, error) {
|
|
return direct.NewOutbound(
|
|
ctx,
|
|
router,
|
|
logFactory.NewLogger("outbound/direct"),
|
|
"direct",
|
|
option.DirectOutboundOptions{},
|
|
)
|
|
})
|
|
dnsTransportManager.Initialize(func() (adapter.DNSTransport, error) {
|
|
return dnsTransportRegistry.CreateDNSTransport(
|
|
ctx,
|
|
logFactory.NewLogger("dns/local"),
|
|
"local",
|
|
C.DNSTypeLocal,
|
|
&option.LocalDNSServerOptions{},
|
|
)
|
|
})
|
|
httpClientManager.Initialize(func() (*httpclient.ManagedTransport, error) {
|
|
deprecated.Report(ctx, deprecated.OptionImplicitDefaultHTTPClient)
|
|
var httpClientOptions option.HTTPClientOptions
|
|
httpClientOptions.DefaultOutbound = true
|
|
return httpclient.NewTransport(ctx, logFactory.NewLogger("httpclient"), "", httpClientOptions)
|
|
})
|
|
if platformInterface != nil {
|
|
err = platformInterface.Initialize(networkManager)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "initialize platform interface")
|
|
}
|
|
}
|
|
if needCacheFile {
|
|
cacheFile := cachefile.New(ctx, logFactory.NewLogger("cache-file"), common.PtrValueOrDefault(experimentalOptions.CacheFile))
|
|
service.MustRegister[adapter.CacheFile](ctx, cacheFile)
|
|
internalServices = append(internalServices, cacheFile)
|
|
}
|
|
if needClashAPI {
|
|
clashAPIOptions := common.PtrValueOrDefault(experimentalOptions.ClashAPI)
|
|
clashAPIOptions.ModeList = experimental.CalculateClashModeList(options.Options)
|
|
clashServer, err := experimental.NewClashServer(ctx, logFactory.(log.ObservableFactory), clashAPIOptions)
|
|
if err != nil {
|
|
return nil, E.Cause(err, "create clash-server")
|
|
}
|
|
service.MustRegister[adapter.ClashServer](ctx, clashServer)
|
|
internalServices = append(internalServices, clashServer)
|
|
}
|
|
if needV2RayAPI {
|
|
v2rayServer, err := experimental.NewV2RayServer(logFactory.NewLogger("v2ray-api"), common.PtrValueOrDefault(experimentalOptions.V2RayAPI))
|
|
if err != nil {
|
|
return nil, E.Cause(err, "create v2ray-server")
|
|
}
|
|
if v2rayServer.StatsService() != nil {
|
|
router.AppendTracker(v2rayServer.StatsService())
|
|
internalServices = append(internalServices, v2rayServer)
|
|
service.MustRegister[adapter.V2RayServer](ctx, v2rayServer)
|
|
}
|
|
}
|
|
if ntpOptions.Enabled {
|
|
ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions, ntpOptions.ServerIsDomain())
|
|
if err != nil {
|
|
return nil, E.Cause(err, "create NTP service")
|
|
}
|
|
ntpService := ntp.NewService(ntp.Options{
|
|
Context: ctx,
|
|
Dialer: ntpDialer,
|
|
Logger: logFactory.NewLogger("ntp"),
|
|
Server: ntpOptions.ServerOptions.Build(),
|
|
Interval: time.Duration(ntpOptions.Interval),
|
|
WriteToSystem: ntpOptions.WriteToSystem,
|
|
})
|
|
timeService.TimeService = ntpService
|
|
internalServices = append(internalServices, adapter.NewLifecycleService(ntpService, "ntp service"))
|
|
}
|
|
return &Box{
|
|
network: networkManager,
|
|
endpoint: endpointManager,
|
|
inbound: inboundManager,
|
|
outbound: outboundManager,
|
|
dnsTransport: dnsTransportManager,
|
|
service: serviceManager,
|
|
certificateProvider: certificateProviderManager,
|
|
dnsRouter: dnsRouter,
|
|
connection: connectionManager,
|
|
router: router,
|
|
httpClientService: httpClientService,
|
|
createdAt: createdAt,
|
|
logFactory: logFactory,
|
|
logger: logFactory.Logger(),
|
|
internalService: internalServices,
|
|
done: make(chan struct{}),
|
|
}, nil
|
|
}
|
|
|
|
func (s *Box) PreStart() error {
|
|
err := s.preStart()
|
|
if err != nil {
|
|
// TODO: remove catch error
|
|
defer func() {
|
|
v := recover()
|
|
if v != nil {
|
|
println(err.Error())
|
|
debug.PrintStack()
|
|
panic("panic on early close: " + fmt.Sprint(v))
|
|
}
|
|
}()
|
|
s.Close()
|
|
return err
|
|
}
|
|
s.logger.Info("sing-box pre-started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
|
|
return nil
|
|
}
|
|
|
|
func (s *Box) Start() error {
|
|
err := s.start()
|
|
if err != nil {
|
|
// TODO: remove catch error
|
|
defer func() {
|
|
v := recover()
|
|
if v != nil {
|
|
println(err.Error())
|
|
debug.PrintStack()
|
|
println("panic on early start: " + fmt.Sprint(v))
|
|
}
|
|
}()
|
|
s.Close()
|
|
return err
|
|
}
|
|
s.logger.Info("sing-box started (", F.Seconds(time.Since(s.createdAt).Seconds()), "s)")
|
|
return nil
|
|
}
|
|
|
|
func (s *Box) preStart() error {
|
|
monitor := taskmonitor.New(s.logger, C.StartTimeout)
|
|
monitor.Start("start logger")
|
|
err := s.logFactory.Start()
|
|
monitor.Finish()
|
|
if err != nil {
|
|
return E.Cause(err, "start logger")
|
|
}
|
|
err = adapter.StartNamed(s.logger, adapter.StartStateInitialize, s.internalService) // cache-file clash-api v2ray-api
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.Start(s.logger, adapter.StartStateInitialize, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.inbound, s.endpoint, s.service, s.certificateProvider)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.Start(s.logger, adapter.StartStateStart, s.outbound, s.dnsTransport, s.network, s.connection)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.StartNamed(s.logger, adapter.StartStateStart, []adapter.LifecycleService{s.httpClientService})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.Start(s.logger, adapter.StartStateStart, s.router, s.dnsRouter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Box) start() error {
|
|
err := s.preStart()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.StartNamed(s.logger, adapter.StartStateStart, s.internalService)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.Start(s.logger, adapter.StartStateStart, s.endpoint)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.Start(s.logger, adapter.StartStateStart, s.certificateProvider)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.Start(s.logger, adapter.StartStateStart, s.inbound, s.service)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.Start(s.logger, adapter.StartStatePostStart, s.outbound, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.endpoint, s.certificateProvider, s.inbound, s.service)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.StartNamed(s.logger, adapter.StartStatePostStart, s.internalService)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.Start(s.logger, adapter.StartStateStarted, s.network, s.dnsTransport, s.dnsRouter, s.connection, s.router, s.outbound, s.endpoint, s.certificateProvider, s.inbound, s.service)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = adapter.StartNamed(s.logger, adapter.StartStateStarted, s.internalService)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Box) Close() error {
|
|
select {
|
|
case <-s.done:
|
|
return os.ErrClosed
|
|
default:
|
|
close(s.done)
|
|
}
|
|
var err error
|
|
for _, closeItem := range []struct {
|
|
name string
|
|
service adapter.Lifecycle
|
|
}{
|
|
{"service", s.service},
|
|
{"inbound", s.inbound},
|
|
{"certificate-provider", s.certificateProvider},
|
|
{"endpoint", s.endpoint},
|
|
{"outbound", s.outbound},
|
|
{"router", s.router},
|
|
{"connection", s.connection},
|
|
{"dns-router", s.dnsRouter},
|
|
{"dns-transport", s.dnsTransport},
|
|
{"network", s.network},
|
|
} {
|
|
done := adapter.LogElapsed(s.logger, "close ", closeItem.name)
|
|
err = E.Append(err, closeItem.service.Close(), func(err error) error {
|
|
return E.Cause(err, "close ", closeItem.name)
|
|
})
|
|
done()
|
|
}
|
|
if s.httpClientService != nil {
|
|
s.logger.Trace("close ", s.httpClientService.Name())
|
|
startTime := time.Now()
|
|
err = E.Append(err, s.httpClientService.Close(), func(err error) error {
|
|
return E.Cause(err, "close ", s.httpClientService.Name())
|
|
})
|
|
s.logger.Trace("close ", s.httpClientService.Name(), " completed (", F.Seconds(time.Since(startTime).Seconds()), "s)")
|
|
}
|
|
for _, lifecycleService := range s.internalService {
|
|
done := adapter.LogElapsed(s.logger, "close ", lifecycleService.Name())
|
|
err = E.Append(err, lifecycleService.Close(), func(err error) error {
|
|
return E.Cause(err, "close ", lifecycleService.Name())
|
|
})
|
|
done()
|
|
}
|
|
done := adapter.LogElapsed(s.logger, "close logger")
|
|
err = E.Append(err, s.logFactory.Close(), func(err error) error {
|
|
return E.Cause(err, "close logger")
|
|
})
|
|
done()
|
|
return err
|
|
}
|
|
|
|
func (s *Box) Network() adapter.NetworkManager {
|
|
return s.network
|
|
}
|
|
|
|
func (s *Box) Router() adapter.Router {
|
|
return s.router
|
|
}
|
|
|
|
func (s *Box) Inbound() adapter.InboundManager {
|
|
return s.inbound
|
|
}
|
|
|
|
func (s *Box) Outbound() adapter.OutboundManager {
|
|
return s.outbound
|
|
}
|
|
|
|
func (s *Box) Endpoint() adapter.EndpointManager {
|
|
return s.endpoint
|
|
}
|
|
|
|
func (s *Box) LogFactory() log.Factory {
|
|
return s.logFactory
|
|
}
|