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