initializable apps

pull/168/head
v2ray 2016-05-18 08:12:04 -07:00
parent 7765fedd78
commit 3ded18a75b
12 changed files with 188 additions and 65 deletions

View File

@ -18,22 +18,36 @@ type DefaultDispatcher struct {
} }
func NewDefaultDispatcher(space app.Space) *DefaultDispatcher { 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) { if !space.HasApp(proxyman.APP_ID_OUTBOUND_MANAGER) {
log.Error("DefaultDispatcher: OutboundHandlerManager is not found in the space.") 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) { if !space.HasApp(dns.APP_ID) {
log.Error("DefaultDispatcher: DNS is not found in the space.") log.Error("DefaultDispatcher: DNS is not found in the space.")
return nil return app.ErrorMissingApplication
}
d := &DefaultDispatcher{
ohm: space.GetApp(proxyman.APP_ID_OUTBOUND_MANAGER).(proxyman.OutboundHandlerManager),
dns: space.GetApp(dns.APP_ID).(dns.Server),
} }
this.dns = space.GetApp(dns.APP_ID).(dns.Server)
if space.HasApp(router.APP_ID) { 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 { func (this *DefaultDispatcher) DispatchToOutbound(destination v2net.Destination) ray.InboundRay {

View File

@ -22,6 +22,7 @@ type DomainRecord struct {
type CacheServer struct { type CacheServer struct {
sync.RWMutex sync.RWMutex
space app.Space
records map[string]*DomainRecord records map[string]*DomainRecord
servers []NameServer servers []NameServer
} }
@ -31,17 +32,29 @@ func NewCacheServer(space app.Space, config *Config) *CacheServer {
records: make(map[string]*DomainRecord), records: make(map[string]*DomainRecord),
servers: make([]NameServer, len(config.NameServers)), servers: make([]NameServer, len(config.NameServers)),
} }
dispatcher := space.GetApp(dispatcher.APP_ID).(dispatcher.PacketDispatcher) space.InitializeApplication(func() error {
for idx, ns := range config.NameServers { if !space.HasApp(dispatcher.APP_ID) {
if ns.Address().IsDomain() && ns.Address().Domain() == "localhost" { log.Error("DNS: Dispatcher is not found in the space.")
server.servers[idx] = &LocalNameServer{} return app.ErrorMissingApplication
} else {
server.servers[idx] = NewUDPNameServer(ns, dispatcher)
} }
}
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 return server
} }
func (this *CacheServer) Release() {
}
//@Private //@Private
func (this *CacheServer) GetCached(domain string) []net.IP { func (this *CacheServer) GetCached(domain string) []net.IP {
this.RLock() this.RLock()

View File

@ -21,10 +21,9 @@ func TestDnsAdd(t *testing.T) {
space := app.NewSpace() space := app.NewSpace()
outboundHandlerManager := &proxyman.DefaultOutboundHandlerManager{} outboundHandlerManager := proxyman.NewDefaultOutboundHandlerManager()
outboundHandlerManager.SetDefaultHandler(&freedom.FreedomConnection{}) outboundHandlerManager.SetDefaultHandler(&freedom.FreedomConnection{})
space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager) space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager)
space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space)) space.BindApp(dispatcher.APP_ID, dispatchers.NewDefaultDispatcher(space))
domain := "local.v2ray.com" 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)), v2net.UDPDestination(v2net.IPAddress([]byte{8, 8, 8, 8}), v2net.Port(53)),
}, },
}) })
space.BindApp(APP_ID, server)
space.Initialize()
ips := server.Get(domain) ips := server.Get(domain)
assert.Int(len(ips)).Equals(1) assert.Int(len(ips)).Equals(1)
netassert.IP(ips[0].To4()).Equals(net.IP([]byte{127, 0, 0, 1})) netassert.IP(ips[0].To4()).Equals(net.IP([]byte{127, 0, 0, 1}))

View File

@ -27,6 +27,16 @@ type DefaultOutboundHandlerManager struct {
taggedHandler map[string]proxy.OutboundHandler 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 { func (this *DefaultOutboundHandlerManager) GetDefaultHandler() proxy.OutboundHandler {
this.RLock() this.RLock()
defer this.RUnlock() defer this.RUnlock()

View File

@ -2,6 +2,7 @@ package router
import ( import (
"github.com/v2ray/v2ray-core/app" "github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/common"
v2net "github.com/v2ray/v2ray-core/common/net" v2net "github.com/v2ray/v2ray-core/common/net"
) )
@ -10,6 +11,7 @@ const (
) )
type Router interface { type Router interface {
common.Releasable
TakeDetour(v2net.Destination) (string, error) TakeDetour(v2net.Destination) (string, error)
} }

View File

@ -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")
}

View File

@ -41,23 +41,34 @@ func (this *cacheEntry) Extend() {
} }
type Router struct { type Router struct {
config *RouterRuleConfig config *RouterRuleConfig
cache *collect.ValidityMap cache *collect.ValidityMap
space app.Space dnsServer dns.Server
} }
func NewRouter(config *RouterRuleConfig, space app.Space) *Router { func NewRouter(config *RouterRuleConfig, space app.Space) *Router {
return &Router{ r := &Router{
config: config, config: config,
cache: collect.NewValidityMap(3600), 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 // @Private
func (this *Router) ResolveIP(dest v2net.Destination) []v2net.Destination { func (this *Router) ResolveIP(dest v2net.Destination) []v2net.Destination {
dnsServer := this.space.GetApp(dns.APP_ID).(dns.Server) ips := this.dnsServer.Get(dest.Address().Domain())
ips := dnsServer.Get(dest.Address().Domain())
if len(ips) == 0 { if len(ips) == 0 {
return nil return nil
} }

View File

@ -3,6 +3,12 @@ package rules_test
import ( import (
"testing" "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" . "github.com/v2ray/v2ray-core/app/router/rules"
v2net "github.com/v2ray/v2ray-core/common/net" v2net "github.com/v2ray/v2ray-core/common/net"
v2testing "github.com/v2ray/v2ray-core/testing" 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.Error(err).IsNil()
assert.StringLiteral(tag).Equals("test") assert.StringLiteral(tag).Equals("test")
} }

View File

@ -1,5 +1,16 @@
package app 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 type ID int
// Context of a function call from proxy to app. // Context of a function call from proxy to app.
@ -11,30 +22,68 @@ type Caller interface {
Tag() string Tag() string
} }
type Application interface {
common.Releasable
}
type ApplicationInitializer func() error
// A Space contains all apps that may be available in a V2Ray runtime. // 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. // Caller must check the availability of an app by calling HasXXX before getting its instance.
type Space interface { type Space interface {
Initialize() error
InitializeApplication(ApplicationInitializer)
HasApp(ID) bool HasApp(ID) bool
GetApp(ID) interface{} GetApp(ID) Application
BindApp(ID, interface{}) BindApp(ID, Application)
} }
type spaceImpl struct { type spaceImpl struct {
cache map[ID]interface{} cache map[ID]Application
initSignal chan struct{}
initErrors chan error
appsToInit int32
appsDone int32
} }
func NewSpace() Space { func NewSpace() Space {
return &spaceImpl{ 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 { func (this *spaceImpl) HasApp(id ID) bool {
_, found := this.cache[id] _, found := this.cache[id]
return found return found
} }
func (this *spaceImpl) GetApp(id ID) interface{} { func (this *spaceImpl) GetApp(id ID) Application {
obj, found := this.cache[id] obj, found := this.cache[id]
if !found { if !found {
return nil return nil
@ -42,6 +91,6 @@ func (this *spaceImpl) GetApp(id ID) interface{} {
return obj return obj
} }
func (this *spaceImpl) BindApp(id ID, object interface{}) { func (this *spaceImpl) BindApp(id ID, application Application) {
this.cache[id] = object this.cache[id] = application
} }

View File

@ -10,6 +10,8 @@ import (
"golang.org/x/net/proxy" "golang.org/x/net/proxy"
"github.com/v2ray/v2ray-core/app" "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" v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
v2proxy "github.com/v2ray/v2ray-core/proxy" v2proxy "github.com/v2ray/v2ray-core/proxy"
proxytesting "github.com/v2ray/v2ray-core/proxy/testing" proxytesting "github.com/v2ray/v2ray-core/proxy/testing"
@ -44,6 +46,11 @@ func TestSocksTcpConnect(t *testing.T) {
"auth": "noauth" "auth": "noauth"
}`), }`),
}, },
DNSConfig: &dns.Config{
NameServers: []v2net.Destination{
v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)),
},
},
OutboundConfig: &point.ConnectionConfig{ OutboundConfig: &point.ConnectionConfig{
Protocol: protocol, Protocol: protocol,
Settings: nil, 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{ OutboundConfig: &point.ConnectionConfig{
Protocol: protocol, Protocol: protocol,
Settings: nil, 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{ OutboundConfig: &point.ConnectionConfig{
Protocol: protocol, Protocol: protocol,
Settings: nil, 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{ OutboundConfig: &point.ConnectionConfig{
Protocol: protocol, Protocol: protocol,
Settings: nil, Settings: nil,

View File

@ -6,6 +6,7 @@ import (
"github.com/v2ray/v2ray-core/app" "github.com/v2ray/v2ray-core/app"
"github.com/v2ray/v2ray-core/app/dispatcher" "github.com/v2ray/v2ray-core/app/dispatcher"
"github.com/v2ray/v2ray-core/app/dns"
v2net "github.com/v2ray/v2ray-core/common/net" v2net "github.com/v2ray/v2ray-core/common/net"
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing" v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
"github.com/v2ray/v2ray-core/common/protocol" "github.com/v2ray/v2ray-core/common/protocol"
@ -46,6 +47,11 @@ func TestVMessInAndOut(t *testing.T) {
configA := &point.Config{ configA := &point.Config{
Port: portA, Port: portA,
DNSConfig: &dns.Config{
NameServers: []v2net.Destination{
v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)),
},
},
InboundConfig: &point.ConnectionConfig{ InboundConfig: &point.ConnectionConfig{
Protocol: protocol, Protocol: protocol,
Settings: nil, Settings: nil,
@ -86,6 +92,11 @@ func TestVMessInAndOut(t *testing.T) {
configB := &point.Config{ configB := &point.Config{
Port: portB, Port: portB,
DNSConfig: &dns.Config{
NameServers: []v2net.Destination{
v2net.UDPDestination(v2net.DomainAddress("localhost"), v2net.Port(53)),
},
},
InboundConfig: &point.ConnectionConfig{ InboundConfig: &point.ConnectionConfig{
Protocol: "vmess", Protocol: "vmess",
Settings: []byte(`{ Settings: []byte(`{

View File

@ -58,7 +58,7 @@ func NewPoint(pConfig *Config) (*Point, error) {
vpoint.space = app.NewSpace() vpoint.space = app.NewSpace()
vpoint.space.BindApp(proxyman.APP_ID_INBOUND_MANAGER, vpoint) vpoint.space.BindApp(proxyman.APP_ID_INBOUND_MANAGER, vpoint)
outboundHandlerManager := &proxyman.DefaultOutboundHandlerManager{} outboundHandlerManager := proxyman.NewDefaultOutboundHandlerManager()
vpoint.space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager) vpoint.space.BindApp(proxyman.APP_ID_OUTBOUND_MANAGER, outboundHandlerManager)
dnsConfig := pConfig.DNSConfig 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 return vpoint, nil
} }
@ -192,3 +196,7 @@ func (this *Point) GetHandler(tag string) (proxy.InboundHandler, int) {
} }
return handler.GetConnectionHandler() return handler.GetConnectionHandler()
} }
func (this *Point) Release() {
}