From a5dcb0f13e808c51274648c8d08a0cda4e4359d1 Mon Sep 17 00:00:00 2001 From: Darien Raymond Date: Sun, 21 Oct 2018 10:27:13 +0200 Subject: [PATCH] refactor dependency resolution --- app/commander/commander.go | 55 +++--- app/dispatcher/default.go | 26 ++- app/dns/server.go | 12 +- app/dns/server_test.go | 5 +- app/log/log.go | 6 - app/policy/manager.go | 8 - app/proxyman/command/command.go | 13 +- app/proxyman/inbound/always.go | 6 +- app/proxyman/inbound/inbound.go | 4 - app/proxyman/mux/mux.go | 10 +- app/proxyman/outbound/handler.go | 2 +- app/proxyman/outbound/outbound.go | 4 - app/router/router.go | 10 +- app/router/router_test.go | 3 +- app/stats/command/command.go | 3 +- app/stats/stats.go | 8 - dns.go | 59 ------ features/dns/client.go | 13 ++ features/policy/default.go | 31 ++++ features/routing/router.go | 19 ++ functions.go | 7 +- network.go | 143 --------------- policy.go | 74 -------- proxy/dokodemo/dokodemo.go | 2 +- proxy/freedom/freedom.go | 13 +- proxy/http/server.go | 11 +- proxy/mtproto/server.go | 2 +- proxy/shadowsocks/client.go | 20 +- proxy/shadowsocks/server.go | 15 +- proxy/socks/client.go | 2 +- proxy/socks/server.go | 2 +- proxy/vmess/inbound/inbound.go | 4 +- proxy/vmess/outbound/outbound.go | 17 +- router.go | 111 ----------- stats.go | 70 ------- v2ray.go | 296 +++++++++++++++--------------- 36 files changed, 356 insertions(+), 730 deletions(-) delete mode 100644 dns.go create mode 100644 features/policy/default.go delete mode 100644 network.go delete mode 100644 policy.go delete mode 100644 router.go delete mode 100644 stats.go diff --git a/app/commander/commander.go b/app/commander/commander.go index 87e9bc9b5..e753448b7 100644 --- a/app/commander/commander.go +++ b/app/commander/commander.go @@ -12,29 +12,46 @@ import ( "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/signal/done" + "v2ray.com/core/features" "v2ray.com/core/features/outbound" ) // Commander is a V2Ray feature that provides gRPC methods to external clients. type Commander struct { sync.Mutex - server *grpc.Server - config Config - v *core.Instance - ohm outbound.Manager + server *grpc.Server + services []Service + ohm outbound.Manager + tag string } // NewCommander creates a new Commander based on the given config. func NewCommander(ctx context.Context, config *Config) (*Commander, error) { - v := core.MustFromContext(ctx) c := &Commander{ - config: *config, - ohm: v.OutboundHandlerManager(), - v: v, + tag: config.Tag, } - if err := v.RegisterFeature(c); err != nil { - return nil, err + + v := core.MustFromContext(ctx) + v.RequireFeatures([]interface{}{outbound.ManagerType()}, func(fs []features.Feature) { + c.ohm = fs[0].(outbound.Manager) + }) + + for _, rawConfig := range config.Service { + config, err := rawConfig.GetInstance() + if err != nil { + return nil, err + } + rawService, err := common.CreateObject(ctx, config) + if err != nil { + return nil, err + } + service, ok := rawService.(Service) + if !ok { + return nil, newError("not a Service.") + } + c.services = append(c.services, service) } + return c, nil } @@ -47,19 +64,7 @@ func (c *Commander) Type() interface{} { func (c *Commander) Start() error { c.Lock() c.server = grpc.NewServer() - for _, rawConfig := range c.config.Service { - config, err := rawConfig.GetInstance() - if err != nil { - return err - } - rawService, err := core.CreateObject(c.v, config) - if err != nil { - return err - } - service, ok := rawService.(Service) - if !ok { - return newError("not a Service.") - } + for _, service := range c.services { service.Register(c.server) } c.Unlock() @@ -75,12 +80,12 @@ func (c *Commander) Start() error { } }() - if err := c.ohm.RemoveHandler(context.Background(), c.config.Tag); err != nil { + if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil { newError("failed to remove existing handler").WriteToLog() } return c.ohm.AddHandler(context.Background(), &Outbound{ - tag: c.config.Tag, + tag: c.tag, listener: listener, }) } diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index 9acb5df02..0ba2f4a7c 100644 --- a/app/dispatcher/default.go +++ b/app/dispatcher/default.go @@ -16,6 +16,7 @@ import ( "v2ray.com/core/common/protocol" "v2ray.com/core/common/session" "v2ray.com/core/common/vio" + "v2ray.com/core/features" "v2ray.com/core/features/outbound" "v2ray.com/core/features/policy" "v2ray.com/core/features/routing" @@ -91,17 +92,22 @@ type DefaultDispatcher struct { // NewDefaultDispatcher create a new DefaultDispatcher. func NewDefaultDispatcher(ctx context.Context, config *Config) (*DefaultDispatcher, error) { - v := core.MustFromContext(ctx) - d := &DefaultDispatcher{ - ohm: v.OutboundHandlerManager(), - router: v.Router(), - policy: v.PolicyManager(), - stats: v.Stats(), - } + d := &DefaultDispatcher{} + + v := core.MustFromContext(ctx) + v.RequireFeatures([]interface{}{outbound.ManagerType(), routing.RouterType(), policy.ManagerType()}, func(fs []features.Feature) { + d.ohm = fs[0].(outbound.Manager) + d.router = fs[1].(routing.Router) + d.policy = fs[2].(policy.Manager) + }) + v.RequireFeatures([]interface{}{core.ServerType()}, func([]features.Feature) { + f := v.GetFeature(stats.ManagerType()) + if f == nil { + return + } + d.stats = f.(stats.Manager) + }) - if err := v.RegisterFeature(d); err != nil { - return nil, newError("unable to register Dispatcher").Base(err) - } return d, nil } diff --git a/app/dns/server.go b/app/dns/server.go index d96efe70a..82902864d 100644 --- a/app/dns/server.go +++ b/app/dns/server.go @@ -7,6 +7,8 @@ import ( "sync" "time" + "v2ray.com/core/features/routing" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/net" @@ -42,9 +44,6 @@ func New(ctx context.Context, config *Config) (*Server, error) { server.hosts = hosts v := core.MustFromContext(ctx) - if err := v.RegisterFeature(server); err != nil { - return nil, newError("unable to register DNSClient.").Base(err) - } addNameServer := func(endpoint *net.Endpoint) int { address := endpoint.Address.AsAddress() @@ -56,7 +55,12 @@ func New(ctx context.Context, config *Config) (*Server, error) { dest.Network = net.Network_UDP } if dest.Network == net.Network_UDP { - server.servers = append(server.servers, NewClassicNameServer(dest, v.Dispatcher(), server.clientIP)) + idx := len(server.servers) + server.servers = append(server.servers, nil) + v.RequireFeatures([]interface{}{routing.DispatcherType()}, func(fs []features.Feature) { + dispatcher := fs[0].(routing.Dispatcher) + server.servers[idx] = NewClassicNameServer(dest, dispatcher, server.clientIP) + }) } } return len(server.servers) - 1 diff --git a/app/dns/server_test.go b/app/dns/server_test.go index 0b1b03297..3a2eaaf3e 100644 --- a/app/dns/server_test.go +++ b/app/dns/server_test.go @@ -13,6 +13,7 @@ import ( _ "v2ray.com/core/app/proxyman/outbound" "v2ray.com/core/common/net" "v2ray.com/core/common/serial" + feature_dns "v2ray.com/core/features/dns" "v2ray.com/core/proxy/freedom" "v2ray.com/core/testing/servers/udp" . "v2ray.com/ext/assert" @@ -85,7 +86,7 @@ func TestUDPServer(t *testing.T) { v, err := core.New(config) assert(err, IsNil) - client := v.DNSClient() + client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) ips, err := client.LookupIP("google.com") assert(err, IsNil) @@ -171,7 +172,7 @@ func TestPrioritizedDomain(t *testing.T) { v, err := core.New(config) assert(err, IsNil) - client := v.DNSClient() + client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) startTime := time.Now() ips, err := client.LookupIP("google.com") diff --git a/app/log/log.go b/app/log/log.go index 5cb0bfef9..93f3fa968 100644 --- a/app/log/log.go +++ b/app/log/log.go @@ -6,7 +6,6 @@ import ( "context" "sync" - "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/log" ) @@ -28,11 +27,6 @@ func New(ctx context.Context, config *Config) (*Instance, error) { } log.RegisterHandler(g) - v := core.FromContext(ctx) - if v != nil { - common.Must(v.RegisterFeature(g)) - } - return g, nil } diff --git a/app/policy/manager.go b/app/policy/manager.go index d23ddf0eb..205fe3d66 100644 --- a/app/policy/manager.go +++ b/app/policy/manager.go @@ -3,7 +3,6 @@ package policy import ( "context" - "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/features/policy" ) @@ -28,13 +27,6 @@ func New(ctx context.Context, config *Config) (*Instance, error) { } } - v := core.FromContext(ctx) - if v != nil { - if err := v.RegisterFeature(m); err != nil { - return nil, newError("unable to register PolicyManager in core").Base(err).AtError() - } - } - return m, nil } diff --git a/app/proxyman/command/command.go b/app/proxyman/command/command.go index 2421a5b1b..d3d8ffc8a 100755 --- a/app/proxyman/command/command.go +++ b/app/proxyman/command/command.go @@ -4,8 +4,10 @@ import ( "context" grpc "google.golang.org/grpc" + "v2ray.com/core" "v2ray.com/core/common" + "v2ray.com/core/features" "v2ray.com/core/features/inbound" "v2ray.com/core/features/outbound" "v2ray.com/core/proxy" @@ -136,11 +138,14 @@ type service struct { } func (s *service) Register(server *grpc.Server) { - RegisterHandlerServiceServer(server, &handlerServer{ - s: s.v, - ihm: s.v.InboundHandlerManager(), - ohm: s.v.OutboundHandlerManager(), + hs := &handlerServer{ + s: s.v, + } + s.v.RequireFeatures([]interface{}{inbound.ManagerType(), outbound.ManagerType()}, func(fs []features.Feature) { + hs.ihm = fs[0].(inbound.Manager) + hs.ohm = fs[1].(outbound.Manager) }) + RegisterHandlerServiceServer(server, hs) } func init() { diff --git a/app/proxyman/inbound/always.go b/app/proxyman/inbound/always.go index d67a7b86c..1ee2371c1 100644 --- a/app/proxyman/inbound/always.go +++ b/app/proxyman/inbound/always.go @@ -10,6 +10,7 @@ import ( "v2ray.com/core/common/dice" "v2ray.com/core/common/net" "v2ray.com/core/common/serial" + "v2ray.com/core/features/policy" "v2ray.com/core/features/stats" "v2ray.com/core/proxy" "v2ray.com/core/transport/internet" @@ -19,9 +20,9 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) var uplinkCounter stats.Counter var downlinkCounter stats.Counter - policy := v.PolicyManager() - statsManager := v.Stats() + policy := v.GetFeature(policy.ManagerType()).(policy.Manager) if len(tag) > 0 && policy.ForSystem().Stats.InboundUplink { + statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) name := "inbound>>>" + tag + ">>>traffic>>>uplink" c, _ := stats.GetOrRegisterCounter(statsManager, name) if c != nil { @@ -29,6 +30,7 @@ func getStatCounter(v *core.Instance, tag string) (stats.Counter, stats.Counter) } } if len(tag) > 0 && policy.ForSystem().Stats.InboundDownlink { + statsManager := v.GetFeature(stats.ManagerType()).(stats.Manager) name := "inbound>>>" + tag + ">>>traffic>>>downlink" c, _ := stats.GetOrRegisterCounter(statsManager, name) if c != nil { diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index 98071269c..e80015e1a 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -27,10 +27,6 @@ func New(ctx context.Context, config *proxyman.InboundConfig) (*Manager, error) m := &Manager{ taggedHandlers: make(map[string]inbound.Handler), } - v := core.MustFromContext(ctx) - if err := v.RegisterFeature(m); err != nil { - return nil, newError("unable to register InboundHandlerManager").Base(err) - } return m, nil } diff --git a/app/proxyman/mux/mux.go b/app/proxyman/mux/mux.go index be2e4c014..57497b0d7 100644 --- a/app/proxyman/mux/mux.go +++ b/app/proxyman/mux/mux.go @@ -8,6 +8,8 @@ import ( "sync" "time" + "v2ray.com/core/features" + "v2ray.com/core" "v2ray.com/core/app/proxyman" "v2ray.com/core/common" @@ -304,9 +306,11 @@ type Server struct { // NewServer creates a new mux.Server. func NewServer(ctx context.Context) *Server { - s := &Server{ - dispatcher: core.MustFromContext(ctx).Dispatcher(), - } + s := &Server{} + v := core.MustFromContext(ctx) + v.RequireFeatures([]interface{}{routing.DispatcherType()}, func(fs []features.Feature) { + s.dispatcher = fs[0].(routing.Dispatcher) + }) return s } diff --git a/app/proxyman/outbound/handler.go b/app/proxyman/outbound/handler.go index 70655d7a6..3975b6d8d 100644 --- a/app/proxyman/outbound/handler.go +++ b/app/proxyman/outbound/handler.go @@ -29,7 +29,7 @@ func NewHandler(ctx context.Context, config *core.OutboundHandlerConfig) (outbou v := core.MustFromContext(ctx) h := &Handler{ config: config, - outboundManager: v.OutboundHandlerManager(), + outboundManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager), } if config.SenderSettings != nil { diff --git a/app/proxyman/outbound/outbound.go b/app/proxyman/outbound/outbound.go index 4a072a1f6..9bc70b16e 100644 --- a/app/proxyman/outbound/outbound.go +++ b/app/proxyman/outbound/outbound.go @@ -26,10 +26,6 @@ func New(ctx context.Context, config *proxyman.OutboundConfig) (*Manager, error) m := &Manager{ taggedHandler: make(map[string]outbound.Handler), } - v := core.MustFromContext(ctx) - if err := v.RegisterFeature(m); err != nil { - return nil, newError("unable to register outbound.Manager").Base(err) - } return m, nil } diff --git a/app/router/router.go b/app/router/router.go index cfcbbdefd..e800d8cf2 100644 --- a/app/router/router.go +++ b/app/router/router.go @@ -9,6 +9,7 @@ import ( "v2ray.com/core/common" "v2ray.com/core/common/net" "v2ray.com/core/common/session" + "v2ray.com/core/features" "v2ray.com/core/features/dns" "v2ray.com/core/features/routing" "v2ray.com/core/proxy" @@ -23,11 +24,9 @@ type Router struct { // NewRouter creates a new Router based on the given config. func NewRouter(ctx context.Context, config *Config) (*Router, error) { - v := core.MustFromContext(ctx) r := &Router{ domainStrategy: config.DomainStrategy, rules: make([]Rule, len(config.Rule)), - dns: v.DNSClient(), } for idx, rule := range config.Rule { @@ -39,9 +38,10 @@ func NewRouter(ctx context.Context, config *Config) (*Router, error) { r.rules[idx].Condition = cond } - if err := v.RegisterFeature(r); err != nil { - return nil, newError("unable to register Router").Base(err) - } + v := core.MustFromContext(ctx) + v.RequireFeatures([]interface{}{dns.ClientType()}, func(fs []features.Feature) { + r.dns = fs[0].(dns.Client) + }) return r, nil } diff --git a/app/router/router_test.go b/app/router/router_test.go index 8e69d47d6..f4d60e29f 100644 --- a/app/router/router_test.go +++ b/app/router/router_test.go @@ -12,6 +12,7 @@ import ( "v2ray.com/core/common/net" "v2ray.com/core/common/serial" "v2ray.com/core/common/session" + "v2ray.com/core/features/routing" . "v2ray.com/ext/assert" ) @@ -38,7 +39,7 @@ func TestSimpleRouter(t *testing.T) { v, err := core.New(config) common.Must(err) - r := v.Router() + r := v.GetFeature(routing.RouterType()).(routing.Router) ctx := withOutbound(&session.Outbound{Target: net.TCPDestination(net.DomainAddress("v2ray.com"), 80)}) tag, err := r.PickRoute(ctx) diff --git a/app/stats/command/command.go b/app/stats/command/command.go index c6f93beff..24bea2d7b 100644 --- a/app/stats/command/command.go +++ b/app/stats/command/command.go @@ -79,7 +79,8 @@ type service struct { } func (s *service) Register(server *grpc.Server) { - RegisterStatsServiceServer(server, NewStatsServer(s.v.Stats())) + f := s.v.GetFeature(feature_stats.ManagerType()) + RegisterStatsServiceServer(server, NewStatsServer(f.(feature_stats.Manager))) } func init() { diff --git a/app/stats/stats.go b/app/stats/stats.go index ae6f184b2..c1b666918 100644 --- a/app/stats/stats.go +++ b/app/stats/stats.go @@ -7,7 +7,6 @@ import ( "sync" "sync/atomic" - "v2ray.com/core" "v2ray.com/core/features/stats" ) @@ -42,13 +41,6 @@ func NewManager(ctx context.Context, config *Config) (*Manager, error) { counters: make(map[string]*Counter), } - v := core.FromContext(ctx) - if v != nil { - if err := v.RegisterFeature(m); err != nil { - return nil, newError("failed to register StatManager").Base(err) - } - } - return m, nil } diff --git a/dns.go b/dns.go deleted file mode 100644 index a7f8d7f31..000000000 --- a/dns.go +++ /dev/null @@ -1,59 +0,0 @@ -package core - -import ( - "sync" - - "v2ray.com/core/common" - "v2ray.com/core/common/net" - "v2ray.com/core/features/dns" -) - -type syncDNSClient struct { - sync.RWMutex - dns.Client -} - -func (d *syncDNSClient) Type() interface{} { - return dns.ClientType() -} - -func (d *syncDNSClient) LookupIP(host string) ([]net.IP, error) { - d.RLock() - defer d.RUnlock() - - if d.Client == nil { - return net.LookupIP(host) - } - - return d.Client.LookupIP(host) -} - -func (d *syncDNSClient) Start() error { - d.RLock() - defer d.RUnlock() - - if d.Client == nil { - return nil - } - - return d.Client.Start() -} - -func (d *syncDNSClient) Close() error { - d.RLock() - defer d.RUnlock() - - return common.Close(d.Client) -} - -func (d *syncDNSClient) Set(client dns.Client) { - if client == nil { - return - } - - d.Lock() - defer d.Unlock() - - common.Close(d.Client) // nolint: errcheck - d.Client = client -} diff --git a/features/dns/client.go b/features/dns/client.go index 948be683b..12568ab69 100644 --- a/features/dns/client.go +++ b/features/dns/client.go @@ -15,3 +15,16 @@ type Client interface { func ClientType() interface{} { return (*Client)(nil) } + +type LocalClient struct{} + +func (LocalClient) Type() interface{} { + return ClientType() +} + +func (LocalClient) Start() error { return nil } +func (LocalClient) Close() error { return nil } + +func (LocalClient) LookupIP(host string) ([]net.IP, error) { + return net.LookupIP(host) +} diff --git a/features/policy/default.go b/features/policy/default.go new file mode 100644 index 000000000..267864708 --- /dev/null +++ b/features/policy/default.go @@ -0,0 +1,31 @@ +package policy + +import ( + "time" +) + +type DefaultManager struct{} + +func (DefaultManager) Type() interface{} { + return ManagerType() +} + +func (DefaultManager) ForLevel(level uint32) Session { + p := SessionDefault() + if level == 1 { + p.Timeouts.ConnectionIdle = time.Second * 600 + } + return p +} + +func (DefaultManager) ForSystem() System { + return System{} +} + +func (DefaultManager) Start() error { + return nil +} + +func (DefaultManager) Close() error { + return nil +} diff --git a/features/routing/router.go b/features/routing/router.go index 1276e25f5..09e7ce20f 100644 --- a/features/routing/router.go +++ b/features/routing/router.go @@ -3,6 +3,7 @@ package routing import ( "context" + "v2ray.com/core/common" "v2ray.com/core/features" ) @@ -18,3 +19,21 @@ type Router interface { func RouterType() interface{} { return (*Router)(nil) } + +type DefaultRouter struct{} + +func (DefaultRouter) Type() interface{} { + return RouterType() +} + +func (DefaultRouter) PickRoute(ctx context.Context) (string, error) { + return "", common.ErrNoClue +} + +func (DefaultRouter) Start() error { + return nil +} + +func (DefaultRouter) Close() error { + return nil +} diff --git a/functions.go b/functions.go index 716bcb1ec..df2289aa6 100644 --- a/functions.go +++ b/functions.go @@ -6,6 +6,7 @@ import ( "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" + "v2ray.com/core/features/routing" ) // CreateObject creates a new object based on the given V2Ray instance and config. The V2Ray instance may be nil. @@ -41,7 +42,11 @@ func StartInstance(configFormat string, configBytes []byte) (*Instance, error) { // Since it is under a proxy context, the LocalAddr() and RemoteAddr() in returned net.Conn // will not show real addresses being used for communication. func Dial(ctx context.Context, v *Instance, dest net.Destination) (net.Conn, error) { - r, err := v.Dispatcher().Dispatch(ctx, dest) + dispatcher := v.GetFeature(routing.DispatcherType()) + if dispatcher == nil { + return nil, newError("routing.Dispatcher is not registered in V2Ray core") + } + r, err := dispatcher.(routing.Dispatcher).Dispatch(ctx, dest) if err != nil { return nil, err } diff --git a/network.go b/network.go deleted file mode 100644 index af0f2ddac..000000000 --- a/network.go +++ /dev/null @@ -1,143 +0,0 @@ -package core - -import ( - "context" - "sync" - - "v2ray.com/core/common" - "v2ray.com/core/features/inbound" - "v2ray.com/core/features/outbound" -) - -type syncInboundHandlerManager struct { - sync.RWMutex - inbound.Manager -} - -func (*syncInboundHandlerManager) Type() interface{} { - return inbound.ManagerType() -} - -func (m *syncInboundHandlerManager) GetHandler(ctx context.Context, tag string) (inbound.Handler, error) { - m.RLock() - defer m.RUnlock() - - if m.Manager == nil { - return nil, newError("inbound.Manager not set.").AtError() - } - - return m.Manager.GetHandler(ctx, tag) -} - -func (m *syncInboundHandlerManager) AddHandler(ctx context.Context, handler inbound.Handler) error { - m.RLock() - defer m.RUnlock() - - if m.Manager == nil { - return newError("inbound.Manager not set.").AtError() - } - - return m.Manager.AddHandler(ctx, handler) -} - -func (m *syncInboundHandlerManager) Start() error { - m.RLock() - defer m.RUnlock() - - if m.Manager == nil { - return newError("inbound.Manager not set.").AtError() - } - - return m.Manager.Start() -} - -func (m *syncInboundHandlerManager) Close() error { - m.RLock() - defer m.RUnlock() - - return common.Close(m.Manager) -} - -func (m *syncInboundHandlerManager) Set(manager inbound.Manager) { - if manager == nil { - return - } - - m.Lock() - defer m.Unlock() - - common.Close(m.Manager) // nolint: errcheck - m.Manager = manager -} - -type syncOutboundHandlerManager struct { - sync.RWMutex - outbound.Manager -} - -func (*syncOutboundHandlerManager) Type() interface{} { - return outbound.ManagerType() -} - -func (m *syncOutboundHandlerManager) GetHandler(tag string) outbound.Handler { - m.RLock() - defer m.RUnlock() - - if m.Manager == nil { - return nil - } - - return m.Manager.GetHandler(tag) -} - -func (m *syncOutboundHandlerManager) GetDefaultHandler() outbound.Handler { - m.RLock() - defer m.RUnlock() - - if m.Manager == nil { - return nil - } - - return m.Manager.GetDefaultHandler() -} - -func (m *syncOutboundHandlerManager) AddHandler(ctx context.Context, handler outbound.Handler) error { - m.RLock() - defer m.RUnlock() - - if m.Manager == nil { - return newError("OutboundHandlerManager not set.").AtError() - } - - return m.Manager.AddHandler(ctx, handler) -} - -func (m *syncOutboundHandlerManager) Start() error { - m.RLock() - defer m.RUnlock() - - if m.Manager == nil { - return newError("OutboundHandlerManager not set.").AtError() - } - - return m.Manager.Start() -} - -func (m *syncOutboundHandlerManager) Close() error { - m.RLock() - defer m.RUnlock() - - return common.Close(m.Manager) -} - -func (m *syncOutboundHandlerManager) Set(manager outbound.Manager) { - if manager == nil { - return - } - - m.Lock() - defer m.Unlock() - - common.Close(m.Manager) // nolint: errcheck - m.Manager = manager -} diff --git a/policy.go b/policy.go deleted file mode 100644 index e5d80d2df..000000000 --- a/policy.go +++ /dev/null @@ -1,74 +0,0 @@ -package core - -import ( - "sync" - "time" - - "v2ray.com/core/common" - "v2ray.com/core/features/policy" -) - -type syncPolicyManager struct { - sync.RWMutex - policy.Manager -} - -func (*syncPolicyManager) Type() interface{} { - return policy.ManagerType() -} - -func (m *syncPolicyManager) ForLevel(level uint32) policy.Session { - m.RLock() - defer m.RUnlock() - - if m.Manager == nil { - p := policy.SessionDefault() - if level == 1 { - p.Timeouts.ConnectionIdle = time.Second * 600 - } - return p - } - - return m.Manager.ForLevel(level) -} - -func (m *syncPolicyManager) ForSystem() policy.System { - m.RLock() - defer m.RUnlock() - - if m.Manager == nil { - return policy.System{} - } - - return m.Manager.ForSystem() -} - -func (m *syncPolicyManager) Start() error { - m.RLock() - defer m.RUnlock() - - if m.Manager == nil { - return nil - } - - return m.Manager.Start() -} - -func (m *syncPolicyManager) Close() error { - m.RLock() - defer m.RUnlock() - - return common.Close(m.Manager) -} - -func (m *syncPolicyManager) Set(manager policy.Manager) { - if manager == nil { - return - } - - m.Lock() - defer m.Unlock() - - common.Close(m.Manager) // nolint: errcheck - m.Manager = manager -} diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 144350c41..ee3630cc7 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -35,7 +35,7 @@ func New(ctx context.Context, config *Config) (*DokodemoDoor, error) { config: config, address: config.GetPredefinedAddress(), port: net.Port(config.Port), - policyManager: v.PolicyManager(), + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return d, nil diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 557260ee5..4baaee820 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -6,6 +6,8 @@ import ( "context" "time" + "v2ray.com/core/features" + "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" @@ -31,13 +33,16 @@ type Handler struct { // New creates a new Freedom handler. func New(ctx context.Context, config *Config) (*Handler, error) { - v := core.MustFromContext(ctx) f := &Handler{ - config: *config, - policyManager: v.PolicyManager(), - dns: v.DNSClient(), + config: *config, } + v := core.MustFromContext(ctx) + v.RequireFeatures([]interface{}{policy.ManagerType(), dns.ClientType()}, func(fs []features.Feature) { + f.policyManager = fs[0].(policy.Manager) + f.dns = fs[1].(dns.Client) + }) + return f, nil } diff --git a/proxy/http/server.go b/proxy/http/server.go index 74ecd5efc..67262e1e0 100755 --- a/proxy/http/server.go +++ b/proxy/http/server.go @@ -28,15 +28,16 @@ import ( // Server is an HTTP proxy server. type Server struct { - config *ServerConfig - v *core.Instance + config *ServerConfig + policyManager policy.Manager } // NewServer creates a new HTTP inbound handler. func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { + v := core.MustFromContext(ctx) s := &Server{ - config: config, - v: core.MustFromContext(ctx), + config: config, + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return s, nil @@ -44,7 +45,7 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { func (s *Server) policy() policy.Session { config := s.config - p := s.v.PolicyManager().ForLevel(config.UserLevel) + p := s.policyManager.ForLevel(config.UserLevel) if config.Timeout > 0 && config.UserLevel == 0 { p.Timeouts.ConnectionIdle = time.Duration(config.Timeout) * time.Second } diff --git a/proxy/mtproto/server.go b/proxy/mtproto/server.go index 3c315427a..37b43e2fc 100644 --- a/proxy/mtproto/server.go +++ b/proxy/mtproto/server.go @@ -56,7 +56,7 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { return &Server{ user: user, account: account, - policy: v.PolicyManager(), + policy: v.GetFeature(policy.ManagerType()).(policy.Manager), }, nil } diff --git a/proxy/shadowsocks/client.go b/proxy/shadowsocks/client.go index 799cc2be3..eb9e58c18 100644 --- a/proxy/shadowsocks/client.go +++ b/proxy/shadowsocks/client.go @@ -3,25 +3,25 @@ package shadowsocks import ( "context" - "v2ray.com/core/common/session" - "v2ray.com/core/common/task" - "v2ray.com/core/common/vio" - "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/retry" + "v2ray.com/core/common/session" "v2ray.com/core/common/signal" + "v2ray.com/core/common/task" + "v2ray.com/core/common/vio" + "v2ray.com/core/features/policy" "v2ray.com/core/proxy" "v2ray.com/core/transport/internet" ) // Client is a inbound handler for Shadowsocks protocol type Client struct { - serverPicker protocol.ServerPicker - v *core.Instance + serverPicker protocol.ServerPicker + policyManager policy.Manager } // NewClient create a new Shadowsocks client. @@ -37,9 +37,11 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { if serverList.Size() == 0 { return nil, newError("0 server") } + + v := core.MustFromContext(ctx) client := &Client{ - serverPicker: protocol.NewRoundRobinServerPicker(serverList), - v: core.MustFromContext(ctx), + serverPicker: protocol.NewRoundRobinServerPicker(serverList), + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return client, nil } @@ -97,7 +99,7 @@ func (c *Client) Process(ctx context.Context, link *vio.Link, dialer proxy.Diale request.Option |= RequestOptionOneTimeAuth } - sessionPolicy := c.v.PolicyManager().ForLevel(user.Level) + sessionPolicy := c.policyManager.ForLevel(user.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) diff --git a/proxy/shadowsocks/server.go b/proxy/shadowsocks/server.go index 40776035e..eb849dc9e 100644 --- a/proxy/shadowsocks/server.go +++ b/proxy/shadowsocks/server.go @@ -21,9 +21,9 @@ import ( ) type Server struct { - config ServerConfig - user *protocol.MemoryUser - v *core.Instance + config ServerConfig + user *protocol.MemoryUser + policyManager policy.Manager } // NewServer create a new Shadowsocks server. @@ -37,10 +37,11 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { return nil, newError("failed to parse user account").Base(err) } + v := core.MustFromContext(ctx) s := &Server{ - config: *config, - user: mUser, - v: core.MustFromContext(ctx), + config: *config, + user: mUser, + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return s, nil @@ -150,7 +151,7 @@ func (s *Server) handlerUDPPayload(ctx context.Context, conn internet.Connection } func (s *Server) handleConnection(ctx context.Context, conn internet.Connection, dispatcher routing.Dispatcher) error { - sessionPolicy := s.v.PolicyManager().ForLevel(s.user.Level) + sessionPolicy := s.policyManager.ForLevel(s.user.Level) conn.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)) bufferedReader := buf.BufferedReader{Reader: buf.NewReader(conn)} diff --git a/proxy/socks/client.go b/proxy/socks/client.go index a17f9951c..b1ffd341d 100644 --- a/proxy/socks/client.go +++ b/proxy/socks/client.go @@ -43,7 +43,7 @@ func NewClient(ctx context.Context, config *ClientConfig) (*Client, error) { v := core.MustFromContext(ctx) return &Client{ serverPicker: protocol.NewRoundRobinServerPicker(serverList), - policyManager: v.PolicyManager(), + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), }, nil } diff --git a/proxy/socks/server.go b/proxy/socks/server.go index 9c15d8d60..834c64047 100644 --- a/proxy/socks/server.go +++ b/proxy/socks/server.go @@ -33,7 +33,7 @@ func NewServer(ctx context.Context, config *ServerConfig) (*Server, error) { v := core.MustFromContext(ctx) s := &Server{ config: config, - policyManager: v.PolicyManager(), + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return s, nil } diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 637ac303d..98cfa078c 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -114,8 +114,8 @@ type Handler struct { func New(ctx context.Context, config *Config) (*Handler, error) { v := core.MustFromContext(ctx) handler := &Handler{ - policyManager: v.PolicyManager(), - inboundHandlerManager: v.InboundHandlerManager(), + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), + inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager), clients: vmess.NewTimedUserValidator(protocol.DefaultIDHash), detours: config.Detour, usersByEmail: newUserByEmail(config.GetDefaultValue()), diff --git a/proxy/vmess/outbound/outbound.go b/proxy/vmess/outbound/outbound.go index 896ee0d38..cd3fcd6b8 100644 --- a/proxy/vmess/outbound/outbound.go +++ b/proxy/vmess/outbound/outbound.go @@ -17,6 +17,7 @@ import ( "v2ray.com/core/common/signal" "v2ray.com/core/common/task" "v2ray.com/core/common/vio" + "v2ray.com/core/features/policy" "v2ray.com/core/proxy" "v2ray.com/core/proxy/vmess" "v2ray.com/core/proxy/vmess/encoding" @@ -25,9 +26,9 @@ import ( // Handler is an outbound connection handler for VMess protocol. type Handler struct { - serverList *protocol.ServerList - serverPicker protocol.ServerPicker - v *core.Instance + serverList *protocol.ServerList + serverPicker protocol.ServerPicker + policyManager policy.Manager } // New creates a new VMess outbound handler. @@ -40,10 +41,12 @@ func New(ctx context.Context, config *Config) (*Handler, error) { } serverList.AddServer(s) } + + v := core.MustFromContext(ctx) handler := &Handler{ - serverList: serverList, - serverPicker: protocol.NewRoundRobinServerPicker(serverList), - v: core.MustFromContext(ctx), + serverList: serverList, + serverPicker: protocol.NewRoundRobinServerPicker(serverList), + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), } return handler, nil @@ -109,7 +112,7 @@ func (v *Handler) Process(ctx context.Context, link *vio.Link, dialer proxy.Dial output := link.Writer session := encoding.NewClientSession(protocol.DefaultIDHash) - sessionPolicy := v.v.PolicyManager().ForLevel(request.User.Level) + sessionPolicy := v.policyManager.ForLevel(request.User.Level) ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) diff --git a/router.go b/router.go deleted file mode 100644 index c80589521..000000000 --- a/router.go +++ /dev/null @@ -1,111 +0,0 @@ -package core - -import ( - "context" - "sync" - - "v2ray.com/core/common" - "v2ray.com/core/common/net" - "v2ray.com/core/common/vio" - "v2ray.com/core/features/routing" -) - -type syncDispatcher struct { - sync.RWMutex - routing.Dispatcher -} - -func (*syncDispatcher) Type() interface{} { - return routing.DispatcherType() -} - -func (d *syncDispatcher) Dispatch(ctx context.Context, dest net.Destination) (*vio.Link, error) { - d.RLock() - defer d.RUnlock() - - if d.Dispatcher == nil { - return nil, newError("Dispatcher not set.").AtError() - } - - return d.Dispatcher.Dispatch(ctx, dest) -} - -func (d *syncDispatcher) Start() error { - d.RLock() - defer d.RUnlock() - - if d.Dispatcher == nil { - return newError("Dispatcher not set.").AtError() - } - - return d.Dispatcher.Start() -} - -func (d *syncDispatcher) Close() error { - d.RLock() - defer d.RUnlock() - - return common.Close(d.Dispatcher) -} - -func (d *syncDispatcher) Set(disp routing.Dispatcher) { - if disp == nil { - return - } - - d.Lock() - defer d.Unlock() - - common.Close(d.Dispatcher) // nolint: errcheck - d.Dispatcher = disp -} - -type syncRouter struct { - sync.RWMutex - routing.Router -} - -func (*syncRouter) Type() interface{} { - return routing.RouterType() -} - -func (r *syncRouter) PickRoute(ctx context.Context) (string, error) { - r.RLock() - defer r.RUnlock() - - if r.Router == nil { - return "", common.ErrNoClue - } - - return r.Router.PickRoute(ctx) -} - -func (r *syncRouter) Start() error { - r.RLock() - defer r.RUnlock() - - if r.Router == nil { - return nil - } - - return r.Router.Start() -} - -func (r *syncRouter) Close() error { - r.RLock() - defer r.RUnlock() - - return common.Close(r.Router) -} - -func (r *syncRouter) Set(router routing.Router) { - if router == nil { - return - } - - r.Lock() - defer r.Unlock() - - common.Close(r.Router) // nolint: errcheck - r.Router = router -} diff --git a/stats.go b/stats.go deleted file mode 100644 index 871408ff5..000000000 --- a/stats.go +++ /dev/null @@ -1,70 +0,0 @@ -package core - -import ( - "sync" - - "v2ray.com/core/features/stats" -) - -type syncStatManager struct { - sync.RWMutex - stats.Manager -} - -func (*syncStatManager) Type() interface{} { - return stats.ManagerType() -} - -func (s *syncStatManager) Start() error { - s.RLock() - defer s.RUnlock() - - if s.Manager == nil { - return nil - } - - return s.Manager.Start() -} - -func (s *syncStatManager) Close() error { - s.RLock() - defer s.RUnlock() - - if s.Manager == nil { - return nil - } - return s.Manager.Close() -} - -func (s *syncStatManager) RegisterCounter(name string) (stats.Counter, error) { - s.RLock() - defer s.RUnlock() - - if s.Manager == nil { - return nil, newError("StatManager not set.") - } - return s.Manager.RegisterCounter(name) -} - -func (s *syncStatManager) GetCounter(name string) stats.Counter { - s.RLock() - defer s.RUnlock() - - if s.Manager == nil { - return nil - } - return s.Manager.GetCounter(name) -} - -func (s *syncStatManager) Set(m stats.Manager) { - if m == nil { - return - } - s.Lock() - defer s.Unlock() - - if s.Manager != nil { - s.Manager.Close() // nolint: errcheck - } - s.Manager = m -} diff --git a/v2ray.go b/v2ray.go index 64ce8b240..187d40cfd 100755 --- a/v2ray.go +++ b/v2ray.go @@ -2,49 +2,71 @@ package core import ( "context" + fmt "fmt" + "reflect" "sync" "v2ray.com/core/common" "v2ray.com/core/common/serial" - "v2ray.com/core/common/uuid" "v2ray.com/core/features" "v2ray.com/core/features/dns" "v2ray.com/core/features/inbound" "v2ray.com/core/features/outbound" "v2ray.com/core/features/policy" "v2ray.com/core/features/routing" - "v2ray.com/core/features/stats" ) // Server is an instance of V2Ray. At any time, there must be at most one Server instance running. -// Deprecated. Use Instance directly. type Server interface { common.Runnable } +// ServerType returns the type of the server. +func ServerType() interface{} { + return (*Instance)(nil) +} + +type resolution struct { + deps []interface{} + callback func([]features.Feature) +} + +func getFeature(allFeatures []features.Feature, t interface{}) features.Feature { + for _, f := range allFeatures { + if f.Type() == t { + return f + } + } + return nil +} + +func (r *resolution) resolve(allFeatures []features.Feature) bool { + var fs []features.Feature + for _, d := range r.deps { + f := getFeature(allFeatures, d) + if f == nil { + return false + } + fs = append(fs, f) + } + + r.callback(fs) + return true +} + // Instance combines all functionalities in V2Ray. type Instance struct { - dnsClient syncDNSClient - policyManager syncPolicyManager - dispatcher syncDispatcher - router syncRouter - ihm syncInboundHandlerManager - ohm syncOutboundHandlerManager - stats syncStatManager - - access sync.Mutex - features []features.Feature - id uuid.UUID - running bool + access sync.Mutex + features []features.Feature + featureResolutions []resolution + running bool } // New returns a new V2Ray instance based on given configuration. // The instance is not started at this point. // To ensure V2Ray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional. func New(config *Config) (*Instance, error) { - var server = &Instance{ - id: uuid.New(), - } + var server = &Instance{} if config.Transport != nil { features.PrintDeprecatedFeatureWarning("global transport settings") @@ -58,45 +80,83 @@ func New(config *Config) (*Instance, error) { if err != nil { return nil, err } - if _, err := CreateObject(server, settings); err != nil { - return nil, err - } - } - - for _, inboundConfig := range config.Inbound { - rawHandler, err := CreateObject(server, inboundConfig) + obj, err := CreateObject(server, settings) if err != nil { return nil, err } - handler, ok := rawHandler.(inbound.Handler) - if !ok { - return nil, newError("not an InboundHandler") - } - if err := server.InboundHandlerManager().AddHandler(context.Background(), handler); err != nil { - return nil, err + if feature, ok := obj.(features.Feature); ok { + server.AddFeature(feature) } } - for _, outboundConfig := range config.Outbound { - rawHandler, err := CreateObject(server, outboundConfig) - if err != nil { - return nil, err + if server.GetFeature(dns.ClientType()) == nil { + server.AddFeature(dns.LocalClient{}) + } + + if server.GetFeature(policy.ManagerType()) == nil { + server.AddFeature(policy.DefaultManager{}) + } + + if server.GetFeature(routing.RouterType()) == nil { + server.AddFeature(routing.DefaultRouter{}) + } + + server.AddFeature(&Instance{}) + + if server.featureResolutions != nil { + fmt.Println("registered") + for _, d := range server.features { + fmt.Println(reflect.TypeOf(d.Type())) } - handler, ok := rawHandler.(outbound.Handler) - if !ok { - return nil, newError("not an OutboundHandler") + for idx, r := range server.featureResolutions { + fmt.Println(idx) + for _, d := range r.deps { + fmt.Println(reflect.TypeOf(d)) + } } - if err := server.OutboundHandlerManager().AddHandler(context.Background(), handler); err != nil { - return nil, err + return nil, newError("not all dependency are resolved.") + } + + if len(config.Inbound) > 0 { + inboundManager := server.GetFeature(inbound.ManagerType()).(inbound.Manager) + for _, inboundConfig := range config.Inbound { + rawHandler, err := CreateObject(server, inboundConfig) + if err != nil { + return nil, err + } + handler, ok := rawHandler.(inbound.Handler) + if !ok { + return nil, newError("not an InboundHandler") + } + if err := inboundManager.AddHandler(context.Background(), handler); err != nil { + return nil, err + } + } + } + + if len(config.Outbound) > 0 { + outboundManager := server.GetFeature(outbound.ManagerType()).(outbound.Manager) + for _, outboundConfig := range config.Outbound { + rawHandler, err := CreateObject(server, outboundConfig) + if err != nil { + return nil, err + } + handler, ok := rawHandler.(outbound.Handler) + if !ok { + return nil, newError("not an OutboundHandler") + } + if err := outboundManager.AddHandler(context.Background(), handler); err != nil { + return nil, err + } } } return server, nil } -// ID returns a unique ID for this V2Ray instance. -func (s *Instance) ID() uuid.UUID { - return s.id +// Type implements common.HasType. +func (s *Instance) Type() interface{} { + return ServerType() } // Close shutdown the V2Ray instance. @@ -107,7 +167,7 @@ func (s *Instance) Close() error { s.running = false var errors []interface{} - for _, f := range s.allFeatures() { + for _, f := range s.features { if err := f.Close(); err != nil { errors = append(errors, err) } @@ -119,6 +179,51 @@ func (s *Instance) Close() error { return nil } +// RequireFeatures registers a callback, which will be called when all dependent features are registered. +func (s *Instance) RequireFeatures(featureTypes []interface{}, callback func([]features.Feature)) { + r := resolution{ + deps: featureTypes, + callback: callback, + } + if r.resolve(s.features) { + return + } + s.featureResolutions = append(s.featureResolutions, r) +} + +// AddFeature registers a feature into current Instance. +func (s *Instance) AddFeature(feature features.Feature) { + s.features = append(s.features, feature) + + if s.running { + if err := feature.Start(); err != nil { + newError("failed to start feature").Base(err).WriteToLog() + } + return + } + + if s.featureResolutions == nil { + return + } + + var pendingResolutions []resolution + for _, r := range s.featureResolutions { + if !r.resolve(s.features) { + pendingResolutions = append(pendingResolutions, r) + } + } + if len(pendingResolutions) == 0 { + s.featureResolutions = nil + } else if len(pendingResolutions) < len(s.featureResolutions) { + s.featureResolutions = pendingResolutions + } +} + +// GetFeature returns a feature of the given type, or nil if such feature is not registered. +func (s *Instance) GetFeature(featureType interface{}) features.Feature { + return getFeature(s.features, featureType) +} + // Start starts the V2Ray instance, including all registered features. When Start returns error, the state of the instance is unknown. // A V2Ray instance can be started only once. Upon closing, the instance is not guaranteed to start again. func (s *Instance) Start() error { @@ -126,7 +231,7 @@ func (s *Instance) Start() error { defer s.access.Unlock() s.running = true - for _, f := range s.allFeatures() { + for _, f := range s.features { if err := f.Start(); err != nil { return err } @@ -136,104 +241,3 @@ func (s *Instance) Start() error { return nil } - -// RegisterFeature registers the given feature into V2Ray. -// If feature is one of the following types, the corresponding feature in this Instance -// will be replaced: DNSClient, PolicyManager, Router, Dispatcher, InboundHandlerManager, OutboundHandlerManager. -func (s *Instance) RegisterFeature(instance features.Feature) error { - running := false - - switch instance.Type().(type) { - case dns.Client, *dns.Client: - s.dnsClient.Set(instance.(dns.Client)) - case policy.Manager, *policy.Manager: - s.policyManager.Set(instance.(policy.Manager)) - case routing.Router, *routing.Router: - s.router.Set(instance.(routing.Router)) - case routing.Dispatcher, *routing.Dispatcher: - s.dispatcher.Set(instance.(routing.Dispatcher)) - case inbound.Manager, *inbound.Manager: - s.ihm.Set(instance.(inbound.Manager)) - case outbound.Manager, *outbound.Manager: - s.ohm.Set(instance.(outbound.Manager)) - case stats.Manager, *stats.Manager: - s.stats.Set(instance.(stats.Manager)) - default: - s.access.Lock() - s.features = append(s.features, instance) - running = s.running - s.access.Unlock() - } - - if running { - return instance.Start() - } - return nil -} - -func (s *Instance) allFeatures() []features.Feature { - return append([]features.Feature{s.DNSClient(), s.PolicyManager(), s.Dispatcher(), s.Router(), s.InboundHandlerManager(), s.OutboundHandlerManager(), s.Stats()}, s.features...) -} - -// GetFeature returns a feature that was registered in this Instance. Nil if not found. -// The returned Feature must implement common.HasType and whose type equals to the given feature type. -func (s *Instance) GetFeature(featureType interface{}) features.Feature { - switch featureType.(type) { - case dns.Client, *dns.Client: - return s.DNSClient() - case policy.Manager, *policy.Manager: - return s.PolicyManager() - case routing.Router, *routing.Router: - return s.Router() - case routing.Dispatcher, *routing.Dispatcher: - return s.Dispatcher() - case inbound.Manager, *inbound.Manager: - return s.InboundHandlerManager() - case outbound.Manager, *outbound.Manager: - return s.OutboundHandlerManager() - case stats.Manager, *stats.Manager: - return s.Stats() - default: - for _, f := range s.features { - if f.Type() == featureType { - return f - } - } - return nil - } -} - -// DNSClient returns the dns.Client used by this Instance. The returned dns.Client is always functional. -func (s *Instance) DNSClient() dns.Client { - return &(s.dnsClient) -} - -// PolicyManager returns the policy.Manager used by this Instance. The returned policy.Manager is always functional. -func (s *Instance) PolicyManager() policy.Manager { - return &(s.policyManager) -} - -// Router returns the Router used by this Instance. The returned Router is always functional. -func (s *Instance) Router() routing.Router { - return &(s.router) -} - -// Dispatcher returns the Dispatcher used by this Instance. If Dispatcher was not registered before, the returned value doesn't work, although it is not nil. -func (s *Instance) Dispatcher() routing.Dispatcher { - return &(s.dispatcher) -} - -// InboundHandlerManager returns the InboundHandlerManager used by this Instance. If InboundHandlerManager was not registered before, the returned value doesn't work. -func (s *Instance) InboundHandlerManager() inbound.Manager { - return &(s.ihm) -} - -// OutboundHandlerManager returns the OutboundHandlerManager used by this Instance. If OutboundHandlerManager was not registered before, the returned value doesn't work. -func (s *Instance) OutboundHandlerManager() outbound.Manager { - return &(s.ohm) -} - -// Stats returns the stats.Manager used by this Instance. If StatManager was not registered before, the returned value doesn't work. -func (s *Instance) Stats() stats.Manager { - return &(s.stats) -}