Add initial_path option to remote rule-sets
This commit is contained in:
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
:material-plus: [http_client](#http_client)
|
:material-plus: [http_client](#http_client)
|
||||||
:material-delete-clock: [download_detour](#download_detour)
|
:material-delete-clock: [download_detour](#download_detour)
|
||||||
:material-alert: [tag](#tag)
|
:material-alert: [tag](#tag)
|
||||||
|
:material-plus: [initial_path](#initial_path)
|
||||||
|
|
||||||
!!! quote "Changes in sing-box 1.10.0"
|
!!! quote "Changes in sing-box 1.10.0"
|
||||||
|
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
"tag": "", // or []
|
"tag": "", // or []
|
||||||
"format": "source", // or binary
|
"format": "source", // or binary
|
||||||
"url": "",
|
"url": "",
|
||||||
|
"initial_path": "",
|
||||||
"http_client": "", // or {}
|
"http_client": "", // or {}
|
||||||
"update_interval": "",
|
"update_interval": "",
|
||||||
|
|
||||||
@@ -76,7 +78,7 @@ Tag of rule-set.
|
|||||||
|
|
||||||
`tag` also accepts a list of tags to define multiple rule-sets sharing other options at once.
|
`tag` also accepts a list of tags to define multiple rule-sets sharing other options at once.
|
||||||
|
|
||||||
The `{tag}` placeholder in `path` or `url` is replaced by each tag,
|
The `{tag}` placeholder in `path`, `url` or `initial_path` is replaced by each tag,
|
||||||
and is required when multiple tags are set.
|
and is required when multiple tags are set.
|
||||||
|
|
||||||
Multiple tags conflict with `type: inline`.
|
Multiple tags conflict with `type: inline`.
|
||||||
@@ -121,6 +123,16 @@ File path of rule-set.
|
|||||||
|
|
||||||
Download URL of rule-set.
|
Download URL of rule-set.
|
||||||
|
|
||||||
|
#### initial_path
|
||||||
|
|
||||||
|
!!! question "Since sing-box 1.14.0"
|
||||||
|
|
||||||
|
File path of the initial rule-set content.
|
||||||
|
|
||||||
|
Read once at startup when no cached rule-set is available, so startup is not
|
||||||
|
blocked by the initial download. The rule-set is still updated in the background
|
||||||
|
immediately after startup.
|
||||||
|
|
||||||
#### http_client
|
#### http_client
|
||||||
|
|
||||||
!!! question "Since sing-box 1.14.0"
|
!!! question "Since sing-box 1.14.0"
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
:material-plus: [http_client](#http_client)
|
:material-plus: [http_client](#http_client)
|
||||||
:material-delete-clock: [download_detour](#download_detour)
|
:material-delete-clock: [download_detour](#download_detour)
|
||||||
:material-alert: [tag](#tag)
|
:material-alert: [tag](#tag)
|
||||||
|
:material-plus: [initial_path](#initial_path)
|
||||||
|
|
||||||
!!! quote "sing-box 1.10.0 中的更改"
|
!!! quote "sing-box 1.10.0 中的更改"
|
||||||
|
|
||||||
@@ -49,6 +50,7 @@
|
|||||||
"tag": "", // 或 []
|
"tag": "", // 或 []
|
||||||
"format": "source", // or binary
|
"format": "source", // or binary
|
||||||
"url": "",
|
"url": "",
|
||||||
|
"initial_path": "",
|
||||||
"http_client": "", // 或 {}
|
"http_client": "", // 或 {}
|
||||||
"update_interval": "",
|
"update_interval": "",
|
||||||
|
|
||||||
@@ -76,7 +78,7 @@
|
|||||||
|
|
||||||
`tag` 也接受一组标签,用于一次定义多个共享其他选项的规则集。
|
`tag` 也接受一组标签,用于一次定义多个共享其他选项的规则集。
|
||||||
|
|
||||||
`path` 或 `url` 中的 `{tag}` 占位符将被替换为每个标签,设置多个标签时必填。
|
`path`、`url` 或 `initial_path` 中的 `{tag}` 占位符将被替换为每个标签,设置多个标签时必填。
|
||||||
|
|
||||||
多个标签与 `type: inline` 冲突。
|
多个标签与 `type: inline` 冲突。
|
||||||
|
|
||||||
@@ -120,6 +122,15 @@
|
|||||||
|
|
||||||
规则集的下载 URL。
|
规则集的下载 URL。
|
||||||
|
|
||||||
|
#### initial_path
|
||||||
|
|
||||||
|
!!! question "自 sing-box 1.14.0 起"
|
||||||
|
|
||||||
|
规则集初始内容的文件路径。
|
||||||
|
|
||||||
|
仅在启动时没有可用的规则集缓存时读取一次,使启动不被初始下载阻塞。
|
||||||
|
启动后规则集仍会立即在后台更新。
|
||||||
|
|
||||||
#### http_client
|
#### http_client
|
||||||
|
|
||||||
!!! question "自 sing-box 1.14.0 起"
|
!!! question "自 sing-box 1.14.0 起"
|
||||||
|
|||||||
@@ -15327,6 +15327,9 @@
|
|||||||
"url": {
|
"url": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"initial_path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"http_client": {
|
"http_client": {
|
||||||
"$ref": "#/$defs/HTTPClientReference"
|
"$ref": "#/$defs/HTTPClientReference"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ func (r *RuleSet) UnmarshalJSON(bytes []byte) error {
|
|||||||
if !strings.Contains(r.RemoteOptions.URL, C.RuleSetTagPlaceholder) {
|
if !strings.Contains(r.RemoteOptions.URL, C.RuleSetTagPlaceholder) {
|
||||||
return E.New("missing ", C.RuleSetTagPlaceholder, " placeholder in url")
|
return E.New("missing ", C.RuleSetTagPlaceholder, " placeholder in url")
|
||||||
}
|
}
|
||||||
|
if r.RemoteOptions.InitialPath != "" && !strings.Contains(r.RemoteOptions.InitialPath, C.RuleSetTagPlaceholder) {
|
||||||
|
return E.New("missing ", C.RuleSetTagPlaceholder, " placeholder in initial_path")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@@ -177,6 +180,7 @@ type LocalRuleSet struct {
|
|||||||
|
|
||||||
type RemoteRuleSet struct {
|
type RemoteRuleSet struct {
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
|
InitialPath string `json:"initial_path,omitempty"`
|
||||||
HTTPClient *HTTPClientOptions `json:"http_client,omitempty"`
|
HTTPClient *HTTPClientOptions `json:"http_client,omitempty"`
|
||||||
UpdateInterval badoption.Duration `json:"update_interval,omitempty"`
|
UpdateInterval badoption.Duration `json:"update_interval,omitempty"`
|
||||||
// Deprecated: use http_client instead
|
// Deprecated: use http_client instead
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@@ -22,6 +23,7 @@ import (
|
|||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
"github.com/sagernet/sing/common/x/list"
|
"github.com/sagernet/sing/common/x/list"
|
||||||
"github.com/sagernet/sing/service"
|
"github.com/sagernet/sing/service"
|
||||||
|
"github.com/sagernet/sing/service/filemanager"
|
||||||
"github.com/sagernet/sing/service/pause"
|
"github.com/sagernet/sing/service/pause"
|
||||||
|
|
||||||
"go4.org/netipx"
|
"go4.org/netipx"
|
||||||
@@ -36,6 +38,7 @@ type RemoteRuleSet struct {
|
|||||||
outbound adapter.OutboundManager
|
outbound adapter.OutboundManager
|
||||||
tag string
|
tag string
|
||||||
url string
|
url string
|
||||||
|
initialPath string
|
||||||
options option.RuleSet
|
options option.RuleSet
|
||||||
updateInterval time.Duration
|
updateInterval time.Duration
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
@@ -58,6 +61,11 @@ func NewRemoteRuleSet(ctx context.Context, logger logger.ContextLogger, tag stri
|
|||||||
} else {
|
} else {
|
||||||
updateInterval = 24 * time.Hour
|
updateInterval = 24 * time.Hour
|
||||||
}
|
}
|
||||||
|
var initialPath string
|
||||||
|
if options.RemoteOptions.InitialPath != "" {
|
||||||
|
initialPath = filemanager.BasePath(ctx, strings.ReplaceAll(options.RemoteOptions.InitialPath, C.RuleSetTagPlaceholder, tag))
|
||||||
|
initialPath, _ = filepath.Abs(initialPath)
|
||||||
|
}
|
||||||
return &RemoteRuleSet{
|
return &RemoteRuleSet{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
@@ -65,6 +73,7 @@ func NewRemoteRuleSet(ctx context.Context, logger logger.ContextLogger, tag stri
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
url: strings.ReplaceAll(options.RemoteOptions.URL, C.RuleSetTagPlaceholder, tag),
|
url: strings.ReplaceAll(options.RemoteOptions.URL, C.RuleSetTagPlaceholder, tag),
|
||||||
|
initialPath: initialPath,
|
||||||
options: options,
|
options: options,
|
||||||
updateInterval: updateInterval,
|
updateInterval: updateInterval,
|
||||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||||
@@ -98,7 +107,20 @@ func (s *RemoteRuleSet) StartContext(ctx context.Context, startContext *adapter.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.lastUpdated.IsZero() {
|
var loadedFromInitialPath bool
|
||||||
|
if s.lastUpdated.IsZero() && s.initialPath != "" {
|
||||||
|
var content []byte
|
||||||
|
content, err = filemanager.ReadFile(s.ctx, s.initialPath)
|
||||||
|
if err == nil {
|
||||||
|
err = s.loadBytes(content)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Warn(E.Cause(err, "load initial rule-set from ", s.initialPath))
|
||||||
|
} else {
|
||||||
|
loadedFromInitialPath = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.lastUpdated.IsZero() && !loadedFromInitialPath {
|
||||||
err = s.fetch(ctx, true)
|
err = s.fetch(ctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return E.Cause(err, "initial rule-set: ", s.tag)
|
return E.Cause(err, "initial rule-set: ", s.tag)
|
||||||
|
|||||||
Reference in New Issue
Block a user