github.com/xtls/xray-core@v1.8.12-0.20240518155711-3168d27b0bdb/app/commander/commander.go (about) 1 package commander 2 3 //go:generate go run github.com/xtls/xray-core/common/errors/errorgen 4 5 import ( 6 "context" 7 "net" 8 "sync" 9 10 "github.com/xtls/xray-core/common" 11 "github.com/xtls/xray-core/common/signal/done" 12 core "github.com/xtls/xray-core/core" 13 "github.com/xtls/xray-core/features/outbound" 14 "google.golang.org/grpc" 15 ) 16 17 // Commander is a Xray feature that provides gRPC methods to external clients. 18 type Commander struct { 19 sync.Mutex 20 server *grpc.Server 21 services []Service 22 ohm outbound.Manager 23 tag string 24 listen string 25 } 26 27 // NewCommander creates a new Commander based on the given config. 28 func NewCommander(ctx context.Context, config *Config) (*Commander, error) { 29 c := &Commander{ 30 tag: config.Tag, 31 listen: config.Listen, 32 } 33 34 common.Must(core.RequireFeatures(ctx, func(om outbound.Manager) { 35 c.ohm = om 36 })) 37 38 for _, rawConfig := range config.Service { 39 config, err := rawConfig.GetInstance() 40 if err != nil { 41 return nil, err 42 } 43 rawService, err := common.CreateObject(ctx, config) 44 if err != nil { 45 return nil, err 46 } 47 service, ok := rawService.(Service) 48 if !ok { 49 return nil, newError("not a Service.") 50 } 51 c.services = append(c.services, service) 52 } 53 54 return c, nil 55 } 56 57 // Type implements common.HasType. 58 func (c *Commander) Type() interface{} { 59 return (*Commander)(nil) 60 } 61 62 // Start implements common.Runnable. 63 func (c *Commander) Start() error { 64 c.Lock() 65 c.server = grpc.NewServer() 66 for _, service := range c.services { 67 service.Register(c.server) 68 } 69 c.Unlock() 70 71 var listen = func(listener net.Listener) { 72 if err := c.server.Serve(listener); err != nil { 73 newError("failed to start grpc server").Base(err).AtError().WriteToLog() 74 } 75 } 76 77 if len(c.listen) > 0 { 78 if l, err := net.Listen("tcp", c.listen); err != nil { 79 newError("API server failed to listen on ", c.listen).Base(err).AtError().WriteToLog() 80 return err 81 } else { 82 newError("API server listening on ", l.Addr()).AtInfo().WriteToLog() 83 go listen(l) 84 } 85 return nil 86 } 87 88 listener := &OutboundListener{ 89 buffer: make(chan net.Conn, 4), 90 done: done.New(), 91 } 92 93 go listen(listener) 94 95 if err := c.ohm.RemoveHandler(context.Background(), c.tag); err != nil { 96 newError("failed to remove existing handler").WriteToLog() 97 } 98 99 return c.ohm.AddHandler(context.Background(), &Outbound{ 100 tag: c.tag, 101 listener: listener, 102 }) 103 } 104 105 // Close implements common.Closable. 106 func (c *Commander) Close() error { 107 c.Lock() 108 defer c.Unlock() 109 110 if c.server != nil { 111 c.server.Stop() 112 c.server = nil 113 } 114 115 return nil 116 } 117 118 func init() { 119 common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, cfg interface{}) (interface{}, error) { 120 return NewCommander(ctx, cfg.(*Config)) 121 })) 122 }