From 3ded18a75bde880141d34d59c5355eec13da2a3f Mon Sep 17 00:00:00 2001 From: v2ray Date: Wed, 18 May 2016 08:12:04 -0700 Subject: [PATCH] initializable apps --- app/dispatcher/impl/default.go | 30 +++++++++++----- app/dns/server.go | 27 ++++++++++---- app/dns/server_test.go | 6 ++-- app/proxyman/proxyman.go | 10 ++++++ app/router/router.go | 2 ++ app/router/router_test.go | 31 ---------------- app/router/rules/router.go | 25 +++++++++---- app/router/rules/router_test.go | 16 +++++++-- app/space.go | 63 +++++++++++++++++++++++++++++---- proxy/socks/server_test.go | 22 ++++++++++++ proxy/vmess/vmess_test.go | 11 ++++++ shell/point/point.go | 10 +++++- 12 files changed, 188 insertions(+), 65 deletions(-) delete mode 100644 app/router/router_test.go diff --git a/app/dispatcher/impl/default.go b/app/dispatcher/impl/default.go index a5435bd5..67e845ff 100644 --- a/app/dispatcher/impl/default.go +++ b/app/dispatcher/impl/default.go @@ -18,22 +18,36 @@ type DefaultDispatcher struct { } func NewDefaultDispatcher(space app.Space) *DefaultDispatcher { + d := &DefaultDispatcher{} + space.InitializeApplication(func() error { + return d.Initialize(space) + }) + return d +} + +// @Private +func (this *DefaultDispatcher) Initialize(space app.Space) error { if !space.HasApp(proxyman.APP_ID_OUTBOUND_MANAGER) { log.Error("DefaultDispatcher: OutboundHandlerManager is not found in the space.") - return nil + return app.ErrorMissingApplication } + this.ohm = space.GetApp(proxyman.APP_ID_OUTBOUND_MANAGER).(proxyman.OutboundHandlerManager) + if !space.HasApp(dns.APP_ID) { log.Error("DefaultDispatcher: DNS is not found in the space.") - return nil - } - d := &DefaultDispatcher{ - ohm: space.GetApp(proxyman.APP_ID_OUTBOUND_MANAGER).(proxyman.OutboundHandlerManager), - dns: space.GetApp(dns.APP_ID).(dns.Server), + return app.ErrorMissingApplication } + this.dns = space.GetApp(dns.APP_ID).(dns.Server) + if space.HasApp(router.APP_ID) { - d.router = space.GetApp(router.APP_ID).(router.Router) + this.router = space.GetApp(router.APP_ID).(router.Router) } - return d + + return nil +} + +func (this *DefaultDispatcher) Release() { + } func (this *DefaultDispatcher) DispatchToOutbound(destination v2net.Destination) ray.InboundRay { diff --git a/app/dns/server.go b/app/dns/server.go index c3078b79..0265b609 100644 --- a/app/dns/server.go +++ b/app/dns/server.go @@ -22,6 +22,7 @@ type DomainRecord struct { type CacheServer struct { sync.RWMutex + space app.Space records map[string]*DomainRecord servers []NameServer } @@ -31,17 +32,29 @@ func NewCacheServer(space app.Space, config *Config) *CacheServer { records: make(map[string]*DomainRecord), servers: make([]NameServer, len(config.NameServers)), } - dispatcher := space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher) - for idx, ns := range config.NameServers { - if ns.Address().IsDomain() && ns.Address().Domain() == "localhost" { - server.servers[idx] = &LocalNameServer{} - } else { - server.servers[idx] = NewUDPNameServer(ns, dispatcher) + space.InitializeApplication(func() error { + if !space.HasApp(dispatcher.APP_ID) { + log.Error("DNS: Dispatcher is not found in the space.") + return app.ErrorMissingApplication } - } + + dispatcher := space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher) + for idx, ns := range config.NameServers { + if ns.Address().IsDomain() && ns.Address().Domain() == "localhost" { + server.servers[idx] = &LocalNameServer{} + } else { + server.servers[idx] = NewUDPNameServer(ns, dispatcher) + } + } + return nil + }) return server } +func (this *CacheServer) Release() { + +} + //@Private func (this *CacheServer) GetCached(domain string) []net.IP { this.RLock() diff --git a/app/dns/server_test.go b/app/dns/server_test.go index 5eb75a5f..9df015e6 100644 --- a/app/dns/server_test.go +++ b/app/dns/server_test.go @@ -21,10 +21,9 @@ func TestDnsAdd(t *testing.T) { space := app.NewSpace() - outboundHandlerManager := &proxyman.DefaultOutboundHandlerManager{} + outboundHandlerManager := proxyman.NewDefaultOutboundHandlerManager() outboundHandlerManager.SetDefaultHandler(&freedom.FreedomConnection{}) space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager) - space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space)) domain := "local.v2ray.com" @@ -33,6 +32,9 @@ func TestDnsAdd(t *testing.T) { v2net.UDPDestination(v2net.IPAddress([]byte{8, 8, 8, 8}), v2net.Port(53)), }, }) + space.BindApp(APP_ID, server) + space.Initialize() + ips := server.Get(domain) assert.Int(len(ips)).Equals(1) netassert.IP(ips[0].To4()).Equals(net.IP([]byte{127, 0, 0, 1})) diff --git a/app/proxyman/proxyman.go b/app/proxyman/proxyman.go index 831e19ac..63dca63a 100644 --- a/app/proxyman/proxyman.go +++ b/app/proxyman/proxyman.go @@ -27,6 +27,16 @@ type DefaultOutboundHandlerManager struct { taggedHandler map[string]proxy.OutboundHandler } +func NewDefaultOutboundHandlerManager() *DefaultOutboundHandlerManager { + return &DefaultOutboundHandlerManager{ + taggedHandler: make(map[string]proxy.OutboundHandler), + } +} + +func (this *DefaultOutboundHandlerManager) Release() { + +} + func (this *DefaultOutboundHandlerManager) GetDefaultHandler() proxy.OutboundHandler { this.RLock() defer this.RUnlock() diff --git a/app/router/router.go b/app/router/router.go index b3a10174..cd670eac 100644 --- a/app/router/router.go +++ b/app/router/router.go @@ -2,6 +2,7 @@ package router import ( "github.com/v2ray/v2ray-core/app" + "github.com/v2ray/v2ray-core/common" v2net "github.com/v2ray/v2ray-core/common/net" ) @@ -10,6 +11,7 @@ const ( ) type Router interface { + common.Releasable TakeDetour(v2net.Destination) (string, error) } diff --git a/app/router/router_test.go b/app/router/router_test.go deleted file mode 100644 index e70e039a..00000000 --- a/app/router/router_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package router_test - -import ( - "net" - "path/filepath" - "testing" - - . "github.com/v2ray/v2ray-core/app/router" - _ "github.com/v2ray/v2ray-core/app/router/rules" - v2net "github.com/v2ray/v2ray-core/common/net" - "github.com/v2ray/v2ray-core/shell/point" - v2testing "github.com/v2ray/v2ray-core/testing" - "github.com/v2ray/v2ray-core/testing/assert" -) - -func TestRouter(t *testing.T) { - v2testing.Current(t) - - baseDir := "$GOPATH/src/github.com/v2ray/v2ray-core/release/config" - - pointConfig, err := point.LoadConfig(filepath.Join(baseDir, "vpoint_socks_vmess.json")) - assert.Error(err).IsNil() - - router, err := CreateRouter(pointConfig.RouterConfig.Strategy, pointConfig.RouterConfig.Settings, nil) - assert.Error(err).IsNil() - - dest := v2net.TCPDestination(v2net.IPAddress(net.ParseIP("120.135.126.1")), 80) - tag, err := router.TakeDetour(dest) - assert.Error(err).IsNil() - assert.StringLiteral(tag).Equals("direct") -} diff --git a/app/router/rules/router.go b/app/router/rules/router.go index 60e3a5fe..cbe3a8ea 100644 --- a/app/router/rules/router.go +++ b/app/router/rules/router.go @@ -41,23 +41,34 @@ func (this *cacheEntry) Extend() { } type Router struct { - config *RouterRuleConfig - cache *collect.ValidityMap - space app.Space + config *RouterRuleConfig + cache *collect.ValidityMap + dnsServer dns.Server } func NewRouter(config *RouterRuleConfig, space app.Space) *Router { - return &Router{ + r := &Router{ config: config, cache: collect.NewValidityMap(3600), - space: space, } + space.InitializeApplication(func() error { + if !space.HasApp(dns.APP_ID) { + log.Error("DNS: Router is not found in the space.") + return app.ErrorMissingApplication + } + r.dnsServer = space.GetApp(dns.APP_ID).(dns.Server) + return nil + }) + return r +} + +func (this *Router) Release() { + } // @Private func (this *Router) ResolveIP(dest v2net.Destination) []v2net.Destination { - dnsServer := this.space.GetApp(dns.APP_ID).(dns.Server) - ips := dnsServer.Get(dest.Address().Domain()) + ips := this.dnsServer.Get(dest.Address().Domain()) if len(ips) == 0 { return nil } diff --git a/app/router/rules/router_test.go b/app/router/rules/router_test.go index e8f72e02..0b369fb2 100644 --- a/app/router/rules/router_test.go +++ b/app/router/rules/router_test.go @@ -3,6 +3,12 @@ package rules_test import ( "testing" + "github.com/v2ray/v2ray-core/app" + "github.com/v2ray/v2ray-core/app/dispatcher" + dispatchers "github.com/v2ray/v2ray-core/app/dispatcher/impl" + "github.com/v2ray/v2ray-core/app/dns" + "github.com/v2ray/v2ray-core/app/proxyman" + "github.com/v2ray/v2ray-core/app/router" . "github.com/v2ray/v2ray-core/app/router/rules" v2net "github.com/v2ray/v2ray-core/common/net" v2testing "github.com/v2ray/v2ray-core/testing" @@ -21,9 +27,15 @@ func TestSimpleRouter(t *testing.T) { }, } - router := NewRouter(config, nil) + space := app.NewSpace() + space.BindApp(dns.APP_ID, dns.NewCacheServer(space, &dns.Config{})) + space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space)) + space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, proxyman.NewDefaultOutboundHandlerManager()) + r := NewRouter(config, space) + space.BindApp(router.APP_ID, r) + assert.Error(space.Initialize()).IsNil() - tag, err := router.TakeDetour(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80)) + tag, err := r.TakeDetour(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80)) assert.Error(err).IsNil() assert.StringLiteral(tag).Equals("test") } diff --git a/app/space.go b/app/space.go index 43cdbb48..0ac950d8 100644 --- a/app/space.go +++ b/app/space.go @@ -1,5 +1,16 @@ package app +import ( + "errors" + "sync/atomic" + + "github.com/v2ray/v2ray-core/common" +) + +var ( + ErrorMissingApplication = errors.New("App: Failed to found one or more applications.") +) + type ID int // Context of a function call from proxy to app. @@ -11,30 +22,68 @@ type Caller interface { Tag() string } +type Application interface { + common.Releasable +} + +type ApplicationInitializer func() error + // A Space contains all apps that may be available in a V2Ray runtime. // Caller must check the availability of an app by calling HasXXX before getting its instance. type Space interface { + Initialize() error + InitializeApplication(ApplicationInitializer) + HasApp(ID) bool - GetApp(ID) interface{} - BindApp(ID, interface{}) + GetApp(ID) Application + BindApp(ID, Application) } type spaceImpl struct { - cache map[ID]interface{} + cache map[ID]Application + initSignal chan struct{} + initErrors chan error + appsToInit int32 + appsDone int32 } func NewSpace() Space { return &spaceImpl{ - cache: make(map[ID]interface{}), + cache: make(map[ID]Application), + initSignal: make(chan struct{}), + initErrors: make(chan error, 1), } } +func (this *spaceImpl) InitializeApplication(f ApplicationInitializer) { + atomic.AddInt32(&(this.appsToInit), 1) + go func() { + <-this.initSignal + err := f() + if err != nil { + this.initErrors <- err + } + count := atomic.AddInt32(&(this.appsDone), 1) + if count == this.appsToInit { + close(this.initErrors) + } + }() +} + +func (this *spaceImpl) Initialize() error { + close(this.initSignal) + if err, open := <-this.initErrors; open { + return err + } + return nil +} + func (this *spaceImpl) HasApp(id ID) bool { _, found := this.cache[id] return found } -func (this *spaceImpl) GetApp(id ID) interface{} { +func (this *spaceImpl) GetApp(id ID) Application { obj, found := this.cache[id] if !found { return nil @@ -42,6 +91,6 @@ func (this *spaceImpl) GetApp(id ID) interface{} { return obj } -func (this *spaceImpl) BindApp(id ID, object interface{}) { - this.cache[id] = object +func (this *spaceImpl) BindApp(id ID, application Application) { + this.cache[id] = application } diff --git a/proxy/socks/server_test.go b/proxy/socks/server_test.go index cb6ab9b4..224c4463 100644 --- a/proxy/socks/server_test.go +++ b/proxy/socks/server_test.go @@ -10,6 +10,8 @@ import ( "golang.org/x/net/proxy" "github.com/v2ray/v2ray-core/app" + "github.com/v2ray/v2ray-core/app/dns" + v2net "github.com/v2ray/v2ray-core/common/net" v2nettesting "github.com/v2ray/v2ray-core/common/net/testing" v2proxy "github.com/v2ray/v2ray-core/proxy" proxytesting "github.com/v2ray/v2ray-core/proxy/testing" @@ -44,6 +46,11 @@ func TestSocksTcpConnect(t *testing.T) { "auth": "noauth" }`), }, + DNSConfig: &dns.Config{ + NameServers: []v2net.Destination{ + v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), + }, + }, OutboundConfig: &point.ConnectionConfig{ Protocol: protocol, Settings: nil, @@ -106,6 +113,11 @@ func TestSocksTcpConnectWithUserPass(t *testing.T) { ] }`), }, + DNSConfig: &dns.Config{ + NameServers: []v2net.Destination{ + v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), + }, + }, OutboundConfig: &point.ConnectionConfig{ Protocol: protocol, Settings: nil, @@ -168,6 +180,11 @@ func TestSocksTcpConnectWithWrongUserPass(t *testing.T) { ] }`), }, + DNSConfig: &dns.Config{ + NameServers: []v2net.Destination{ + v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), + }, + }, OutboundConfig: &point.ConnectionConfig{ Protocol: protocol, Settings: nil, @@ -216,6 +233,11 @@ func TestSocksTcpConnectWithWrongAuthMethod(t *testing.T) { ] }`), }, + DNSConfig: &dns.Config{ + NameServers: []v2net.Destination{ + v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), + }, + }, OutboundConfig: &point.ConnectionConfig{ Protocol: protocol, Settings: nil, diff --git a/proxy/vmess/vmess_test.go b/proxy/vmess/vmess_test.go index af074da6..326f677e 100644 --- a/proxy/vmess/vmess_test.go +++ b/proxy/vmess/vmess_test.go @@ -6,6 +6,7 @@ import ( "github.com/v2ray/v2ray-core/app" "github.com/v2ray/v2ray-core/app/dispatcher" + "github.com/v2ray/v2ray-core/app/dns" v2net "github.com/v2ray/v2ray-core/common/net" v2nettesting "github.com/v2ray/v2ray-core/common/net/testing" "github.com/v2ray/v2ray-core/common/protocol" @@ -46,6 +47,11 @@ func TestVMessInAndOut(t *testing.T) { configA := &point.Config{ Port: portA, + DNSConfig: &dns.Config{ + NameServers: []v2net.Destination{ + v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), + }, + }, InboundConfig: &point.ConnectionConfig{ Protocol: protocol, Settings: nil, @@ -86,6 +92,11 @@ func TestVMessInAndOut(t *testing.T) { configB := &point.Config{ Port: portB, + DNSConfig: &dns.Config{ + NameServers: []v2net.Destination{ + v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)), + }, + }, InboundConfig: &point.ConnectionConfig{ Protocol: "vmess", Settings: []byte(`{ diff --git a/shell/point/point.go b/shell/point/point.go index b66ec058..b8deb5cc 100644 --- a/shell/point/point.go +++ b/shell/point/point.go @@ -58,7 +58,7 @@ func NewPoint(pConfig *Config) (*Point, error) { vpoint.space = app.NewSpace() vpoint.space.BindApp(proxyman.APP_ID_INBOUND_MANAGER, vpoint) - outboundHandlerManager := &proxyman.DefaultOutboundHandlerManager{} + outboundHandlerManager := proxyman.NewDefaultOutboundHandlerManager() vpoint.space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager) dnsConfig := pConfig.DNSConfig @@ -144,6 +144,10 @@ func NewPoint(pConfig *Config) (*Point, error) { } } + if err := vpoint.space.Initialize(); err != nil { + return nil, err + } + return vpoint, nil } @@ -192,3 +196,7 @@ func (this *Point) GetHandler(tag string) (proxy.InboundHandler, int) { } return handler.GetConnectionHandler() } + +func (this *Point) Release() { + +}