Skip to content

Commit bbea3aa

Browse files
committed
Migrate to independent cache file
1 parent 1c1eb01 commit bbea3aa

File tree

16 files changed

+184
-171
lines changed

16 files changed

+184
-171
lines changed

adapter/experimental.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@ type ClashServer interface {
1313
PreStarter
1414
Mode() string
1515
ModeList() []string
16-
StoreSelected() bool
17-
StoreFakeIP() bool
18-
CacheFile() ClashCacheFile
1916
HistoryStorage() *urltest.HistoryStorage
2017
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
2118
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
2219
}
2320

24-
type ClashCacheFile interface {
21+
type CacheFile interface {
22+
Service
23+
PreStarter
24+
25+
StoreFakeIP() bool
26+
2527
LoadMode() string
2628
StoreMode(mode string) error
2729
LoadSelected(group string) string

box.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
"github.com/sagernet/sing-box/adapter"
1212
"github.com/sagernet/sing-box/experimental"
13+
"github.com/sagernet/sing-box/experimental/cachefile"
1314
"github.com/sagernet/sing-box/experimental/libbox/platform"
1415
"github.com/sagernet/sing-box/inbound"
1516
"github.com/sagernet/sing-box/log"
@@ -45,17 +46,21 @@ type Options struct {
4546
}
4647

4748
func New(options Options) (*Box, error) {
49+
createdAt := time.Now()
4850
ctx := options.Context
4951
if ctx == nil {
5052
ctx = context.Background()
5153
}
5254
ctx = service.ContextWithDefaultRegistry(ctx)
5355
ctx = pause.ContextWithDefaultManager(ctx)
54-
createdAt := time.Now()
5556
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
5657
applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
58+
var needCacheFile bool
5759
var needClashAPI bool
5860
var needV2RayAPI bool
61+
if experimentalOptions.CacheFile != nil && experimentalOptions.CacheFile.Enabled || options.PlatformLogWriter != nil {
62+
needCacheFile = true
63+
}
5964
if experimentalOptions.ClashAPI != nil || options.PlatformLogWriter != nil {
6065
needClashAPI = true
6166
}
@@ -147,6 +152,11 @@ func New(options Options) (*Box, error) {
147152
}
148153
preServices := make(map[string]adapter.Service)
149154
postServices := make(map[string]adapter.Service)
155+
if needCacheFile {
156+
cacheFile := cachefile.NewCacheFile(ctx, common.PtrValueOrDefault(experimentalOptions.CacheFile))
157+
preServices["cache file"] = cacheFile
158+
service.MustRegister[adapter.CacheFile](ctx, cacheFile)
159+
}
150160
if needClashAPI {
151161
clashAPIOptions := common.PtrValueOrDefault(experimentalOptions.ClashAPI)
152162
clashAPIOptions.ModeList = experimental.CalculateClashModeList(options.Options)

experimental/clashapi/cachefile/cache.go experimental/cachefile/cache.go

+51-25
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/sagernet/bbolt"
1313
bboltErrors "github.com/sagernet/bbolt/errors"
1414
"github.com/sagernet/sing-box/adapter"
15+
"github.com/sagernet/sing-box/option"
1516
"github.com/sagernet/sing/common"
1617
E "github.com/sagernet/sing/common/exceptions"
1718
"github.com/sagernet/sing/service/filemanager"
@@ -31,51 +32,68 @@ var (
3132
cacheIDDefault = []byte("default")
3233
)
3334

34-
var _ adapter.ClashCacheFile = (*CacheFile)(nil)
35+
var _ adapter.CacheFile = (*CacheFile)(nil)
3536

3637
type CacheFile struct {
38+
ctx context.Context
39+
path string
40+
cacheID []byte
41+
storeFakeIP bool
42+
3743
DB *bbolt.DB
38-
cacheID []byte
3944
saveAccess sync.RWMutex
4045
saveDomain map[netip.Addr]string
4146
saveAddress4 map[string]netip.Addr
4247
saveAddress6 map[string]netip.Addr
4348
saveMetadataTimer *time.Timer
4449
}
4550

46-
func Open(ctx context.Context, path string, cacheID string) (*CacheFile, error) {
51+
func NewCacheFile(ctx context.Context, options option.CacheFileOptions) *CacheFile {
52+
var cacheIDBytes []byte
53+
if options.CacheID != "" {
54+
cacheIDBytes = append([]byte{0}, []byte(options.CacheID)...)
55+
}
56+
return &CacheFile{
57+
ctx: ctx,
58+
path: options.Path,
59+
cacheID: cacheIDBytes,
60+
storeFakeIP: options.StoreFakeIP,
61+
saveDomain: make(map[netip.Addr]string),
62+
saveAddress4: make(map[string]netip.Addr),
63+
saveAddress6: make(map[string]netip.Addr),
64+
}
65+
}
66+
67+
func (c *CacheFile) start() error {
4768
const fileMode = 0o666
4869
options := bbolt.Options{Timeout: time.Second}
4970
var (
5071
db *bbolt.DB
5172
err error
5273
)
5374
for i := 0; i < 10; i++ {
54-
db, err = bbolt.Open(path, fileMode, &options)
75+
db, err = bbolt.Open(c.path, fileMode, &options)
5576
if err == nil {
5677
break
5778
}
5879
if errors.Is(err, bboltErrors.ErrTimeout) {
5980
continue
6081
}
6182
if E.IsMulti(err, bboltErrors.ErrInvalid, bboltErrors.ErrChecksum, bboltErrors.ErrVersionMismatch) {
62-
rmErr := os.Remove(path)
83+
rmErr := os.Remove(c.path)
6384
if rmErr != nil {
64-
return nil, err
85+
return err
6586
}
6687
}
6788
time.Sleep(100 * time.Millisecond)
6889
}
6990
if err != nil {
70-
return nil, err
91+
return err
7192
}
72-
err = filemanager.Chown(ctx, path)
93+
err = filemanager.Chown(c.ctx, c.path)
7394
if err != nil {
74-
return nil, E.Cause(err, "platform chown")
75-
}
76-
var cacheIDBytes []byte
77-
if cacheID != "" {
78-
cacheIDBytes = append([]byte{0}, []byte(cacheID)...)
95+
db.Close()
96+
return E.Cause(err, "platform chown")
7997
}
8098
err = db.Batch(func(tx *bbolt.Tx) error {
8199
return tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
@@ -97,15 +115,27 @@ func Open(ctx context.Context, path string, cacheID string) (*CacheFile, error)
97115
})
98116
})
99117
if err != nil {
100-
return nil, err
118+
db.Close()
119+
return err
101120
}
102-
return &CacheFile{
103-
DB: db,
104-
cacheID: cacheIDBytes,
105-
saveDomain: make(map[netip.Addr]string),
106-
saveAddress4: make(map[string]netip.Addr),
107-
saveAddress6: make(map[string]netip.Addr),
108-
}, nil
121+
c.DB = db
122+
return nil
123+
}
124+
125+
func (c *CacheFile) PreStart() error {
126+
return c.start()
127+
}
128+
129+
func (c *CacheFile) Start() error {
130+
return nil
131+
}
132+
133+
func (c *CacheFile) Close() error {
134+
return c.DB.Close()
135+
}
136+
137+
func (c *CacheFile) StoreFakeIP() bool {
138+
return c.storeFakeIP
109139
}
110140

111141
func (c *CacheFile) LoadMode() string {
@@ -218,7 +248,3 @@ func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
218248
}
219249
})
220250
}
221-
222-
func (c *CacheFile) Close() error {
223-
return c.DB.Close()
224-
}
File renamed without changes.

experimental/clashapi/cache.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
package clashapi
22

33
import (
4+
"context"
45
"net/http"
56

67
"github.com/sagernet/sing-box/adapter"
8+
"github.com/sagernet/sing/service"
79

810
"github.com/go-chi/chi/v5"
911
"github.com/go-chi/render"
1012
)
1113

12-
func cacheRouter(router adapter.Router) http.Handler {
14+
func cacheRouter(ctx context.Context) http.Handler {
1315
r := chi.NewRouter()
14-
r.Post("/fakeip/flush", flushFakeip(router))
16+
r.Post("/fakeip/flush", flushFakeip(ctx))
1517
return r
1618
}
1719

18-
func flushFakeip(router adapter.Router) func(w http.ResponseWriter, r *http.Request) {
20+
func flushFakeip(ctx context.Context) func(w http.ResponseWriter, r *http.Request) {
1921
return func(w http.ResponseWriter, r *http.Request) {
20-
if cacheFile := router.ClashServer().CacheFile(); cacheFile != nil {
22+
cacheFile := service.FromContext[adapter.CacheFile](ctx)
23+
if cacheFile != nil {
2124
err := cacheFile.FakeIPReset()
2225
if err != nil {
2326
render.Status(r, http.StatusInternalServerError)

experimental/clashapi/server.go

+14-51
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"github.com/sagernet/sing-box/common/urltest"
1616
C "github.com/sagernet/sing-box/constant"
1717
"github.com/sagernet/sing-box/experimental"
18-
"github.com/sagernet/sing-box/experimental/clashapi/cachefile"
1918
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
2019
"github.com/sagernet/sing-box/log"
2120
"github.com/sagernet/sing-box/option"
@@ -49,12 +48,6 @@ type Server struct {
4948
mode string
5049
modeList []string
5150
modeUpdateHook chan<- struct{}
52-
storeMode bool
53-
storeSelected bool
54-
storeFakeIP bool
55-
cacheFilePath string
56-
cacheID string
57-
cacheFile adapter.ClashCacheFile
5851

5952
externalController bool
6053
externalUI string
@@ -76,9 +69,6 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
7669
trafficManager: trafficManager,
7770
modeList: options.ModeList,
7871
externalController: options.ExternalController != "",
79-
storeMode: options.StoreMode,
80-
storeSelected: options.StoreSelected,
81-
storeFakeIP: options.StoreFakeIP,
8272
externalUIDownloadURL: options.ExternalUIDownloadURL,
8373
externalUIDownloadDetour: options.ExternalUIDownloadDetour,
8474
}
@@ -94,18 +84,9 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
9484
server.modeList = append([]string{defaultMode}, server.modeList...)
9585
}
9686
server.mode = defaultMode
97-
if options.StoreMode || options.StoreSelected || options.StoreFakeIP || options.ExternalController == "" {
98-
cachePath := os.ExpandEnv(options.CacheFile)
99-
if cachePath == "" {
100-
cachePath = "cache.db"
101-
}
102-
if foundPath, loaded := C.FindPath(cachePath); loaded {
103-
cachePath = foundPath
104-
} else {
105-
cachePath = filemanager.BasePath(ctx, cachePath)
106-
}
107-
server.cacheFilePath = cachePath
108-
server.cacheID = options.CacheID
87+
//goland:noinspection GoDeprecation
88+
if options.StoreMode || options.StoreSelected || options.StoreFakeIP || options.CacheFile != "" || options.CacheID != "" {
89+
return nil, E.New("cache_file and related fields in Clash API is deprecated in sing-box 1.8.0, use experimental.cache_file instead.")
10990
}
11091
cors := cors.New(cors.Options{
11192
AllowedOrigins: []string{"*"},
@@ -128,7 +109,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
128109
r.Mount("/providers/rules", ruleProviderRouter())
129110
r.Mount("/script", scriptRouter())
130111
r.Mount("/profile", profileRouter())
131-
r.Mount("/cache", cacheRouter(router))
112+
r.Mount("/cache", cacheRouter(ctx))
132113
r.Mount("/dns", dnsRouter(router))
133114

134115
server.setupMetaAPI(r)
@@ -147,19 +128,13 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
147128
}
148129

149130
func (s *Server) PreStart() error {
150-
if s.cacheFilePath != "" {
151-
cacheFile, err := cachefile.Open(s.ctx, s.cacheFilePath, s.cacheID)
152-
if err != nil {
153-
return E.Cause(err, "open cache file")
154-
}
155-
s.cacheFile = cacheFile
156-
if s.storeMode {
157-
mode := s.cacheFile.LoadMode()
158-
if common.Any(s.modeList, func(it string) bool {
159-
return strings.EqualFold(it, mode)
160-
}) {
161-
s.mode = mode
162-
}
131+
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
132+
if cacheFile != nil {
133+
mode := cacheFile.LoadMode()
134+
if common.Any(s.modeList, func(it string) bool {
135+
return strings.EqualFold(it, mode)
136+
}) {
137+
s.mode = mode
163138
}
164139
}
165140
return nil
@@ -187,7 +162,6 @@ func (s *Server) Close() error {
187162
return common.Close(
188163
common.PtrOrNil(s.httpServer),
189164
s.trafficManager,
190-
s.cacheFile,
191165
s.urlTestHistory,
192166
)
193167
}
@@ -224,27 +198,16 @@ func (s *Server) SetMode(newMode string) {
224198
}
225199
}
226200
s.router.ClearDNSCache()
227-
if s.storeMode {
228-
err := s.cacheFile.StoreMode(newMode)
201+
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
202+
if cacheFile != nil {
203+
err := cacheFile.StoreMode(newMode)
229204
if err != nil {
230205
s.logger.Error(E.Cause(err, "save mode"))
231206
}
232207
}
233208
s.logger.Info("updated mode: ", newMode)
234209
}
235210

236-
func (s *Server) StoreSelected() bool {
237-
return s.storeSelected
238-
}
239-
240-
func (s *Server) StoreFakeIP() bool {
241-
return s.storeFakeIP
242-
}
243-
244-
func (s *Server) CacheFile() adapter.ClashCacheFile {
245-
return s.cacheFile
246-
}
247-
248211
func (s *Server) HistoryStorage() *urltest.HistoryStorage {
249212
return s.urlTestHistory
250213
}

experimental/libbox/command_group.go

+8-13
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,7 @@ func readGroups(reader io.Reader) (OutboundGroupIterator, error) {
159159

160160
func writeGroups(writer io.Writer, boxService *BoxService) error {
161161
historyStorage := service.PtrFromContext[urltest.HistoryStorage](boxService.ctx)
162-
var cacheFile adapter.ClashCacheFile
163-
if clashServer := boxService.instance.Router().ClashServer(); clashServer != nil {
164-
cacheFile = clashServer.CacheFile()
165-
}
166-
162+
cacheFile := service.FromContext[adapter.CacheFile](boxService.ctx)
167163
outbounds := boxService.instance.Router().Outbounds()
168164
var iGroups []adapter.OutboundGroup
169165
for _, it := range outbounds {
@@ -288,16 +284,15 @@ func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
288284
if err != nil {
289285
return err
290286
}
291-
service := s.service
292-
if service == nil {
287+
serviceNow := s.service
288+
if serviceNow == nil {
293289
return writeError(conn, E.New("service not ready"))
294290
}
295-
if clashServer := service.instance.Router().ClashServer(); clashServer != nil {
296-
if cacheFile := clashServer.CacheFile(); cacheFile != nil {
297-
err = cacheFile.StoreGroupExpand(groupTag, isExpand)
298-
if err != nil {
299-
return writeError(conn, err)
300-
}
291+
cacheFile := service.FromContext[adapter.CacheFile](serviceNow.ctx)
292+
if cacheFile != nil {
293+
err = cacheFile.StoreGroupExpand(groupTag, isExpand)
294+
if err != nil {
295+
return writeError(conn, err)
301296
}
302297
}
303298
return writeError(conn, nil)

0 commit comments

Comments
 (0)