mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-25 18:11:28 +00:00
Invalid config check
This commit is contained in:
parent
6eae8e361f
commit
30444057bd
|
@ -8,10 +8,14 @@ import (
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(ctx context.Context, router adapter.Router, logger log.Logger, index int, options option.Inbound) (adapter.Inbound, error) {
|
func New(ctx context.Context, router adapter.Router, logger log.Logger, index int, options option.Inbound) (adapter.Inbound, error) {
|
||||||
|
if common.IsEmptyByEquals(options) {
|
||||||
|
return nil, E.New("empty inbound config")
|
||||||
|
}
|
||||||
var tag string
|
var tag string
|
||||||
if options.Tag != "" {
|
if options.Tag != "" {
|
||||||
tag = options.Tag
|
tag = options.Tag
|
||||||
|
@ -21,16 +25,16 @@ func New(ctx context.Context, router adapter.Router, logger log.Logger, index in
|
||||||
inboundLogger := logger.WithPrefix(F.ToString("inbound/", options.Type, "[", tag, "]: "))
|
inboundLogger := logger.WithPrefix(F.ToString("inbound/", options.Type, "[", tag, "]: "))
|
||||||
switch options.Type {
|
switch options.Type {
|
||||||
case C.TypeDirect:
|
case C.TypeDirect:
|
||||||
return NewDirect(ctx, router, inboundLogger, options.Tag, common.PtrValueOrDefault(options.DirectOptions)), nil
|
return NewDirect(ctx, router, inboundLogger, options.Tag, options.DirectOptions), nil
|
||||||
case C.TypeSocks:
|
case C.TypeSocks:
|
||||||
return NewSocks(ctx, router, inboundLogger, options.Tag, common.PtrValueOrDefault(options.SocksOptions)), nil
|
return NewSocks(ctx, router, inboundLogger, options.Tag, options.SocksOptions), nil
|
||||||
case C.TypeHTTP:
|
case C.TypeHTTP:
|
||||||
return NewHTTP(ctx, router, inboundLogger, options.Tag, common.PtrValueOrDefault(options.HTTPOptions)), nil
|
return NewHTTP(ctx, router, inboundLogger, options.Tag, options.HTTPOptions), nil
|
||||||
case C.TypeMixed:
|
case C.TypeMixed:
|
||||||
return NewMixed(ctx, router, inboundLogger, options.Tag, common.PtrValueOrDefault(options.MixedOptions)), nil
|
return NewMixed(ctx, router, inboundLogger, options.Tag, options.MixedOptions), nil
|
||||||
case C.TypeShadowsocks:
|
case C.TypeShadowsocks:
|
||||||
return NewShadowsocks(ctx, router, inboundLogger, options.Tag, common.PtrValueOrDefault(options.ShadowsocksOptions))
|
return NewShadowsocks(ctx, router, inboundLogger, options.Tag, options.ShadowsocksOptions)
|
||||||
default:
|
default:
|
||||||
panic(F.ToString("unknown inbound type: ", options.Type))
|
return nil, E.New("unknown inbound type: ", options.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,14 @@ import (
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
F "github.com/sagernet/sing/common/format"
|
F "github.com/sagernet/sing/common/format"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New(router adapter.Router, logger log.Logger, index int, options option.Outbound) (adapter.Outbound, error) {
|
func New(router adapter.Router, logger log.Logger, index int, options option.Outbound) (adapter.Outbound, error) {
|
||||||
|
if common.IsEmpty(options) {
|
||||||
|
return nil, E.New("empty outbound config")
|
||||||
|
}
|
||||||
var tag string
|
var tag string
|
||||||
if options.Tag != "" {
|
if options.Tag != "" {
|
||||||
tag = options.Tag
|
tag = options.Tag
|
||||||
|
@ -19,10 +23,10 @@ func New(router adapter.Router, logger log.Logger, index int, options option.Out
|
||||||
outboundLogger := logger.WithPrefix(F.ToString("outbound/", options.Type, "[", tag, "]: "))
|
outboundLogger := logger.WithPrefix(F.ToString("outbound/", options.Type, "[", tag, "]: "))
|
||||||
switch options.Type {
|
switch options.Type {
|
||||||
case C.TypeDirect:
|
case C.TypeDirect:
|
||||||
return NewDirect(router, outboundLogger, options.Tag, common.PtrValueOrDefault(options.DirectOptions)), nil
|
return NewDirect(router, outboundLogger, options.Tag, options.DirectOptions), nil
|
||||||
case C.TypeShadowsocks:
|
case C.TypeShadowsocks:
|
||||||
return NewShadowsocks(router, outboundLogger, options.Tag, common.PtrValueOrDefault(options.ShadowsocksOptions))
|
return NewShadowsocks(router, outboundLogger, options.Tag, options.ShadowsocksOptions)
|
||||||
default:
|
default:
|
||||||
panic(F.ToString("unknown outbound type: ", options.Type))
|
return nil, E.New("unknown outbound type: ", options.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,10 +13,25 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewRule(router adapter.Router, logger log.Logger, options option.Rule) (adapter.Rule, error) {
|
func NewRule(router adapter.Router, logger log.Logger, options option.Rule) (adapter.Rule, error) {
|
||||||
|
if common.IsEmptyByEquals(options) {
|
||||||
|
return nil, E.New("empty rule config")
|
||||||
|
}
|
||||||
switch options.Type {
|
switch options.Type {
|
||||||
case "", C.RuleTypeDefault:
|
case "", C.RuleTypeDefault:
|
||||||
|
if !options.DefaultOptions.IsValid() {
|
||||||
|
return nil, E.New("missing conditions")
|
||||||
|
}
|
||||||
|
if options.DefaultOptions.Outbound == "" {
|
||||||
|
return nil, E.New("missing outbound field")
|
||||||
|
}
|
||||||
return NewDefaultRule(router, logger, common.PtrValueOrDefault(options.DefaultOptions))
|
return NewDefaultRule(router, logger, common.PtrValueOrDefault(options.DefaultOptions))
|
||||||
case C.RuleTypeLogical:
|
case C.RuleTypeLogical:
|
||||||
|
if !options.LogicalOptions.IsValid() {
|
||||||
|
return nil, E.New("missing conditions")
|
||||||
|
}
|
||||||
|
if options.LogicalOptions.Outbound == "" {
|
||||||
|
return nil, E.New("missing outbound field")
|
||||||
|
}
|
||||||
return NewLogicalRule(router, logger, common.PtrValueOrDefault(options.LogicalOptions))
|
return NewLogicalRule(router, logger, common.PtrValueOrDefault(options.LogicalOptions))
|
||||||
default:
|
default:
|
||||||
return nil, E.New("unknown rule type: ", options.Type)
|
return nil, E.New("unknown rule type: ", options.Type)
|
||||||
|
|
|
@ -18,7 +18,10 @@ func init() {
|
||||||
logrus.StandardLogger().Formatter.(*logrus.TextFormatter).ForceColors = true
|
logrus.StandardLogger().Formatter.(*logrus.TextFormatter).ForceColors = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var configPath string
|
var (
|
||||||
|
configPath string
|
||||||
|
workingDir string
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
command := &cobra.Command{
|
command := &cobra.Command{
|
||||||
|
@ -26,12 +29,19 @@ func main() {
|
||||||
Run: run,
|
Run: run,
|
||||||
}
|
}
|
||||||
command.Flags().StringVarP(&configPath, "config", "c", "config.json", "set configuration file path")
|
command.Flags().StringVarP(&configPath, "config", "c", "config.json", "set configuration file path")
|
||||||
|
command.Flags().StringVarP(&workingDir, "directory", "D", "", "set working directory")
|
||||||
if err := command.Execute(); err != nil {
|
if err := command.Execute(); err != nil {
|
||||||
logrus.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(cmd *cobra.Command, args []string) {
|
func run(cmd *cobra.Command, args []string) {
|
||||||
|
if workingDir != "" {
|
||||||
|
if err := os.Chdir(workingDir); err != nil {
|
||||||
|
logrus.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
configContent, err := os.ReadFile(configPath)
|
configContent, err := os.ReadFile(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Fatal("read config: ", err)
|
logrus.Fatal("read config: ", err)
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -6,7 +6,7 @@ require (
|
||||||
github.com/database64128/tfo-go v1.0.4
|
github.com/database64128/tfo-go v1.0.4
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/oschwald/geoip2-golang v1.7.0
|
github.com/oschwald/geoip2-golang v1.7.0
|
||||||
github.com/sagernet/sing v0.0.0-20220702141141-b3923d54845b
|
github.com/sagernet/sing v0.0.0-20220702174608-cb5bb5132de4
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/spf13/cobra v1.5.0
|
github.com/spf13/cobra v1.5.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -18,8 +18,8 @@ github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagernet/sing v0.0.0-20220702141141-b3923d54845b h1:oK5RglZ0s4oXNSrIsLJkBiHbYoAUMOGLN3a0JgDNzVM=
|
github.com/sagernet/sing v0.0.0-20220702174608-cb5bb5132de4 h1:Ce6nW9gV6g2hq/1K/nMtlVGiTtxh86EWs0/jOzMuNa4=
|
||||||
github.com/sagernet/sing v0.0.0-20220702141141-b3923d54845b/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
|
github.com/sagernet/sing v0.0.0-20220702174608-cb5bb5132de4/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
|
Start() error
|
||||||
|
Close() error
|
||||||
Trace(args ...interface{})
|
Trace(args ...interface{})
|
||||||
Debug(args ...interface{})
|
Debug(args ...interface{})
|
||||||
Info(args ...interface{})
|
Info(args ...interface{})
|
||||||
|
@ -18,7 +20,6 @@ type Logger interface {
|
||||||
Panic(args ...interface{})
|
Panic(args ...interface{})
|
||||||
WithContext(ctx context.Context) Logger
|
WithContext(ctx context.Context) Logger
|
||||||
WithPrefix(prefix string) Logger
|
WithPrefix(prefix string) Logger
|
||||||
Close() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLogger(options option.LogOption) (Logger, error) {
|
func NewLogger(options option.LogOption) (Logger, error) {
|
||||||
|
|
|
@ -15,7 +15,8 @@ var _ Logger = (*logrusLogger)(nil)
|
||||||
|
|
||||||
type logrusLogger struct {
|
type logrusLogger struct {
|
||||||
abstractLogrusLogger
|
abstractLogrusLogger
|
||||||
output *os.File
|
outputPath string
|
||||||
|
output *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
type abstractLogrusLogger interface {
|
type abstractLogrusLogger interface {
|
||||||
|
@ -28,7 +29,6 @@ func NewLogrusLogger(options option.LogOption) (*logrusLogger, error) {
|
||||||
logger.SetLevel(logrus.TraceLevel)
|
logger.SetLevel(logrus.TraceLevel)
|
||||||
logger.Formatter.(*logrus.TextFormatter).ForceColors = true
|
logger.Formatter.(*logrus.TextFormatter).ForceColors = true
|
||||||
logger.AddHook(new(logrusHook))
|
logger.AddHook(new(logrusHook))
|
||||||
var output *os.File
|
|
||||||
var err error
|
var err error
|
||||||
if options.Level != "" {
|
if options.Level != "" {
|
||||||
logger.Level, err = logrus.ParseLevel(options.Level)
|
logger.Level, err = logrus.ParseLevel(options.Level)
|
||||||
|
@ -36,18 +36,26 @@ func NewLogrusLogger(options option.LogOption) (*logrusLogger, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if options.Output != "" {
|
return &logrusLogger{logger, options.Output, nil}, nil
|
||||||
output, err = os.OpenFile(options.Output, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
}
|
||||||
|
|
||||||
|
func (l *logrusLogger) Start() error {
|
||||||
|
if l.outputPath != "" {
|
||||||
|
output, err := os.OpenFile(l.outputPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Extend(err, "open log output")
|
return E.Cause(err, "open log output")
|
||||||
}
|
}
|
||||||
logger.SetOutput(output)
|
l.abstractLogrusLogger.(*logrus.Logger).SetOutput(output)
|
||||||
}
|
}
|
||||||
return &logrusLogger{logger, output}, nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logrusLogger) Close() error {
|
||||||
|
return common.Close(common.PtrOrNil(l.output))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logrusLogger) WithContext(ctx context.Context) Logger {
|
func (l *logrusLogger) WithContext(ctx context.Context) Logger {
|
||||||
return &logrusLogger{l.abstractLogrusLogger.WithContext(ctx), nil}
|
return &logrusLogger{abstractLogrusLogger: l.abstractLogrusLogger.WithContext(ctx)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *logrusLogger) WithPrefix(prefix string) Logger {
|
func (l *logrusLogger) WithPrefix(prefix string) Logger {
|
||||||
|
@ -57,9 +65,5 @@ func (l *logrusLogger) WithPrefix(prefix string) Logger {
|
||||||
prefix = F.ToString(loadedPrefix, prefix)
|
prefix = F.ToString(loadedPrefix, prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &logrusLogger{l.WithField("prefix", prefix), nil}
|
return &logrusLogger{abstractLogrusLogger: l.WithField("prefix", prefix)}
|
||||||
}
|
|
||||||
|
|
||||||
func (l *logrusLogger) Close() error {
|
|
||||||
return common.Close(common.PtrOrNil(l.output))
|
|
||||||
}
|
}
|
||||||
|
|
12
log/nop.go
12
log/nop.go
|
@ -10,6 +10,14 @@ func NewNopLogger() Logger {
|
||||||
return (*nopLogger)(nil)
|
return (*nopLogger)(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *nopLogger) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *nopLogger) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (l *nopLogger) Trace(args ...interface{}) {
|
func (l *nopLogger) Trace(args ...interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +52,3 @@ func (l *nopLogger) WithContext(ctx context.Context) Logger {
|
||||||
func (l *nopLogger) WithPrefix(prefix string) Logger {
|
func (l *nopLogger) WithPrefix(prefix string) Logger {
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *nopLogger) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package option
|
package option
|
||||||
|
|
||||||
|
import "github.com/sagernet/sing/common"
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Log *LogOption `json:"log"`
|
Log *LogOption `json:"log"`
|
||||||
Inbounds []Inbound `json:"inbounds,omitempty"`
|
Inbounds []Inbound `json:"inbounds,omitempty"`
|
||||||
|
@ -7,6 +9,13 @@ type Options struct {
|
||||||
Route *RouteOptions `json:"route,omitempty"`
|
Route *RouteOptions `json:"route,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o Options) Equals(other Options) bool {
|
||||||
|
return common.ComparablePtrEquals(o.Log, other.Log) &&
|
||||||
|
common.SliceEquals(o.Inbounds, other.Inbounds) &&
|
||||||
|
common.ComparableSliceEquals(o.Outbounds, other.Outbounds) &&
|
||||||
|
common.PtrEquals(o.Route, other.Route)
|
||||||
|
}
|
||||||
|
|
||||||
type LogOption struct {
|
type LogOption struct {
|
||||||
Disabled bool `json:"disabled,omitempty"`
|
Disabled bool `json:"disabled,omitempty"`
|
||||||
Level string `json:"level,omitempty"`
|
Level string `json:"level,omitempty"`
|
||||||
|
|
|
@ -3,52 +3,50 @@ package option
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/auth"
|
"github.com/sagernet/sing/common/auth"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrUnknownInboundType = E.New("unknown inbound type")
|
|
||||||
|
|
||||||
type _Inbound struct {
|
type _Inbound struct {
|
||||||
Tag string `json:"tag,omitempty"`
|
Tag string `json:"tag,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type"`
|
||||||
DirectOptions *DirectInboundOptions `json:"directOptions,omitempty"`
|
DirectOptions DirectInboundOptions `json:"-"`
|
||||||
SocksOptions *SimpleInboundOptions `json:"socksOptions,omitempty"`
|
SocksOptions SimpleInboundOptions `json:"-"`
|
||||||
HTTPOptions *SimpleInboundOptions `json:"httpOptions,omitempty"`
|
HTTPOptions SimpleInboundOptions `json:"-"`
|
||||||
MixedOptions *SimpleInboundOptions `json:"mixedOptions,omitempty"`
|
MixedOptions SimpleInboundOptions `json:"-"`
|
||||||
ShadowsocksOptions *ShadowsocksInboundOptions `json:"shadowsocksOptions,omitempty"`
|
ShadowsocksOptions ShadowsocksInboundOptions `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Inbound _Inbound
|
type Inbound _Inbound
|
||||||
|
|
||||||
|
func (i Inbound) Equals(other Inbound) bool {
|
||||||
|
return i.Type == other.Type &&
|
||||||
|
i.Tag == other.Tag &&
|
||||||
|
common.Equals(i.DirectOptions, other.DirectOptions) &&
|
||||||
|
common.Equals(i.SocksOptions, other.SocksOptions) &&
|
||||||
|
common.Equals(i.HTTPOptions, other.HTTPOptions) &&
|
||||||
|
common.Equals(i.MixedOptions, other.MixedOptions) &&
|
||||||
|
common.Equals(i.ShadowsocksOptions, other.ShadowsocksOptions)
|
||||||
|
}
|
||||||
|
|
||||||
func (i *Inbound) MarshalJSON() ([]byte, error) {
|
func (i *Inbound) MarshalJSON() ([]byte, error) {
|
||||||
var options []byte
|
var v any
|
||||||
var err error
|
|
||||||
switch i.Type {
|
switch i.Type {
|
||||||
case "direct":
|
case "direct":
|
||||||
options, err = json.Marshal(i.DirectOptions)
|
v = i.DirectOptions
|
||||||
case "socks":
|
case "socks":
|
||||||
options, err = json.Marshal(i.SocksOptions)
|
v = i.SocksOptions
|
||||||
case "http":
|
case "http":
|
||||||
options, err = json.Marshal(i.HTTPOptions)
|
v = i.HTTPOptions
|
||||||
case "mixed":
|
case "mixed":
|
||||||
options, err = json.Marshal(i.MixedOptions)
|
v = i.MixedOptions
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
options, err = json.Marshal(i.ShadowsocksOptions)
|
v = i.ShadowsocksOptions
|
||||||
default:
|
default:
|
||||||
return nil, E.Extend(ErrUnknownInboundType, i.Type)
|
return nil, E.New("unknown inbound type: ", i.Type)
|
||||||
}
|
}
|
||||||
if err != nil {
|
return MarshallObjects(i, v)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var content map[string]any
|
|
||||||
err = json.Unmarshal(options, &content)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
content["tag"] = i.Tag
|
|
||||||
content["type"] = i.Type
|
|
||||||
return json.Marshal(content)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Inbound) UnmarshalJSON(bytes []byte) error {
|
func (i *Inbound) UnmarshalJSON(bytes []byte) error {
|
||||||
|
@ -56,43 +54,29 @@ func (i *Inbound) UnmarshalJSON(bytes []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
var v any
|
||||||
switch i.Type {
|
switch i.Type {
|
||||||
case "direct":
|
case "direct":
|
||||||
if i.DirectOptions != nil {
|
v = &i.DirectOptions
|
||||||
break
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(bytes, &i.DirectOptions)
|
|
||||||
case "socks":
|
case "socks":
|
||||||
if i.SocksOptions != nil {
|
v = &i.SocksOptions
|
||||||
break
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(bytes, &i.SocksOptions)
|
|
||||||
case "http":
|
case "http":
|
||||||
if i.HTTPOptions != nil {
|
v = &i.HTTPOptions
|
||||||
break
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(bytes, &i.HTTPOptions)
|
|
||||||
case "mixed":
|
case "mixed":
|
||||||
if i.MixedOptions != nil {
|
v = &i.MixedOptions
|
||||||
break
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(bytes, &i.MixedOptions)
|
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
if i.ShadowsocksOptions != nil {
|
v = &i.ShadowsocksOptions
|
||||||
break
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(bytes, &i.ShadowsocksOptions)
|
|
||||||
default:
|
default:
|
||||||
return E.Extend(ErrUnknownInboundType, i.Type)
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return json.Unmarshal(bytes, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ListenOptions struct {
|
type ListenOptions struct {
|
||||||
Listen ListenAddress `json:"listen"`
|
Listen ListenAddress `json:"listen"`
|
||||||
Port uint16 `json:"listen_port"`
|
Port uint16 `json:"listen_port"`
|
||||||
TCPFastOpen bool `json:"tcpFastOpen,omitempty"`
|
TCPFastOpen bool `json:"tcp_fast_open,omitempty"`
|
||||||
UDPTimeout int64 `json:"udpTimeout,omitempty"`
|
UDPTimeout int64 `json:"udp_timeout,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SimpleInboundOptions struct {
|
type SimpleInboundOptions struct {
|
||||||
|
@ -100,11 +84,23 @@ type SimpleInboundOptions struct {
|
||||||
Users []auth.User `json:"users,omitempty"`
|
Users []auth.User `json:"users,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o SimpleInboundOptions) Equals(other SimpleInboundOptions) bool {
|
||||||
|
return o.ListenOptions == other.ListenOptions &&
|
||||||
|
common.ComparableSliceEquals(o.Users, other.Users)
|
||||||
|
}
|
||||||
|
|
||||||
type DirectInboundOptions struct {
|
type DirectInboundOptions struct {
|
||||||
ListenOptions
|
ListenOptions
|
||||||
Network NetworkList `json:"network,omitempty"`
|
Network NetworkList `json:"network,omitempty"`
|
||||||
OverrideAddress string `json:"overrideAddress,omitempty"`
|
OverrideAddress string `json:"override_address,omitempty"`
|
||||||
OverridePort uint16 `json:"overridePort,omitempty"`
|
OverridePort uint16 `json:"override_port,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o DirectInboundOptions) Equals(other DirectInboundOptions) bool {
|
||||||
|
return o.ListenOptions == other.ListenOptions &&
|
||||||
|
common.ComparableSliceEquals(o.Network, other.Network) &&
|
||||||
|
o.OverrideAddress == other.OverrideAddress &&
|
||||||
|
o.OverridePort == other.OverridePort
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShadowsocksInboundOptions struct {
|
type ShadowsocksInboundOptions struct {
|
||||||
|
@ -113,3 +109,10 @@ type ShadowsocksInboundOptions struct {
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o ShadowsocksInboundOptions) Equals(other ShadowsocksInboundOptions) bool {
|
||||||
|
return o.ListenOptions == other.ListenOptions &&
|
||||||
|
common.ComparableSliceEquals(o.Network, other.Network) &&
|
||||||
|
o.Method == other.Method &&
|
||||||
|
o.Password == other.Password
|
||||||
|
}
|
||||||
|
|
40
option/json.go
Normal file
40
option/json.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package option
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToMap(v any) (map[string]any, error) {
|
||||||
|
bytes, err := json.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var content map[string]any
|
||||||
|
err = json.Unmarshal(bytes, &content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MergeObjects(objects ...any) (map[string]any, error) {
|
||||||
|
content := make(map[string]any)
|
||||||
|
for _, object := range objects {
|
||||||
|
objectMap, err := ToMap(object)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for k, v := range objectMap {
|
||||||
|
content[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MarshallObjects(objects ...any) ([]byte, error) {
|
||||||
|
content, err := MergeObjects(objects...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return json.Marshal(content)
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ package option
|
||||||
|
|
||||||
import "encoding/json"
|
import "encoding/json"
|
||||||
|
|
||||||
type Listable[T any] []T
|
type Listable[T comparable] []T
|
||||||
|
|
||||||
func (l *Listable[T]) MarshalJSON() ([]byte, error) {
|
func (l *Listable[T]) MarshalJSON() ([]byte, error) {
|
||||||
arrayList := []T(*l)
|
arrayList := []T(*l)
|
||||||
|
|
|
@ -7,64 +7,43 @@ import (
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrUnknownOutboundType = E.New("unknown outbound type")
|
|
||||||
|
|
||||||
type _Outbound struct {
|
type _Outbound struct {
|
||||||
Tag string `json:"tag,omitempty"`
|
Tag string `json:"tag,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
DirectOptions *DirectOutboundOptions `json:"directOptions,omitempty"`
|
DirectOptions DirectOutboundOptions `json:"-"`
|
||||||
ShadowsocksOptions *ShadowsocksOutboundOptions `json:"shadowsocksOptions,omitempty"`
|
ShadowsocksOptions ShadowsocksOutboundOptions `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Outbound _Outbound
|
type Outbound _Outbound
|
||||||
|
|
||||||
func (i *Outbound) MarshalJSON() ([]byte, error) {
|
func (i *Outbound) MarshalJSON() ([]byte, error) {
|
||||||
var options []byte
|
var v any
|
||||||
var err error
|
|
||||||
switch i.Type {
|
switch i.Type {
|
||||||
case "direct":
|
case "direct":
|
||||||
options, err = json.Marshal(i.DirectOptions)
|
v = i.DirectOptions
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
options, err = json.Marshal(i.ShadowsocksOptions)
|
v = i.ShadowsocksOptions
|
||||||
default:
|
default:
|
||||||
return nil, E.Extend(ErrUnknownOutboundType, i.Type)
|
return nil, E.New("unknown outbound type: ", i.Type)
|
||||||
}
|
}
|
||||||
if err != nil {
|
return MarshallObjects(i, v)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var content map[string]any
|
|
||||||
err = json.Unmarshal(options, &content)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
content["tag"] = i.Tag
|
|
||||||
content["type"] = i.Type
|
|
||||||
return json.Marshal(content)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Outbound) UnmarshalJSON(bytes []byte) error {
|
func (i *Outbound) UnmarshalJSON(bytes []byte) error {
|
||||||
if err := json.Unmarshal(bytes, (*_Outbound)(i)); err != nil {
|
err := json.Unmarshal(bytes, (*_Outbound)(i))
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
var v any
|
||||||
switch i.Type {
|
switch i.Type {
|
||||||
case "direct":
|
case "direct":
|
||||||
if i.DirectOptions != nil {
|
v = &i.DirectOptions
|
||||||
break
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(bytes, &i.DirectOptions); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case "shadowsocks":
|
case "shadowsocks":
|
||||||
if i.ShadowsocksOptions != nil {
|
v = &i.ShadowsocksOptions
|
||||||
break
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(bytes, &i.ShadowsocksOptions); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return E.Extend(ErrUnknownOutboundType, i.Type)
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
return json.Unmarshal(bytes, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DialerOptions struct {
|
type DialerOptions struct {
|
||||||
|
|
|
@ -4,16 +4,20 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrUnknownRuleType = E.New("unknown rule type")
|
|
||||||
|
|
||||||
type RouteOptions struct {
|
type RouteOptions struct {
|
||||||
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
GeoIP *GeoIPOptions `json:"geoip,omitempty"`
|
||||||
Rules []Rule `json:"rules,omitempty"`
|
Rules []Rule `json:"rules,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o RouteOptions) Equals(other RouteOptions) bool {
|
||||||
|
return common.ComparablePtrEquals(o.GeoIP, other.GeoIP) &&
|
||||||
|
common.SliceEquals(o.Rules, other.Rules)
|
||||||
|
}
|
||||||
|
|
||||||
type GeoIPOptions struct {
|
type GeoIPOptions struct {
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
DownloadURL string `json:"download_url,omitempty"`
|
DownloadURL string `json:"download_url,omitempty"`
|
||||||
|
@ -28,25 +32,23 @@ type _Rule struct {
|
||||||
|
|
||||||
type Rule _Rule
|
type Rule _Rule
|
||||||
|
|
||||||
|
func (r Rule) Equals(other Rule) bool {
|
||||||
|
return r.Type == other.Type &&
|
||||||
|
common.PtrEquals(r.DefaultOptions, other.DefaultOptions) &&
|
||||||
|
common.PtrEquals(r.LogicalOptions, other.LogicalOptions)
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Rule) MarshalJSON() ([]byte, error) {
|
func (r *Rule) MarshalJSON() ([]byte, error) {
|
||||||
var content map[string]any
|
var v any
|
||||||
switch r.Type {
|
switch r.Type {
|
||||||
case "", C.RuleTypeDefault:
|
case C.RuleTypeDefault:
|
||||||
return json.Marshal(r.DefaultOptions)
|
v = r.DefaultOptions
|
||||||
case C.RuleTypeLogical:
|
case C.RuleTypeLogical:
|
||||||
options, err := json.Marshal(r.LogicalOptions)
|
v = r.LogicalOptions
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(options, &content)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
content["type"] = r.Type
|
|
||||||
return json.Marshal(content)
|
|
||||||
default:
|
default:
|
||||||
return nil, E.Extend(ErrUnknownRuleType, r.Type)
|
return nil, E.New("unknown rule type: " + r.Type)
|
||||||
}
|
}
|
||||||
|
return MarshallObjects(r, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Rule) UnmarshalJSON(bytes []byte) error {
|
func (r *Rule) UnmarshalJSON(bytes []byte) error {
|
||||||
|
@ -54,21 +56,19 @@ func (r *Rule) UnmarshalJSON(bytes []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
switch r.Type {
|
if r.Type == "" {
|
||||||
case "", C.RuleTypeDefault:
|
r.Type = C.RuleTypeDefault
|
||||||
if r.DefaultOptions == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(bytes, r.DefaultOptions)
|
|
||||||
case C.RuleTypeLogical:
|
|
||||||
if r.LogicalOptions == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(bytes, r.LogicalOptions)
|
|
||||||
default:
|
|
||||||
err = E.Extend(ErrUnknownRuleType, r.Type)
|
|
||||||
}
|
}
|
||||||
return err
|
var v any
|
||||||
|
switch r.Type {
|
||||||
|
case C.RuleTypeDefault:
|
||||||
|
v = &r.DefaultOptions
|
||||||
|
case C.RuleTypeLogical:
|
||||||
|
v = &r.LogicalOptions
|
||||||
|
default:
|
||||||
|
return E.New("unknown rule type: " + r.Type)
|
||||||
|
}
|
||||||
|
return json.Unmarshal(bytes, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DefaultRule struct {
|
type DefaultRule struct {
|
||||||
|
@ -90,8 +90,41 @@ type DefaultRule struct {
|
||||||
Outbound string `json:"outbound,omitempty"`
|
Outbound string `json:"outbound,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r DefaultRule) IsValid() bool {
|
||||||
|
var defaultValue DefaultRule
|
||||||
|
defaultValue.Outbound = r.Outbound
|
||||||
|
return !r.Equals(defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r DefaultRule) Equals(other DefaultRule) bool {
|
||||||
|
return common.ComparableSliceEquals(r.Inbound, other.Inbound) &&
|
||||||
|
r.IPVersion == other.IPVersion &&
|
||||||
|
r.Network == other.Network &&
|
||||||
|
common.ComparableSliceEquals(r.Protocol, other.Protocol) &&
|
||||||
|
common.ComparableSliceEquals(r.Domain, other.Domain) &&
|
||||||
|
common.ComparableSliceEquals(r.DomainSuffix, other.DomainSuffix) &&
|
||||||
|
common.ComparableSliceEquals(r.DomainKeyword, other.DomainKeyword) &&
|
||||||
|
common.ComparableSliceEquals(r.SourceGeoIP, other.SourceGeoIP) &&
|
||||||
|
common.ComparableSliceEquals(r.GeoIP, other.GeoIP) &&
|
||||||
|
common.ComparableSliceEquals(r.SourceIPCIDR, other.SourceIPCIDR) &&
|
||||||
|
common.ComparableSliceEquals(r.IPCIDR, other.IPCIDR) &&
|
||||||
|
common.ComparableSliceEquals(r.SourcePort, other.SourcePort) &&
|
||||||
|
common.ComparableSliceEquals(r.Port, other.Port) &&
|
||||||
|
r.Outbound == other.Outbound
|
||||||
|
}
|
||||||
|
|
||||||
type LogicalRule struct {
|
type LogicalRule struct {
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
Rules []DefaultRule `json:"rules,omitempty"`
|
Rules []DefaultRule `json:"rules,omitempty"`
|
||||||
Outbound string `json:"outbound,omitempty"`
|
Outbound string `json:"outbound,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r LogicalRule) IsValid() bool {
|
||||||
|
return len(r.Rules) > 0 && common.All(r.Rules, DefaultRule.IsValid)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r LogicalRule) Equals(other LogicalRule) bool {
|
||||||
|
return r.Mode == other.Mode &&
|
||||||
|
common.SliceEquals(r.Rules, other.Rules) &&
|
||||||
|
r.Outbound == other.Outbound
|
||||||
|
}
|
||||||
|
|
26
service.go
26
service.go
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.Service = (*Service)(nil)
|
var _ adapter.Service = (*Service)(nil)
|
||||||
|
@ -24,11 +25,11 @@ type Service struct {
|
||||||
func NewService(ctx context.Context, options option.Options) (*Service, error) {
|
func NewService(ctx context.Context, options option.Options) (*Service, error) {
|
||||||
logger, err := log.NewLogger(common.PtrValueOrDefault(options.Log))
|
logger, err := log.NewLogger(common.PtrValueOrDefault(options.Log))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, E.Cause(err, "parse log options")
|
||||||
}
|
}
|
||||||
router, err := route.NewRouter(ctx, logger, common.PtrValueOrDefault(options.Route))
|
router, err := route.NewRouter(ctx, logger, common.PtrValueOrDefault(options.Route))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, E.Cause(err, "parse route options")
|
||||||
}
|
}
|
||||||
inbounds := make([]adapter.Inbound, 0, len(options.Inbounds))
|
inbounds := make([]adapter.Inbound, 0, len(options.Inbounds))
|
||||||
outbounds := make([]adapter.Outbound, 0, len(options.Outbounds))
|
outbounds := make([]adapter.Outbound, 0, len(options.Outbounds))
|
||||||
|
@ -36,7 +37,7 @@ func NewService(ctx context.Context, options option.Options) (*Service, error) {
|
||||||
var inboundService adapter.Inbound
|
var inboundService adapter.Inbound
|
||||||
inboundService, err = inbound.New(ctx, router, logger, i, inboundOptions)
|
inboundService, err = inbound.New(ctx, router, logger, i, inboundOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, E.Cause(err, "parse inbound[", i, "]")
|
||||||
}
|
}
|
||||||
inbounds = append(inbounds, inboundService)
|
inbounds = append(inbounds, inboundService)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +45,7 @@ func NewService(ctx context.Context, options option.Options) (*Service, error) {
|
||||||
var outboundService adapter.Outbound
|
var outboundService adapter.Outbound
|
||||||
outboundService, err = outbound.New(router, logger, i, outboundOptions)
|
outboundService, err = outbound.New(router, logger, i, outboundOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, E.Cause(err, "parse outbound[", i, "]")
|
||||||
}
|
}
|
||||||
outbounds = append(outbounds, outboundService)
|
outbounds = append(outbounds, outboundService)
|
||||||
}
|
}
|
||||||
|
@ -61,13 +62,19 @@ func NewService(ctx context.Context, options option.Options) (*Service, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Start() error {
|
func (s *Service) Start() error {
|
||||||
|
err := s.logger.Start()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
for _, in := range s.inbounds {
|
for _, in := range s.inbounds {
|
||||||
err := in.Start()
|
err = in.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return common.AnyError(
|
||||||
|
s.router.Start(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Close() error {
|
func (s *Service) Close() error {
|
||||||
|
@ -77,7 +84,8 @@ func (s *Service) Close() error {
|
||||||
for _, out := range s.outbounds {
|
for _, out := range s.outbounds {
|
||||||
common.Close(out)
|
common.Close(out)
|
||||||
}
|
}
|
||||||
s.logger.Close()
|
return common.Close(
|
||||||
s.router.Close()
|
s.router,
|
||||||
return nil
|
s.logger,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue