github.com/moqsien/xraycore@v1.8.5/core/xray.go (about) 1 package core 2 3 import ( 4 "context" 5 "os" 6 "reflect" 7 "sync" 8 9 "github.com/moqsien/xraycore/common" 10 "github.com/moqsien/xraycore/common/serial" 11 "github.com/moqsien/xraycore/features" 12 "github.com/moqsien/xraycore/features/dns" 13 "github.com/moqsien/xraycore/features/dns/localdns" 14 "github.com/moqsien/xraycore/features/inbound" 15 "github.com/moqsien/xraycore/features/outbound" 16 "github.com/moqsien/xraycore/features/policy" 17 "github.com/moqsien/xraycore/features/routing" 18 "github.com/moqsien/xraycore/features/stats" 19 "github.com/moqsien/xraycore/transport/internet" 20 ) 21 22 // Server is an instance of Xray. At any time, there must be at most one Server instance running. 23 type Server interface { 24 common.Runnable 25 } 26 27 // ServerType returns the type of the server. 28 func ServerType() interface{} { 29 return (*Instance)(nil) 30 } 31 32 type resolution struct { 33 deps []reflect.Type 34 callback interface{} 35 } 36 37 func getFeature(allFeatures []features.Feature, t reflect.Type) features.Feature { 38 for _, f := range allFeatures { 39 if reflect.TypeOf(f.Type()) == t { 40 return f 41 } 42 } 43 return nil 44 } 45 46 func (r *resolution) resolve(allFeatures []features.Feature) (bool, error) { 47 var fs []features.Feature 48 for _, d := range r.deps { 49 f := getFeature(allFeatures, d) 50 if f == nil { 51 return false, nil 52 } 53 fs = append(fs, f) 54 } 55 56 callback := reflect.ValueOf(r.callback) 57 var input []reflect.Value 58 callbackType := callback.Type() 59 for i := 0; i < callbackType.NumIn(); i++ { 60 pt := callbackType.In(i) 61 for _, f := range fs { 62 if reflect.TypeOf(f).AssignableTo(pt) { 63 input = append(input, reflect.ValueOf(f)) 64 break 65 } 66 } 67 } 68 69 if len(input) != callbackType.NumIn() { 70 panic("Can't get all input parameters") 71 } 72 73 var err error 74 ret := callback.Call(input) 75 errInterface := reflect.TypeOf((*error)(nil)).Elem() 76 for i := len(ret) - 1; i >= 0; i-- { 77 if ret[i].Type() == errInterface { 78 v := ret[i].Interface() 79 if v != nil { 80 err = v.(error) 81 } 82 break 83 } 84 } 85 86 return true, err 87 } 88 89 // Instance combines all functionalities in Xray. 90 type Instance struct { 91 access sync.Mutex 92 features []features.Feature 93 featureResolutions []resolution 94 running bool 95 96 ctx context.Context 97 } 98 99 func AddInboundHandler(server *Instance, config *InboundHandlerConfig) error { 100 inboundManager := server.GetFeature(inbound.ManagerType()).(inbound.Manager) 101 rawHandler, err := CreateObject(server, config) 102 if err != nil { 103 return err 104 } 105 handler, ok := rawHandler.(inbound.Handler) 106 if !ok { 107 return newError("not an InboundHandler") 108 } 109 if err := inboundManager.AddHandler(server.ctx, handler); err != nil { 110 return err 111 } 112 return nil 113 } 114 115 func addInboundHandlers(server *Instance, configs []*InboundHandlerConfig) error { 116 for _, inboundConfig := range configs { 117 if err := AddInboundHandler(server, inboundConfig); err != nil { 118 return err 119 } 120 } 121 122 return nil 123 } 124 125 func AddOutboundHandler(server *Instance, config *OutboundHandlerConfig) error { 126 outboundManager := server.GetFeature(outbound.ManagerType()).(outbound.Manager) 127 rawHandler, err := CreateObject(server, config) 128 if err != nil { 129 return err 130 } 131 handler, ok := rawHandler.(outbound.Handler) 132 if !ok { 133 return newError("not an OutboundHandler") 134 } 135 if err := outboundManager.AddHandler(server.ctx, handler); err != nil { 136 return err 137 } 138 return nil 139 } 140 141 func addOutboundHandlers(server *Instance, configs []*OutboundHandlerConfig) error { 142 for _, outboundConfig := range configs { 143 if err := AddOutboundHandler(server, outboundConfig); err != nil { 144 return err 145 } 146 } 147 148 return nil 149 } 150 151 // RequireFeatures is a helper function to require features from Instance in context. 152 // See Instance.RequireFeatures for more information. 153 func RequireFeatures(ctx context.Context, callback interface{}) error { 154 v := MustFromContext(ctx) 155 return v.RequireFeatures(callback) 156 } 157 158 // New returns a new Xray instance based on given configuration. 159 // The instance is not started at this point. 160 // To ensure Xray instance works properly, the config must contain one Dispatcher, one InboundHandlerManager and one OutboundHandlerManager. Other features are optional. 161 func New(config *Config) (*Instance, error) { 162 server := &Instance{ctx: context.Background()} 163 164 done, err := initInstanceWithConfig(config, server) 165 if done { 166 return nil, err 167 } 168 169 return server, nil 170 } 171 172 func NewWithContext(ctx context.Context, config *Config) (*Instance, error) { 173 server := &Instance{ctx: ctx} 174 175 done, err := initInstanceWithConfig(config, server) 176 if done { 177 return nil, err 178 } 179 180 return server, nil 181 } 182 183 func initInstanceWithConfig(config *Config, server *Instance) (bool, error) { 184 server.ctx = context.WithValue(server.ctx, "cone", os.Getenv("XRAY_CONE_DISABLED") != "true") 185 186 if config.Transport != nil { 187 features.PrintDeprecatedFeatureWarning("global transport settings") 188 } 189 if err := config.Transport.Apply(); err != nil { 190 return true, err 191 } 192 193 for _, appSettings := range config.App { 194 settings, err := appSettings.GetInstance() 195 if err != nil { 196 return true, err 197 } 198 obj, err := CreateObject(server, settings) 199 if err != nil { 200 return true, err 201 } 202 if feature, ok := obj.(features.Feature); ok { 203 if err := server.AddFeature(feature); err != nil { 204 return true, err 205 } 206 } 207 } 208 209 essentialFeatures := []struct { 210 Type interface{} 211 Instance features.Feature 212 }{ 213 {dns.ClientType(), localdns.New()}, 214 {policy.ManagerType(), policy.DefaultManager{}}, 215 {routing.RouterType(), routing.DefaultRouter{}}, 216 {stats.ManagerType(), stats.NoopManager{}}, 217 } 218 219 for _, f := range essentialFeatures { 220 if server.GetFeature(f.Type) == nil { 221 if err := server.AddFeature(f.Instance); err != nil { 222 return true, err 223 } 224 } 225 } 226 227 internet.InitSystemDialer( 228 server.GetFeature(dns.ClientType()).(dns.Client), 229 func() outbound.Manager { 230 obm, _ := server.GetFeature(outbound.ManagerType()).(outbound.Manager) 231 return obm 232 }(), 233 ) 234 235 if server.featureResolutions != nil { 236 return true, newError("not all dependency are resolved.") 237 } 238 239 if err := addInboundHandlers(server, config.Inbound); err != nil { 240 return true, err 241 } 242 243 if err := addOutboundHandlers(server, config.Outbound); err != nil { 244 return true, err 245 } 246 return false, nil 247 } 248 249 // Type implements common.HasType. 250 func (s *Instance) Type() interface{} { 251 return ServerType() 252 } 253 254 // Close shutdown the Xray instance. 255 func (s *Instance) Close() error { 256 s.access.Lock() 257 defer s.access.Unlock() 258 259 s.running = false 260 261 var errors []interface{} 262 for _, f := range s.features { 263 if err := f.Close(); err != nil { 264 errors = append(errors, err) 265 } 266 } 267 if len(errors) > 0 { 268 return newError("failed to close all features").Base(newError(serial.Concat(errors...))) 269 } 270 271 return nil 272 } 273 274 // RequireFeatures registers a callback, which will be called when all dependent features are registered. 275 // The callback must be a func(). All its parameters must be features.Feature. 276 func (s *Instance) RequireFeatures(callback interface{}) error { 277 callbackType := reflect.TypeOf(callback) 278 if callbackType.Kind() != reflect.Func { 279 panic("not a function") 280 } 281 282 var featureTypes []reflect.Type 283 for i := 0; i < callbackType.NumIn(); i++ { 284 featureTypes = append(featureTypes, reflect.PtrTo(callbackType.In(i))) 285 } 286 287 r := resolution{ 288 deps: featureTypes, 289 callback: callback, 290 } 291 if finished, err := r.resolve(s.features); finished { 292 return err 293 } 294 s.featureResolutions = append(s.featureResolutions, r) 295 return nil 296 } 297 298 // AddFeature registers a feature into current Instance. 299 func (s *Instance) AddFeature(feature features.Feature) error { 300 s.features = append(s.features, feature) 301 302 if s.running { 303 if err := feature.Start(); err != nil { 304 newError("failed to start feature").Base(err).WriteToLog() 305 } 306 return nil 307 } 308 309 if s.featureResolutions == nil { 310 return nil 311 } 312 313 var pendingResolutions []resolution 314 for _, r := range s.featureResolutions { 315 finished, err := r.resolve(s.features) 316 if finished && err != nil { 317 return err 318 } 319 if !finished { 320 pendingResolutions = append(pendingResolutions, r) 321 } 322 } 323 if len(pendingResolutions) == 0 { 324 s.featureResolutions = nil 325 } else if len(pendingResolutions) < len(s.featureResolutions) { 326 s.featureResolutions = pendingResolutions 327 } 328 329 return nil 330 } 331 332 // GetFeature returns a feature of the given type, or nil if such feature is not registered. 333 func (s *Instance) GetFeature(featureType interface{}) features.Feature { 334 return getFeature(s.features, reflect.TypeOf(featureType)) 335 } 336 337 // Start starts the Xray instance, including all registered features. When Start returns error, the state of the instance is unknown. 338 // A Xray instance can be started only once. Upon closing, the instance is not guaranteed to start again. 339 // 340 // xray:api:stable 341 func (s *Instance) Start() error { 342 s.access.Lock() 343 defer s.access.Unlock() 344 345 s.running = true 346 for _, f := range s.features { 347 if err := f.Start(); err != nil { 348 return err 349 } 350 } 351 352 newError("Xray ", Version(), " started").AtWarning().WriteToLog() 353 354 return nil 355 }