Add OptionalFeatures injection

For example OptionalFeatures() is useful for fakedns module
pull/4206/head
yuhan6665 2024-12-25 23:37:34 -05:00
parent 9c62017e96
commit 377a41844f
8 changed files with 47 additions and 20 deletions

View File

@ -106,7 +106,7 @@ func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
d := new(DefaultDispatcher) d := new(DefaultDispatcher)
if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error { if err := core.RequireFeatures(ctx, func(om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dc dns.Client) error {
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) { // FakeDNSEngine is optional core.OptionalFeatures(ctx, func(fdns dns.FakeDNSEngine) {
d.fdns = fdns d.fdns = fdns
}) })
return d.Init(config.(*Config), om, router, pm, sm, dc) return d.Init(config.(*Config), om, router, pm, sm, dc)

View File

@ -56,7 +56,7 @@ func NewServer(ctx context.Context, dest net.Destination, dispatcher routing.Dis
return NewTCPLocalNameServer(u, queryStrategy) return NewTCPLocalNameServer(u, queryStrategy)
case strings.EqualFold(u.String(), "fakedns"): case strings.EqualFold(u.String(), "fakedns"):
var fd dns.FakeDNSEngine var fd dns.FakeDNSEngine
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) { // FakeDNSEngine is optional core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) {
fd = fdns fd = fdns
}) })
return NewFakeDNSServer(fd), nil return NewFakeDNSServer(fd), nil

View File

@ -38,7 +38,7 @@ func init() {
sv := &service{v: s} sv := &service{v: s}
err := s.RequireFeatures(func(Observatory extension.Observatory) { err := s.RequireFeatures(func(Observatory extension.Observatory) {
sv.observatory = Observatory sv.observatory = Observatory
}) }, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -177,7 +177,7 @@ func (s *service) Register(server *grpc.Server) {
common.Must(s.v.RequireFeatures(func(im inbound.Manager, om outbound.Manager) { common.Must(s.v.RequireFeatures(func(im inbound.Manager, om outbound.Manager) {
hs.ihm = im hs.ihm = im
hs.ohm = om hs.ohm = om
})) }, false))
RegisterHandlerServiceServer(server, hs) RegisterHandlerServiceServer(server, hs)
// For compatibility purposes // For compatibility purposes

View File

@ -135,7 +135,7 @@ func (s *service) Register(server *grpc.Server) {
vCoreDesc := RoutingService_ServiceDesc vCoreDesc := RoutingService_ServiceDesc
vCoreDesc.ServiceName = "v2ray.core.app.router.command.RoutingService" vCoreDesc.ServiceName = "v2ray.core.app.router.command.RoutingService"
server.RegisterService(&vCoreDesc, rs) server.RegisterService(&vCoreDesc, rs)
})) }, false))
} }
func init() { func init() {

View File

@ -83,6 +83,7 @@ type Instance struct {
statusLock sync.Mutex statusLock sync.Mutex
features []features.Feature features []features.Feature
pendingResolutions []resolution pendingResolutions []resolution
pendingOptionalResolutions []resolution
running bool running bool
resolveLock sync.Mutex resolveLock sync.Mutex
@ -145,7 +146,14 @@ func addOutboundHandlers(server *Instance, configs []*OutboundHandlerConfig) err
// See Instance.RequireFeatures for more information. // See Instance.RequireFeatures for more information.
func RequireFeatures(ctx context.Context, callback interface{}) error { func RequireFeatures(ctx context.Context, callback interface{}) error {
v := MustFromContext(ctx) v := MustFromContext(ctx)
return v.RequireFeatures(callback) return v.RequireFeatures(callback, false)
}
// OptionalFeatures is a helper function to aquire features from Instance in context.
// See Instance.RequireFeatures for more information.
func OptionalFeatures(ctx context.Context, callback interface{}) error {
v := MustFromContext(ctx)
return v.RequireFeatures(callback, true)
} }
// New returns a new Xray instance based on given configuration. // New returns a new Xray instance based on given configuration.
@ -263,7 +271,7 @@ func (s *Instance) Close() error {
// RequireFeatures registers a callback, which will be called when all dependent features are registered. // RequireFeatures registers a callback, which will be called when all dependent features are registered.
// The callback must be a func(). All its parameters must be features.Feature. // The callback must be a func(). All its parameters must be features.Feature.
func (s *Instance) RequireFeatures(callback interface{}) error { func (s *Instance) RequireFeatures(callback interface{}, optional bool) error {
callbackType := reflect.TypeOf(callback) callbackType := reflect.TypeOf(callback)
if callbackType.Kind() != reflect.Func { if callbackType.Kind() != reflect.Func {
panic("not a function") panic("not a function")
@ -291,8 +299,12 @@ func (s *Instance) RequireFeatures(callback interface{}) error {
if foundAll { if foundAll {
s.resolveLock.Unlock() s.resolveLock.Unlock()
return r.callbackResolution(s.features) return r.callbackResolution(s.features)
} else {
if optional {
s.pendingOptionalResolutions = append(s.pendingOptionalResolutions, r)
} else { } else {
s.pendingResolutions = append(s.pendingResolutions, r) s.pendingResolutions = append(s.pendingResolutions, r)
}
s.resolveLock.Unlock() s.resolveLock.Unlock()
return nil return nil
} }
@ -309,12 +321,9 @@ func (s *Instance) AddFeature(feature features.Feature) error {
s.resolveLock.Lock() s.resolveLock.Lock()
s.features = append(s.features, feature) s.features = append(s.features, feature)
if s.pendingResolutions == nil {
s.resolveLock.Unlock()
return nil
}
var pending []resolution
var availableResolution []resolution var availableResolution []resolution
var pending []resolution
for _, r := range s.pendingResolutions { for _, r := range s.pendingResolutions {
foundAll := true foundAll := true
for _, d := range r.deps { for _, d := range r.deps {
@ -331,6 +340,24 @@ func (s *Instance) AddFeature(feature features.Feature) error {
} }
} }
s.pendingResolutions = pending s.pendingResolutions = pending
var pendingOptional []resolution
for _, r := range s.pendingOptionalResolutions {
foundAll := true
for _, d := range r.deps {
f := getFeature(s.features, d)
if f == nil {
foundAll = false
break
}
}
if foundAll {
availableResolution = append(availableResolution, r)
} else {
pendingOptional = append(pendingOptional, r)
}
}
s.pendingOptionalResolutions = pendingOptional
s.resolveLock.Unlock() s.resolveLock.Unlock()
var err error var err error

View File

@ -30,7 +30,7 @@ func TestXrayDependency(t *testing.T) {
t.Error("expected dns client fulfilled, but actually nil") t.Error("expected dns client fulfilled, but actually nil")
} }
wait <- true wait <- true
}) }, false)
instance.AddFeature(localdns.New()) instance.AddFeature(localdns.New())
<-wait <-wait
} }

View File

@ -27,7 +27,7 @@ func init() {
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
h := new(Handler) h := new(Handler)
if err := core.RequireFeatures(ctx, func(dnsClient dns.Client, policyManager policy.Manager) error { if err := core.RequireFeatures(ctx, func(dnsClient dns.Client, policyManager policy.Manager) error {
core.RequireFeatures(ctx, func(fdns dns.FakeDNSEngine) { // FakeDNSEngine is optional core.OptionalFeatures(ctx, func(fdns dns.FakeDNSEngine) {
h.fdns = fdns h.fdns = fdns
}) })
return h.Init(config.(*Config), dnsClient, policyManager) return h.Init(config.(*Config), dnsClient, policyManager)