Skip to content

Commit

Permalink
Migrate to independent cache file
Browse files Browse the repository at this point in the history
  • Loading branch information
nekohasekai committed Nov 28, 2023
1 parent 1c1eb01 commit bbea3aa
Show file tree
Hide file tree
Showing 16 changed files with 184 additions and 171 deletions.
10 changes: 6 additions & 4 deletions adapter/experimental.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ type ClashServer interface {
PreStarter
Mode() string
ModeList() []string
StoreSelected() bool
StoreFakeIP() bool
CacheFile() ClashCacheFile
HistoryStorage() *urltest.HistoryStorage
RoutedConnection(ctx context.Context, conn net.Conn, metadata InboundContext, matchedRule Rule) (net.Conn, Tracker)
RoutedPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext, matchedRule Rule) (N.PacketConn, Tracker)
}

type ClashCacheFile interface {
type CacheFile interface {
Service
PreStarter

StoreFakeIP() bool

LoadMode() string
StoreMode(mode string) error
LoadSelected(group string) string
Expand Down
12 changes: 11 additions & 1 deletion box.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/experimental/cachefile"
"github.com/sagernet/sing-box/experimental/libbox/platform"
"github.com/sagernet/sing-box/inbound"
"github.com/sagernet/sing-box/log"
Expand Down Expand Up @@ -45,17 +46,21 @@ type Options struct {
}

func New(options Options) (*Box, error) {
createdAt := time.Now()
ctx := options.Context
if ctx == nil {
ctx = context.Background()
}
ctx = service.ContextWithDefaultRegistry(ctx)
ctx = pause.ContextWithDefaultManager(ctx)
createdAt := time.Now()
experimentalOptions := common.PtrValueOrDefault(options.Experimental)
applyDebugOptions(common.PtrValueOrDefault(experimentalOptions.Debug))
var needCacheFile bool
var needClashAPI bool
var needV2RayAPI bool
if experimentalOptions.CacheFile != nil && experimentalOptions.CacheFile.Enabled || options.PlatformLogWriter != nil {
needCacheFile = true
}
if experimentalOptions.ClashAPI != nil || options.PlatformLogWriter != nil {
needClashAPI = true
}
Expand Down Expand Up @@ -147,6 +152,11 @@ func New(options Options) (*Box, error) {
}
preServices := make(map[string]adapter.Service)
postServices := make(map[string]adapter.Service)
if needCacheFile {
cacheFile := cachefile.NewCacheFile(ctx, common.PtrValueOrDefault(experimentalOptions.CacheFile))
preServices["cache file"] = cacheFile
service.MustRegister[adapter.CacheFile](ctx, cacheFile)
}
if needClashAPI {
clashAPIOptions := common.PtrValueOrDefault(experimentalOptions.ClashAPI)
clashAPIOptions.ModeList = experimental.CalculateClashModeList(options.Options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/sagernet/bbolt"
bboltErrors "github.com/sagernet/bbolt/errors"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/service/filemanager"
Expand All @@ -31,51 +32,68 @@ var (
cacheIDDefault = []byte("default")
)

var _ adapter.ClashCacheFile = (*CacheFile)(nil)
var _ adapter.CacheFile = (*CacheFile)(nil)

type CacheFile struct {
ctx context.Context
path string
cacheID []byte
storeFakeIP bool

DB *bbolt.DB
cacheID []byte
saveAccess sync.RWMutex
saveDomain map[netip.Addr]string
saveAddress4 map[string]netip.Addr
saveAddress6 map[string]netip.Addr
saveMetadataTimer *time.Timer
}

func Open(ctx context.Context, path string, cacheID string) (*CacheFile, error) {
func NewCacheFile(ctx context.Context, options option.CacheFileOptions) *CacheFile {
var cacheIDBytes []byte
if options.CacheID != "" {
cacheIDBytes = append([]byte{0}, []byte(options.CacheID)...)
}
return &CacheFile{
ctx: ctx,
path: options.Path,
cacheID: cacheIDBytes,
storeFakeIP: options.StoreFakeIP,
saveDomain: make(map[netip.Addr]string),
saveAddress4: make(map[string]netip.Addr),
saveAddress6: make(map[string]netip.Addr),
}
}

func (c *CacheFile) start() error {
const fileMode = 0o666
options := bbolt.Options{Timeout: time.Second}
var (
db *bbolt.DB
err error
)
for i := 0; i < 10; i++ {
db, err = bbolt.Open(path, fileMode, &options)
db, err = bbolt.Open(c.path, fileMode, &options)
if err == nil {
break
}
if errors.Is(err, bboltErrors.ErrTimeout) {
continue
}
if E.IsMulti(err, bboltErrors.ErrInvalid, bboltErrors.ErrChecksum, bboltErrors.ErrVersionMismatch) {
rmErr := os.Remove(path)
rmErr := os.Remove(c.path)
if rmErr != nil {
return nil, err
return err
}
}
time.Sleep(100 * time.Millisecond)
}
if err != nil {
return nil, err
return err
}
err = filemanager.Chown(ctx, path)
err = filemanager.Chown(c.ctx, c.path)
if err != nil {
return nil, E.Cause(err, "platform chown")
}
var cacheIDBytes []byte
if cacheID != "" {
cacheIDBytes = append([]byte{0}, []byte(cacheID)...)
db.Close()
return E.Cause(err, "platform chown")
}
err = db.Batch(func(tx *bbolt.Tx) error {
return tx.ForEach(func(name []byte, b *bbolt.Bucket) error {
Expand All @@ -97,15 +115,27 @@ func Open(ctx context.Context, path string, cacheID string) (*CacheFile, error)
})
})
if err != nil {
return nil, err
db.Close()
return err
}
return &CacheFile{
DB: db,
cacheID: cacheIDBytes,
saveDomain: make(map[netip.Addr]string),
saveAddress4: make(map[string]netip.Addr),
saveAddress6: make(map[string]netip.Addr),
}, nil
c.DB = db
return nil
}

func (c *CacheFile) PreStart() error {
return c.start()
}

func (c *CacheFile) Start() error {
return nil
}

func (c *CacheFile) Close() error {
return c.DB.Close()
}

func (c *CacheFile) StoreFakeIP() bool {
return c.storeFakeIP
}

func (c *CacheFile) LoadMode() string {
Expand Down Expand Up @@ -218,7 +248,3 @@ func (c *CacheFile) StoreGroupExpand(group string, isExpand bool) error {
}
})
}

func (c *CacheFile) Close() error {
return c.DB.Close()
}
File renamed without changes.
11 changes: 7 additions & 4 deletions experimental/clashapi/cache.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
package clashapi

import (
"context"
"net/http"

"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/service"

"github.com/go-chi/chi/v5"
"github.com/go-chi/render"
)

func cacheRouter(router adapter.Router) http.Handler {
func cacheRouter(ctx context.Context) http.Handler {
r := chi.NewRouter()
r.Post("/fakeip/flush", flushFakeip(router))
r.Post("/fakeip/flush", flushFakeip(ctx))
return r
}

func flushFakeip(router adapter.Router) func(w http.ResponseWriter, r *http.Request) {
func flushFakeip(ctx context.Context) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if cacheFile := router.ClashServer().CacheFile(); cacheFile != nil {
cacheFile := service.FromContext[adapter.CacheFile](ctx)
if cacheFile != nil {
err := cacheFile.FakeIPReset()
if err != nil {
render.Status(r, http.StatusInternalServerError)
Expand Down
65 changes: 14 additions & 51 deletions experimental/clashapi/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (
"github.com/sagernet/sing-box/common/urltest"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/experimental"
"github.com/sagernet/sing-box/experimental/clashapi/cachefile"
"github.com/sagernet/sing-box/experimental/clashapi/trafficontrol"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
Expand Down Expand Up @@ -49,12 +48,6 @@ type Server struct {
mode string
modeList []string
modeUpdateHook chan<- struct{}
storeMode bool
storeSelected bool
storeFakeIP bool
cacheFilePath string
cacheID string
cacheFile adapter.ClashCacheFile

externalController bool
externalUI string
Expand All @@ -76,9 +69,6 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
trafficManager: trafficManager,
modeList: options.ModeList,
externalController: options.ExternalController != "",
storeMode: options.StoreMode,
storeSelected: options.StoreSelected,
storeFakeIP: options.StoreFakeIP,
externalUIDownloadURL: options.ExternalUIDownloadURL,
externalUIDownloadDetour: options.ExternalUIDownloadDetour,
}
Expand All @@ -94,18 +84,9 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
server.modeList = append([]string{defaultMode}, server.modeList...)
}
server.mode = defaultMode
if options.StoreMode || options.StoreSelected || options.StoreFakeIP || options.ExternalController == "" {
cachePath := os.ExpandEnv(options.CacheFile)
if cachePath == "" {
cachePath = "cache.db"
}
if foundPath, loaded := C.FindPath(cachePath); loaded {
cachePath = foundPath
} else {
cachePath = filemanager.BasePath(ctx, cachePath)
}
server.cacheFilePath = cachePath
server.cacheID = options.CacheID
//goland:noinspection GoDeprecation
if options.StoreMode || options.StoreSelected || options.StoreFakeIP || options.CacheFile != "" || options.CacheID != "" {
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.")
}
cors := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
Expand All @@ -128,7 +109,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ
r.Mount("/providers/rules", ruleProviderRouter())
r.Mount("/script", scriptRouter())
r.Mount("/profile", profileRouter())
r.Mount("/cache", cacheRouter(router))
r.Mount("/cache", cacheRouter(ctx))
r.Mount("/dns", dnsRouter(router))

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

func (s *Server) PreStart() error {
if s.cacheFilePath != "" {
cacheFile, err := cachefile.Open(s.ctx, s.cacheFilePath, s.cacheID)
if err != nil {
return E.Cause(err, "open cache file")
}
s.cacheFile = cacheFile
if s.storeMode {
mode := s.cacheFile.LoadMode()
if common.Any(s.modeList, func(it string) bool {
return strings.EqualFold(it, mode)
}) {
s.mode = mode
}
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
if cacheFile != nil {
mode := cacheFile.LoadMode()
if common.Any(s.modeList, func(it string) bool {
return strings.EqualFold(it, mode)
}) {
s.mode = mode
}
}
return nil
Expand Down Expand Up @@ -187,7 +162,6 @@ func (s *Server) Close() error {
return common.Close(
common.PtrOrNil(s.httpServer),
s.trafficManager,
s.cacheFile,
s.urlTestHistory,
)
}
Expand Down Expand Up @@ -224,27 +198,16 @@ func (s *Server) SetMode(newMode string) {
}
}
s.router.ClearDNSCache()
if s.storeMode {
err := s.cacheFile.StoreMode(newMode)
cacheFile := service.FromContext[adapter.CacheFile](s.ctx)
if cacheFile != nil {
err := cacheFile.StoreMode(newMode)
if err != nil {
s.logger.Error(E.Cause(err, "save mode"))
}
}
s.logger.Info("updated mode: ", newMode)
}

func (s *Server) StoreSelected() bool {
return s.storeSelected
}

func (s *Server) StoreFakeIP() bool {
return s.storeFakeIP
}

func (s *Server) CacheFile() adapter.ClashCacheFile {
return s.cacheFile
}

func (s *Server) HistoryStorage() *urltest.HistoryStorage {
return s.urlTestHistory
}
Expand Down
21 changes: 8 additions & 13 deletions experimental/libbox/command_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,7 @@ func readGroups(reader io.Reader) (OutboundGroupIterator, error) {

func writeGroups(writer io.Writer, boxService *BoxService) error {
historyStorage := service.PtrFromContext[urltest.HistoryStorage](boxService.ctx)
var cacheFile adapter.ClashCacheFile
if clashServer := boxService.instance.Router().ClashServer(); clashServer != nil {
cacheFile = clashServer.CacheFile()
}

cacheFile := service.FromContext[adapter.CacheFile](boxService.ctx)
outbounds := boxService.instance.Router().Outbounds()
var iGroups []adapter.OutboundGroup
for _, it := range outbounds {
Expand Down Expand Up @@ -288,16 +284,15 @@ func (s *CommandServer) handleSetGroupExpand(conn net.Conn) error {
if err != nil {
return err
}
service := s.service
if service == nil {
serviceNow := s.service
if serviceNow == nil {
return writeError(conn, E.New("service not ready"))
}
if clashServer := service.instance.Router().ClashServer(); clashServer != nil {
if cacheFile := clashServer.CacheFile(); cacheFile != nil {
err = cacheFile.StoreGroupExpand(groupTag, isExpand)
if err != nil {
return writeError(conn, err)
}
cacheFile := service.FromContext[adapter.CacheFile](serviceNow.ctx)
if cacheFile != nil {
err = cacheFile.StoreGroupExpand(groupTag, isExpand)
if err != nil {
return writeError(conn, err)
}
}
return writeError(conn, nil)
Expand Down
Loading

0 comments on commit bbea3aa

Please sign in to comment.