mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
Merge branch 'dev-next' into origin/tls-client-auth
Signed-off-by: jose-C2OaWi <111356383+jose-C2OaWi@users.noreply.github.com>
This commit is contained in:
commit
df3ff301e6
@ -109,7 +109,7 @@ type OutboundGroup interface {
|
|||||||
|
|
||||||
type URLTestGroup interface {
|
type URLTestGroup interface {
|
||||||
OutboundGroup
|
OutboundGroup
|
||||||
URLTest(ctx context.Context, url string) (map[string]uint16, error)
|
URLTest(ctx context.Context) (map[string]uint16, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func OutboundTag(detour Outbound) string {
|
func OutboundTag(detour Outbound) string {
|
||||||
|
15
box.go
15
box.go
@ -287,7 +287,7 @@ func (s *Box) start() error {
|
|||||||
return E.Cause(err, "initialize inbound/", in.Type(), "[", tag, "]")
|
return E.Cause(err, "initialize inbound/", in.Type(), "[", tag, "]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return s.postStart()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Box) postStart() error {
|
func (s *Box) postStart() error {
|
||||||
@ -298,20 +298,21 @@ func (s *Box) postStart() error {
|
|||||||
return E.Cause(err, "start ", serviceName)
|
return E.Cause(err, "start ", serviceName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for serviceName, service := range s.outbounds {
|
for _, outbound := range s.outbounds {
|
||||||
if lateService, isLateService := service.(adapter.PostStarter); isLateService {
|
if lateOutbound, isLateOutbound := outbound.(adapter.PostStarter); isLateOutbound {
|
||||||
s.logger.Trace("post-starting ", service)
|
s.logger.Trace("post-starting outbound/", outbound.Tag())
|
||||||
err := lateService.PostStart()
|
err := lateOutbound.PostStart()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "post-start ", serviceName)
|
return E.Cause(err, "post-start outbound/", outbound.Tag())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.logger.Trace("post-starting router")
|
||||||
err := s.router.PostStart()
|
err := s.router.PostStart()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "post-start router")
|
return E.Cause(err, "post-start router")
|
||||||
}
|
}
|
||||||
return nil
|
return s.router.PostStart()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Box) Close() error {
|
func (s *Box) Close() error {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/common/geosite"
|
"github.com/sagernet/sing-box/common/geosite"
|
||||||
|
"github.com/sagernet/sing-box/common/json"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
|
@ -3,11 +3,12 @@ package constant
|
|||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TCPTimeout = 5 * time.Second
|
TCPTimeout = 5 * time.Second
|
||||||
ReadPayloadTimeout = 300 * time.Millisecond
|
ReadPayloadTimeout = 300 * time.Millisecond
|
||||||
DNSTimeout = 10 * time.Second
|
DNSTimeout = 10 * time.Second
|
||||||
QUICTimeout = 30 * time.Second
|
QUICTimeout = 30 * time.Second
|
||||||
STUNTimeout = 15 * time.Second
|
STUNTimeout = 15 * time.Second
|
||||||
UDPTimeout = 5 * time.Minute
|
UDPTimeout = 5 * time.Minute
|
||||||
DefaultURLTestInterval = 1 * time.Minute
|
DefaultURLTestInterval = 3 * time.Minute
|
||||||
|
DefaultURLTestIdleTimeout = 30 * time.Second
|
||||||
)
|
)
|
||||||
|
@ -4,25 +4,8 @@ icon: material/alert-decagram
|
|||||||
|
|
||||||
# ChangeLog
|
# ChangeLog
|
||||||
|
|
||||||
#### 1.8.0-alpha.8
|
|
||||||
|
|
||||||
* Add context to JSON decode error message **1**
|
#### 1.7.2
|
||||||
* Reject internal fake-ip queries **2**
|
|
||||||
* Fixes and improvements
|
|
||||||
|
|
||||||
**1**:
|
|
||||||
|
|
||||||
JSON parse errors will now include the current key path.
|
|
||||||
Only takes effect when compiled with Go 1.21+.
|
|
||||||
|
|
||||||
**2**:
|
|
||||||
|
|
||||||
All internal DNS queries now skip DNS rules with `server` type `fakeip`,
|
|
||||||
and the default DNS server can no longer be `fakeip`.
|
|
||||||
|
|
||||||
This change is intended to break incorrect usage and essentially requires no action.
|
|
||||||
|
|
||||||
#### 1.8.0-alpha.7
|
|
||||||
|
|
||||||
* Fixes and improvements
|
* Fixes and improvements
|
||||||
|
|
||||||
|
@ -10,9 +10,10 @@
|
|||||||
"proxy-b",
|
"proxy-b",
|
||||||
"proxy-c"
|
"proxy-c"
|
||||||
],
|
],
|
||||||
"url": "https://www.gstatic.com/generate_204",
|
"url": "",
|
||||||
"interval": "1m",
|
"interval": "",
|
||||||
"tolerance": 50,
|
"tolerance": 0,
|
||||||
|
"idle_timeout": "",
|
||||||
"interrupt_exist_connections": false
|
"interrupt_exist_connections": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -31,12 +32,16 @@ The URL to test. `https://www.gstatic.com/generate_204` will be used if empty.
|
|||||||
|
|
||||||
#### interval
|
#### interval
|
||||||
|
|
||||||
The test interval. `1m` will be used if empty.
|
The test interval. `3m` will be used if empty.
|
||||||
|
|
||||||
#### tolerance
|
#### tolerance
|
||||||
|
|
||||||
The test tolerance in milliseconds. `50` will be used if empty.
|
The test tolerance in milliseconds. `50` will be used if empty.
|
||||||
|
|
||||||
|
#### idle_timeout
|
||||||
|
|
||||||
|
The idle timeout. `30m` will be used if empty.
|
||||||
|
|
||||||
#### interrupt_exist_connections
|
#### interrupt_exist_connections
|
||||||
|
|
||||||
Interrupt existing connections when the selected outbound has changed.
|
Interrupt existing connections when the selected outbound has changed.
|
||||||
|
@ -10,9 +10,10 @@
|
|||||||
"proxy-b",
|
"proxy-b",
|
||||||
"proxy-c"
|
"proxy-c"
|
||||||
],
|
],
|
||||||
"url": "https://www.gstatic.com/generate_204",
|
"url": "",
|
||||||
"interval": "1m",
|
"interval": "",
|
||||||
"tolerance": 50,
|
"tolerance": 50,
|
||||||
|
"idle_timeout": "",
|
||||||
"interrupt_exist_connections": false
|
"interrupt_exist_connections": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -31,12 +32,16 @@
|
|||||||
|
|
||||||
#### interval
|
#### interval
|
||||||
|
|
||||||
测试间隔。 默认使用 `1m`。
|
测试间隔。 默认使用 `3m`。
|
||||||
|
|
||||||
#### tolerance
|
#### tolerance
|
||||||
|
|
||||||
以毫秒为单位的测试容差。 默认使用 `50`。
|
以毫秒为单位的测试容差。 默认使用 `50`。
|
||||||
|
|
||||||
|
#### idle_timeout
|
||||||
|
|
||||||
|
空闲超时。默认使用 `30m`。
|
||||||
|
|
||||||
#### interrupt_exist_connections
|
#### interrupt_exist_connections
|
||||||
|
|
||||||
当选定的出站发生更改时,中断现有连接。
|
当选定的出站发生更改时,中断现有连接。
|
||||||
|
@ -83,7 +83,7 @@ func getGroupDelay(server *Server) func(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
var result map[string]uint16
|
var result map[string]uint16
|
||||||
if urlTestGroup, isURLTestGroup := group.(adapter.URLTestGroup); isURLTestGroup {
|
if urlTestGroup, isURLTestGroup := group.(adapter.URLTestGroup); isURLTestGroup {
|
||||||
result, err = urlTestGroup.URLTest(ctx, url)
|
result, err = urlTestGroup.URLTest(ctx)
|
||||||
} else {
|
} else {
|
||||||
outbounds := common.FilterNotNil(common.Map(group.All(), func(it string) adapter.Outbound {
|
outbounds := common.FilterNotNil(common.Map(group.All(), func(it string) adapter.Outbound {
|
||||||
itOutbound, _ := server.router.Outbound(it)
|
itOutbound, _ := server.router.Outbound(it)
|
||||||
|
1
go.sum
1
go.sum
@ -111,6 +111,7 @@ github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4Wk
|
|||||||
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=
|
||||||
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=
|
||||||
github.com/sagernet/sing v0.2.18-0.20231201060417-575186ed63c2 h1:1ydWkFgLURGlrnwRdjyrpo9lp1g5Qq7XrNBghMntWTs=
|
github.com/sagernet/sing v0.2.18-0.20231201060417-575186ed63c2 h1:1ydWkFgLURGlrnwRdjyrpo9lp1g5Qq7XrNBghMntWTs=
|
||||||
|
github.com/sagernet/sing v0.2.18-0.20231201060417-575186ed63c2/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
|
||||||
github.com/sagernet/sing-dns v0.1.11 h1:PPrMCVVrAeR3f5X23I+cmvacXJ+kzuyAsBiWyUKhGSE=
|
github.com/sagernet/sing-dns v0.1.11 h1:PPrMCVVrAeR3f5X23I+cmvacXJ+kzuyAsBiWyUKhGSE=
|
||||||
github.com/sagernet/sing-dns v0.1.11/go.mod h1:zJ/YjnYB61SYE+ubMcMqVdpaSvsyQ2iShQGO3vuLvvE=
|
github.com/sagernet/sing-dns v0.1.11/go.mod h1:zJ/YjnYB61SYE+ubMcMqVdpaSvsyQ2iShQGO3vuLvvE=
|
||||||
github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM=
|
github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM=
|
||||||
|
@ -11,5 +11,6 @@ type URLTestOutboundOptions struct {
|
|||||||
URL string `json:"url,omitempty"`
|
URL string `json:"url,omitempty"`
|
||||||
Interval Duration `json:"interval,omitempty"`
|
Interval Duration `json:"interval,omitempty"`
|
||||||
Tolerance uint16 `json:"tolerance,omitempty"`
|
Tolerance uint16 `json:"tolerance,omitempty"`
|
||||||
|
IdleTimeout Duration `json:"idle_timeout,omitempty"`
|
||||||
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
InterruptExistConnections bool `json:"interrupt_exist_connections,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,7 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada
|
|||||||
}
|
}
|
||||||
timeout.Update()
|
timeout.Update()
|
||||||
responseBuffer := buf.NewPacket()
|
responseBuffer := buf.NewPacket()
|
||||||
|
responseBuffer.Resize(1024, 0)
|
||||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel(err)
|
cancel(err)
|
||||||
@ -241,6 +242,7 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
|
|||||||
}
|
}
|
||||||
timeout.Update()
|
timeout.Update()
|
||||||
responseBuffer := buf.NewPacket()
|
responseBuffer := buf.NewPacket()
|
||||||
|
responseBuffer.Resize(1024, 0)
|
||||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel(err)
|
cancel(err)
|
||||||
|
@ -3,7 +3,6 @@ package outbound
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"sort"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -36,9 +35,9 @@ type URLTest struct {
|
|||||||
link string
|
link string
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
tolerance uint16
|
tolerance uint16
|
||||||
|
idleTimeout time.Duration
|
||||||
group *URLTestGroup
|
group *URLTestGroup
|
||||||
interruptExternalConnections bool
|
interruptExternalConnections bool
|
||||||
started bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) {
|
func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.URLTestOutboundOptions) (*URLTest, error) {
|
||||||
@ -55,6 +54,7 @@ func NewURLTest(ctx context.Context, router adapter.Router, logger log.ContextLo
|
|||||||
link: options.URL,
|
link: options.URL,
|
||||||
interval: time.Duration(options.Interval),
|
interval: time.Duration(options.Interval),
|
||||||
tolerance: options.Tolerance,
|
tolerance: options.Tolerance,
|
||||||
|
idleTimeout: time.Duration(options.IdleTimeout),
|
||||||
interruptExternalConnections: options.InterruptExistConnections,
|
interruptExternalConnections: options.InterruptExistConnections,
|
||||||
}
|
}
|
||||||
if len(outbound.tags) == 0 {
|
if len(outbound.tags) == 0 {
|
||||||
@ -79,13 +79,26 @@ func (s *URLTest) Start() error {
|
|||||||
}
|
}
|
||||||
outbounds = append(outbounds, detour)
|
outbounds = append(outbounds, detour)
|
||||||
}
|
}
|
||||||
s.group = NewURLTestGroup(s.ctx, s.router, s.logger, outbounds, s.link, s.interval, s.tolerance, s.interruptExternalConnections)
|
group, err := NewURLTestGroup(
|
||||||
|
s.ctx,
|
||||||
|
s.router,
|
||||||
|
s.logger,
|
||||||
|
outbounds,
|
||||||
|
s.link,
|
||||||
|
s.interval,
|
||||||
|
s.tolerance,
|
||||||
|
s.idleTimeout,
|
||||||
|
s.interruptExternalConnections,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.group = group
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *URLTest) PostStart() error {
|
func (s *URLTest) PostStart() error {
|
||||||
s.started = true
|
s.group.PostStart()
|
||||||
go s.CheckOutbounds()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,8 +116,8 @@ func (s *URLTest) All() []string {
|
|||||||
return s.tags
|
return s.tags
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *URLTest) URLTest(ctx context.Context, link string) (map[string]uint16, error) {
|
func (s *URLTest) URLTest(ctx context.Context) (map[string]uint16, error) {
|
||||||
return s.group.URLTest(ctx, link)
|
return s.group.URLTest(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *URLTest) CheckOutbounds() {
|
func (s *URLTest) CheckOutbounds() {
|
||||||
@ -112,9 +125,7 @@ func (s *URLTest) CheckOutbounds() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
func (s *URLTest) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
|
||||||
if s.started {
|
s.group.Touch()
|
||||||
s.group.Start()
|
|
||||||
}
|
|
||||||
outbound := s.group.Select(network)
|
outbound := s.group.Select(network)
|
||||||
conn, err := outbound.DialContext(ctx, network, destination)
|
conn, err := outbound.DialContext(ctx, network, destination)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -126,9 +137,7 @@ func (s *URLTest) DialContext(ctx context.Context, network string, destination M
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
func (s *URLTest) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
|
||||||
if s.started {
|
s.group.Touch()
|
||||||
s.group.Start()
|
|
||||||
}
|
|
||||||
outbound := s.group.Select(N.NetworkUDP)
|
outbound := s.group.Select(N.NetworkUDP)
|
||||||
conn, err := outbound.ListenPacket(ctx, destination)
|
conn, err := outbound.ListenPacket(ctx, destination)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -162,6 +171,7 @@ type URLTestGroup struct {
|
|||||||
link string
|
link string
|
||||||
interval time.Duration
|
interval time.Duration
|
||||||
tolerance uint16
|
tolerance uint16
|
||||||
|
idleTimeout time.Duration
|
||||||
history *urltest.HistoryStorage
|
history *urltest.HistoryStorage
|
||||||
checking atomic.Bool
|
checking atomic.Bool
|
||||||
pauseManager pause.Manager
|
pauseManager pause.Manager
|
||||||
@ -170,9 +180,11 @@ type URLTestGroup struct {
|
|||||||
interruptGroup *interrupt.Group
|
interruptGroup *interrupt.Group
|
||||||
interruptExternalConnections bool
|
interruptExternalConnections bool
|
||||||
|
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
ticker *time.Ticker
|
ticker *time.Ticker
|
||||||
close chan struct{}
|
close chan struct{}
|
||||||
|
started bool
|
||||||
|
lastActive atomic.TypedValue[time.Time]
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewURLTestGroup(
|
func NewURLTestGroup(
|
||||||
@ -183,14 +195,21 @@ func NewURLTestGroup(
|
|||||||
link string,
|
link string,
|
||||||
interval time.Duration,
|
interval time.Duration,
|
||||||
tolerance uint16,
|
tolerance uint16,
|
||||||
|
idleTimeout time.Duration,
|
||||||
interruptExternalConnections bool,
|
interruptExternalConnections bool,
|
||||||
) *URLTestGroup {
|
) (*URLTestGroup, error) {
|
||||||
if interval == 0 {
|
if interval == 0 {
|
||||||
interval = C.DefaultURLTestInterval
|
interval = C.DefaultURLTestInterval
|
||||||
}
|
}
|
||||||
if tolerance == 0 {
|
if tolerance == 0 {
|
||||||
tolerance = 50
|
tolerance = 50
|
||||||
}
|
}
|
||||||
|
if idleTimeout == 0 {
|
||||||
|
idleTimeout = C.DefaultURLTestIdleTimeout
|
||||||
|
}
|
||||||
|
if interval > idleTimeout {
|
||||||
|
return nil, E.New("interval must be less or equal than idle_timeout")
|
||||||
|
}
|
||||||
var history *urltest.HistoryStorage
|
var history *urltest.HistoryStorage
|
||||||
if history = service.PtrFromContext[urltest.HistoryStorage](ctx); history != nil {
|
if history = service.PtrFromContext[urltest.HistoryStorage](ctx); history != nil {
|
||||||
} else if clashServer := router.ClashServer(); clashServer != nil {
|
} else if clashServer := router.ClashServer(); clashServer != nil {
|
||||||
@ -206,16 +225,27 @@ func NewURLTestGroup(
|
|||||||
link: link,
|
link: link,
|
||||||
interval: interval,
|
interval: interval,
|
||||||
tolerance: tolerance,
|
tolerance: tolerance,
|
||||||
|
idleTimeout: idleTimeout,
|
||||||
history: history,
|
history: history,
|
||||||
close: make(chan struct{}),
|
close: make(chan struct{}),
|
||||||
pauseManager: pause.ManagerFromContext(ctx),
|
pauseManager: pause.ManagerFromContext(ctx),
|
||||||
interruptGroup: interrupt.NewGroup(),
|
interruptGroup: interrupt.NewGroup(),
|
||||||
interruptExternalConnections: interruptExternalConnections,
|
interruptExternalConnections: interruptExternalConnections,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *URLTestGroup) Start() {
|
func (g *URLTestGroup) PostStart() {
|
||||||
|
g.started = true
|
||||||
|
g.lastActive.Store(time.Now())
|
||||||
|
go g.CheckOutbounds(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *URLTestGroup) Touch() {
|
||||||
|
if !g.started {
|
||||||
|
return
|
||||||
|
}
|
||||||
if g.ticker != nil {
|
if g.ticker != nil {
|
||||||
|
g.lastActive.Store(time.Now())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
g.access.Lock()
|
g.access.Lock()
|
||||||
@ -266,51 +296,38 @@ func (g *URLTestGroup) Select(network string) adapter.Outbound {
|
|||||||
return minOutbound
|
return minOutbound
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *URLTestGroup) Fallback(used adapter.Outbound) []adapter.Outbound {
|
|
||||||
outbounds := make([]adapter.Outbound, 0, len(g.outbounds)-1)
|
|
||||||
for _, detour := range g.outbounds {
|
|
||||||
if detour != used {
|
|
||||||
outbounds = append(outbounds, detour)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.SliceStable(outbounds, func(i, j int) bool {
|
|
||||||
oi := outbounds[i]
|
|
||||||
oj := outbounds[j]
|
|
||||||
hi := g.history.LoadURLTestHistory(RealTag(oi))
|
|
||||||
if hi == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
hj := g.history.LoadURLTestHistory(RealTag(oj))
|
|
||||||
if hj == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return hi.Delay < hj.Delay
|
|
||||||
})
|
|
||||||
return outbounds
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *URLTestGroup) loopCheck() {
|
func (g *URLTestGroup) loopCheck() {
|
||||||
go g.CheckOutbounds(true)
|
if time.Now().Sub(g.lastActive.Load()) > g.interval {
|
||||||
|
g.lastActive.Store(time.Now())
|
||||||
|
g.CheckOutbounds(false)
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
g.pauseManager.WaitActive()
|
|
||||||
select {
|
select {
|
||||||
case <-g.close:
|
case <-g.close:
|
||||||
return
|
return
|
||||||
case <-g.ticker.C:
|
case <-g.ticker.C:
|
||||||
g.CheckOutbounds(false)
|
|
||||||
}
|
}
|
||||||
|
if time.Now().Sub(g.lastActive.Load()) > g.idleTimeout {
|
||||||
|
g.access.Lock()
|
||||||
|
g.ticker.Stop()
|
||||||
|
g.ticker = nil
|
||||||
|
g.access.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.pauseManager.WaitActive()
|
||||||
|
g.CheckOutbounds(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *URLTestGroup) CheckOutbounds(force bool) {
|
func (g *URLTestGroup) CheckOutbounds(force bool) {
|
||||||
_, _ = g.urlTest(g.ctx, g.link, force)
|
_, _ = g.urlTest(g.ctx, force)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *URLTestGroup) URLTest(ctx context.Context, link string) (map[string]uint16, error) {
|
func (g *URLTestGroup) URLTest(ctx context.Context) (map[string]uint16, error) {
|
||||||
return g.urlTest(ctx, link, false)
|
return g.urlTest(ctx, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (map[string]uint16, error) {
|
func (g *URLTestGroup) urlTest(ctx context.Context, force bool) (map[string]uint16, error) {
|
||||||
result := make(map[string]uint16)
|
result := make(map[string]uint16)
|
||||||
if g.checking.Swap(true) {
|
if g.checking.Swap(true) {
|
||||||
return result, nil
|
return result, nil
|
||||||
@ -337,7 +354,7 @@ func (g *URLTestGroup) urlTest(ctx context.Context, link string, force bool) (ma
|
|||||||
b.Go(realTag, func() (any, error) {
|
b.Go(realTag, func() (any, error) {
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), C.TCPTimeout)
|
ctx, cancel := context.WithTimeout(context.Background(), C.TCPTimeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
t, err := urltest.URLTest(ctx, link, p)
|
t, err := urltest.URLTest(ctx, g.link, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
g.logger.Debug("outbound ", tag, " unavailable: ", err)
|
g.logger.Debug("outbound ", tag, " unavailable: ", err)
|
||||||
g.history.DeleteURLTestHistory(realTag)
|
g.history.DeleteURLTestHistory(realTag)
|
||||||
|
@ -91,6 +91,7 @@ type Router struct {
|
|||||||
platformInterface platform.Interface
|
platformInterface platform.Interface
|
||||||
needWIFIState bool
|
needWIFIState bool
|
||||||
wifiState adapter.WIFIState
|
wifiState adapter.WIFIState
|
||||||
|
started bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRouter(
|
func NewRouter(
|
||||||
@ -145,6 +146,9 @@ func NewRouter(
|
|||||||
router.dnsRules = append(router.dnsRules, dnsRule)
|
router.dnsRules = append(router.dnsRules, dnsRule)
|
||||||
}
|
}
|
||||||
for i, ruleSetOptions := range options.RuleSet {
|
for i, ruleSetOptions := range options.RuleSet {
|
||||||
|
if _, exists := router.ruleSetMap[ruleSetOptions.Tag]; exists {
|
||||||
|
return nil, E.New("duplicate rule-set tag: ", ruleSetOptions.Tag)
|
||||||
|
}
|
||||||
ruleSet, err := NewRuleSet(ctx, router, router.logger, ruleSetOptions)
|
ruleSet, err := NewRuleSet(ctx, router, router.logger, ruleSetOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "parse rule-set[", i, "]")
|
return nil, E.Cause(err, "parse rule-set[", i, "]")
|
||||||
@ -619,6 +623,19 @@ func (r *Router) Close() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Router) PostStart() error {
|
||||||
|
if len(r.ruleSets) > 0 {
|
||||||
|
for i, ruleSet := range r.ruleSets {
|
||||||
|
err := ruleSet.PostStart()
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "post start rule-set[", i, "]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.started = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
|
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
|
||||||
outbound, loaded := r.outboundByTag[tag]
|
outbound, loaded := r.outboundByTag[tag]
|
||||||
return outbound, loaded
|
return outbound, loaded
|
||||||
@ -1073,8 +1090,11 @@ func (r *Router) notifyNetworkUpdate(event int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.ResetNetwork()
|
if !r.started {
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = r.ResetNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Router) ResetNetwork() error {
|
func (r *Router) ResetNetwork() error {
|
||||||
|
@ -93,6 +93,12 @@ func (s *RemoteRuleSet) StartContext(ctx context.Context, startContext adapter.R
|
|||||||
s.lastEtag = savedSet.LastEtag
|
s.lastEtag = savedSet.LastEtag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if s.lastUpdated.IsZero() {
|
||||||
|
err := s.fetchOnce(ctx, startContext)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "initial rule-set: ", s.options.Tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
s.updateTicker = time.NewTicker(s.updateInterval)
|
s.updateTicker = time.NewTicker(s.updateInterval)
|
||||||
go s.loopUpdate()
|
go s.loopUpdate()
|
||||||
return nil
|
return nil
|
||||||
|
Loading…
x
Reference in New Issue
Block a user