2020-10-04 14:30:42 +08:00
package caches
import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs"
2021-03-02 19:43:05 +08:00
"github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/shared"
2020-10-28 11:19:06 +08:00
"github.com/TeaOSLab/EdgeNode/internal/events"
2020-12-17 17:36:10 +08:00
"github.com/TeaOSLab/EdgeNode/internal/remotelogs"
2020-10-04 14:30:42 +08:00
"github.com/TeaOSLab/EdgeNode/internal/utils"
"github.com/iwind/TeaGo/Tea"
stringutil "github.com/iwind/TeaGo/utils/string"
2021-05-24 09:23:51 +08:00
"golang.org/x/text/language"
"golang.org/x/text/message"
2020-10-04 14:30:42 +08:00
"io"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
2020-10-05 20:23:18 +08:00
"sync/atomic"
2021-01-11 23:06:50 +08:00
"syscall"
2020-10-04 14:30:42 +08:00
"time"
)
const (
2021-01-13 12:02:50 +08:00
SizeExpiresAt = 4
SizeStatus = 3
SizeURLLength = 4
SizeHeaderLength = 4
SizeBodyLength = 8
SizeMeta = SizeExpiresAt + SizeStatus + SizeURLLength + SizeHeaderLength + SizeBodyLength
2020-10-04 14:30:42 +08:00
)
var (
2021-01-11 23:06:50 +08:00
ErrNotFound = errors . New ( "cache not found" )
ErrFileIsWriting = errors . New ( "the file is writing" )
2021-01-13 12:02:50 +08:00
ErrInvalidRange = errors . New ( "invalid range" )
2020-10-04 14:30:42 +08:00
)
2021-05-12 21:38:44 +08:00
// FileStorage 文件缓存
2021-01-13 12:02:50 +08:00
// 文件结构:
// [expires time] | [ status ] | [url length] | [header length] | [body length] | [url] [header data] [body data]
2020-10-04 14:30:42 +08:00
type FileStorage struct {
2021-03-02 19:43:05 +08:00
policy * serverconfigs . HTTPCachePolicy
cacheConfig * serverconfigs . HTTPFileCacheStorage // 二级缓存
memoryStorage * MemoryStorage // 一级缓存
totalSize int64
2020-10-04 14:30:42 +08:00
2021-05-19 12:07:35 +08:00
list ListInterface
2020-10-04 14:30:42 +08:00
locker sync . RWMutex
ticker * utils . Ticker
}
func NewFileStorage ( policy * serverconfigs . HTTPCachePolicy ) * FileStorage {
return & FileStorage {
policy : policy ,
}
}
2021-05-12 21:38:44 +08:00
// Policy 获取当前的Policy
2020-10-04 14:30:42 +08:00
func ( this * FileStorage ) Policy ( ) * serverconfigs . HTTPCachePolicy {
return this . policy
}
2021-05-12 21:38:44 +08:00
// Init 初始化
2020-10-04 14:30:42 +08:00
func ( this * FileStorage ) Init ( ) error {
this . locker . Lock ( )
defer this . locker . Unlock ( )
before := time . Now ( )
// 配置
2020-10-05 16:55:14 +08:00
cacheConfig := & serverconfigs . HTTPFileCacheStorage { }
2020-10-04 14:30:42 +08:00
optionsJSON , err := json . Marshal ( this . policy . Options )
if err != nil {
return err
}
err = json . Unmarshal ( optionsJSON , cacheConfig )
if err != nil {
return err
}
this . cacheConfig = cacheConfig
2021-05-19 12:07:35 +08:00
cacheDir := cacheConfig . Dir
2020-10-04 14:30:42 +08:00
if ! filepath . IsAbs ( this . cacheConfig . Dir ) {
this . cacheConfig . Dir = Tea . Root + Tea . DS + this . cacheConfig . Dir
}
dir := this . cacheConfig . Dir
if len ( dir ) == 0 {
return errors . New ( "[CACHE]cache storage dir can not be empty" )
}
2021-05-19 12:07:35 +08:00
list := NewFileList ( dir + "/p" + strconv . FormatInt ( this . policy . Id , 10 ) )
err = list . Init ( )
if err != nil {
return err
}
this . list = list
stat , err := list . Stat ( func ( hash string ) bool {
return true
} )
if err != nil {
return err
}
this . totalSize = stat . Size
this . list . OnAdd ( func ( item * Item ) {
atomic . AddInt64 ( & this . totalSize , item . TotalSize ( ) )
} )
this . list . OnRemove ( func ( item * Item ) {
atomic . AddInt64 ( & this . totalSize , - item . TotalSize ( ) )
} )
2020-10-04 14:30:42 +08:00
// 检查目录是否存在
_ , err = os . Stat ( dir )
if err != nil {
if ! os . IsNotExist ( err ) {
return err
} else {
err = os . MkdirAll ( dir , 0777 )
if err != nil {
return errors . New ( "[CACHE]can not create dir:" + err . Error ( ) )
}
}
}
2021-05-19 12:07:35 +08:00
defer func ( ) {
// 统计
2021-05-24 09:23:51 +08:00
count := stat . Count
size := stat . Size
2021-05-19 12:07:35 +08:00
cost := time . Since ( before ) . Seconds ( ) * 1000
sizeMB := strconv . FormatInt ( size , 10 ) + " Bytes"
if size > 1024 * 1024 * 1024 {
sizeMB = fmt . Sprintf ( "%.3f G" , float64 ( size ) / 1024 / 1024 / 1024 )
} else if size > 1024 * 1024 {
sizeMB = fmt . Sprintf ( "%.3f M" , float64 ( size ) / 1024 / 1024 )
} else if size > 1024 {
sizeMB = fmt . Sprintf ( "%.3f K" , float64 ( size ) / 1024 )
}
2021-05-24 09:23:51 +08:00
remotelogs . Println ( "CACHE" , "init policy " + strconv . FormatInt ( this . policy . Id , 10 ) + " from '" + cacheDir + "', cost: " + fmt . Sprintf ( "%.2f" , cost ) + " ms, count: " + message . NewPrinter ( language . English ) . Sprintf ( "%d" , count ) + ", size: " + sizeMB )
2021-05-19 12:07:35 +08:00
} ( )
2020-10-04 14:30:42 +08:00
// 初始化list
err = this . initList ( )
if err != nil {
return err
}
2021-03-02 19:43:05 +08:00
// 加载内存缓存
if this . cacheConfig . MemoryPolicy != nil {
2021-05-12 21:38:44 +08:00
if this . cacheConfig . MemoryPolicy . Capacity != nil && this . cacheConfig . MemoryPolicy . Capacity . Count > 0 {
2021-03-02 19:43:05 +08:00
memoryPolicy := & serverconfigs . HTTPCachePolicy {
Id : this . policy . Id ,
IsOn : this . policy . IsOn ,
Name : this . policy . Name ,
Description : this . policy . Description ,
2021-05-12 21:38:44 +08:00
Capacity : this . cacheConfig . MemoryPolicy . Capacity ,
2021-03-02 19:43:05 +08:00
MaxKeys : this . policy . MaxKeys ,
MaxSize : & shared . SizeCapacity { Count : 128 , Unit : shared . SizeCapacityUnitMB } , // TODO 将来可以修改
Type : serverconfigs . CachePolicyStorageMemory ,
Options : this . policy . Options ,
Life : this . policy . Life ,
MinLife : this . policy . MinLife ,
MaxLife : this . policy . MaxLife ,
}
err = memoryPolicy . Init ( )
if err != nil {
return err
}
memoryStorage := NewMemoryStorage ( memoryPolicy )
err = memoryStorage . Init ( )
if err != nil {
return err
}
this . memoryStorage = memoryStorage
}
}
2020-10-04 14:30:42 +08:00
return nil
}
2021-01-13 12:02:50 +08:00
func ( this * FileStorage ) OpenReader ( key string ) ( Reader , error ) {
2021-03-02 19:43:05 +08:00
// 先尝试内存缓存
if this . memoryStorage != nil {
reader , err := this . memoryStorage . OpenReader ( key )
if err == nil {
return reader , err
}
}
2021-05-19 12:07:35 +08:00
_ , path := this . keyPath ( key )
2020-10-04 14:30:42 +08:00
// TODO 尝试使用mmap加快读取速度
fp , err := os . OpenFile ( path , os . O_RDONLY , 0444 )
if err != nil {
if ! os . IsNotExist ( err ) {
2021-01-13 12:02:50 +08:00
return nil , err
2020-10-04 14:30:42 +08:00
}
2021-01-13 12:02:50 +08:00
return nil , ErrNotFound
2020-10-04 14:30:42 +08:00
}
2021-01-13 12:02:50 +08:00
reader := NewFileReader ( fp )
2020-10-04 14:30:42 +08:00
if err != nil {
2021-01-13 12:02:50 +08:00
return nil , err
2020-10-04 14:30:42 +08:00
}
2021-01-13 12:02:50 +08:00
err = reader . Init ( )
2020-10-04 14:30:42 +08:00
if err != nil {
2021-01-13 12:02:50 +08:00
return nil , err
2020-10-04 14:30:42 +08:00
}
2021-01-13 12:02:50 +08:00
return reader , nil
2020-10-04 14:30:42 +08:00
}
2021-05-12 21:38:44 +08:00
// OpenWriter 打开缓存文件等待写入
2021-01-13 12:02:50 +08:00
func ( this * FileStorage ) OpenWriter ( key string , expiredAt int64 , status int ) ( Writer , error ) {
2021-03-02 19:43:05 +08:00
// 先尝试内存缓存
if this . memoryStorage != nil {
writer , err := this . memoryStorage . OpenWriter ( key , expiredAt , status )
if err == nil {
return writer , nil
}
}
2020-10-05 19:15:35 +08:00
// 检查是否超出最大值
2021-05-19 12:07:35 +08:00
count , err := this . list . Count ( )
if err != nil {
return nil , err
}
if this . policy . MaxKeys > 0 && count > this . policy . MaxKeys {
2020-10-05 20:23:18 +08:00
return nil , errors . New ( "write file cache failed: too many keys in cache storage" )
}
2021-05-12 21:38:44 +08:00
capacityBytes := this . diskCapacityBytes ( )
if capacityBytes > 0 && capacityBytes <= this . totalSize {
2021-05-19 12:07:35 +08:00
return nil , errors . New ( "write file cache failed: over disk size, current total size: " + strconv . FormatInt ( this . totalSize , 10 ) + " bytes, capacity: " + strconv . FormatInt ( capacityBytes , 10 ) )
2020-10-05 19:15:35 +08:00
}
2020-10-04 14:30:42 +08:00
hash := stringutil . Md5 ( key )
dir := this . cacheConfig . Dir + "/p" + strconv . FormatInt ( this . policy . Id , 10 ) + "/" + hash [ : 2 ] + "/" + hash [ 2 : 4 ]
2021-05-19 12:07:35 +08:00
_ , err = os . Stat ( dir )
2020-10-04 14:30:42 +08:00
if err != nil {
if ! os . IsNotExist ( err ) {
return nil , err
}
err = os . MkdirAll ( dir , 0777 )
if err != nil {
return nil , err
}
}
2020-10-05 20:23:18 +08:00
// 先删除
2021-05-19 12:07:35 +08:00
err = this . list . Remove ( hash )
if err != nil {
return nil , err
}
2020-10-05 20:23:18 +08:00
2021-01-13 12:02:50 +08:00
path := dir + "/" + hash + ".cache.tmp"
2021-01-11 23:06:50 +08:00
writer , err := os . OpenFile ( path , os . O_CREATE | os . O_SYNC | os . O_WRONLY , 0666 )
2020-10-04 14:30:42 +08:00
if err != nil {
return nil , err
}
isOk := false
2021-01-11 23:06:50 +08:00
removeOnFailure := true
2020-10-04 14:30:42 +08:00
defer func ( ) {
if err != nil {
isOk = false
}
// 如果出错了,就删除文件,避免写一半
if ! isOk {
_ = writer . Close ( )
2021-01-11 23:06:50 +08:00
if removeOnFailure {
_ = os . Remove ( path )
}
2020-10-04 14:30:42 +08:00
}
} ( )
2021-01-11 23:06:50 +08:00
// 尝试锁定,如果锁定失败,则直接返回
err = syscall . Flock ( int ( writer . Fd ( ) ) , syscall . LOCK_EX | syscall . LOCK_NB )
if err != nil {
removeOnFailure = false
return nil , ErrFileIsWriting
}
err = writer . Truncate ( 0 )
if err != nil {
return nil , err
}
2020-10-04 14:30:42 +08:00
// 写入过期时间
2021-01-13 12:02:50 +08:00
bytes4 := make ( [ ] byte , 4 )
{
binary . BigEndian . PutUint32 ( bytes4 , uint32 ( expiredAt ) )
_ , err = writer . Write ( bytes4 )
if err != nil {
return nil , err
}
2020-10-04 14:30:42 +08:00
}
2021-01-13 12:02:50 +08:00
// 写入状态码
if status > 999 || status < 100 {
status = 200
2020-10-04 14:30:42 +08:00
}
2021-01-13 12:02:50 +08:00
_ , err = writer . WriteString ( strconv . Itoa ( status ) )
2020-10-04 14:30:42 +08:00
if err != nil {
return nil , err
}
2021-01-13 12:02:50 +08:00
// 写入URL长度
{
binary . BigEndian . PutUint32 ( bytes4 , uint32 ( len ( key ) ) )
_ , err = writer . Write ( bytes4 )
2020-10-04 14:30:42 +08:00
if err != nil {
2021-01-13 12:02:50 +08:00
return nil , err
2020-10-04 14:30:42 +08:00
}
}
2021-01-13 12:02:50 +08:00
// 写入Header Length
{
binary . BigEndian . PutUint32 ( bytes4 , uint32 ( 0 ) )
_ , err = writer . Write ( bytes4 )
2020-10-04 14:30:42 +08:00
if err != nil {
2021-01-13 12:02:50 +08:00
return nil , err
2020-10-04 14:30:42 +08:00
}
}
2021-01-13 12:02:50 +08:00
// 写入Body Length
{
b := make ( [ ] byte , SizeBodyLength )
binary . BigEndian . PutUint64 ( b , uint64 ( 0 ) )
_ , err = writer . Write ( b )
if err != nil {
return nil , err
}
2020-10-04 14:30:42 +08:00
}
2021-01-13 12:02:50 +08:00
// 写入URL
_ , err = writer . WriteString ( key )
2020-10-04 14:30:42 +08:00
if err != nil {
2021-01-13 12:02:50 +08:00
return nil , err
2020-10-04 14:30:42 +08:00
}
isOk = true
2021-01-13 12:02:50 +08:00
return NewFileWriter ( writer , key , expiredAt ) , nil
2020-10-04 14:30:42 +08:00
}
2021-05-12 21:38:44 +08:00
// AddToList 添加到List
2020-10-04 14:30:42 +08:00
func ( this * FileStorage ) AddToList ( item * Item ) {
2021-03-02 19:43:05 +08:00
if this . memoryStorage != nil {
if item . Type == ItemTypeMemory {
this . memoryStorage . AddToList ( item )
return
}
}
2021-01-13 12:02:50 +08:00
item . MetaSize = SizeMeta
2020-10-04 14:30:42 +08:00
hash := stringutil . Md5 ( item . Key )
2021-05-19 12:07:35 +08:00
err := this . list . Add ( hash , item )
if err != nil && ! strings . Contains ( err . Error ( ) , "UNIQUE constraint failed" ) {
remotelogs . Error ( "CACHE" , "add to list failed: " + err . Error ( ) )
}
2020-10-04 14:30:42 +08:00
}
2021-05-12 21:38:44 +08:00
// Delete 删除某个键值对应的缓存
2020-10-04 14:30:42 +08:00
func ( this * FileStorage ) Delete ( key string ) error {
this . locker . Lock ( )
defer this . locker . Unlock ( )
2021-03-02 19:43:05 +08:00
// 先尝试内存缓存
if this . memoryStorage != nil {
_ = this . memoryStorage . Delete ( key )
}
2020-10-04 14:30:42 +08:00
hash , path := this . keyPath ( key )
2021-05-19 12:07:35 +08:00
err := this . list . Remove ( hash )
if err != nil {
return err
}
err = os . Remove ( path )
2020-10-04 14:30:42 +08:00
if err == nil || os . IsNotExist ( err ) {
return nil
}
return err
}
2021-05-12 21:38:44 +08:00
// Stat 统计
2020-10-04 14:30:42 +08:00
func ( this * FileStorage ) Stat ( ) ( * Stat , error ) {
this . locker . RLock ( )
defer this . locker . RUnlock ( )
return this . list . Stat ( func ( hash string ) bool {
return true
2021-05-19 12:07:35 +08:00
} )
2020-10-04 14:30:42 +08:00
}
2021-05-12 21:38:44 +08:00
// CleanAll 清除所有的缓存
2020-10-04 14:30:42 +08:00
func ( this * FileStorage ) CleanAll ( ) error {
this . locker . Lock ( )
defer this . locker . Unlock ( )
2021-03-02 19:43:05 +08:00
// 先尝试内存缓存
if this . memoryStorage != nil {
_ = this . memoryStorage . CleanAll ( )
}
2021-05-19 12:07:35 +08:00
err := this . list . CleanAll ( )
if err != nil {
return err
}
2020-10-04 14:30:42 +08:00
// 删除缓存和目录
// 不能直接删除子目录,比较危险
dir := this . dir ( )
fp , err := os . Open ( dir )
if err != nil {
return err
}
defer func ( ) {
_ = fp . Close ( )
} ( )
stat , err := fp . Stat ( )
if err != nil {
return err
}
if ! stat . IsDir ( ) {
return nil
}
subDirs , err := fp . Readdir ( - 1 )
if err != nil {
return err
}
for _ , info := range subDirs {
subDir := info . Name ( )
// 检查目录名
ok , err := regexp . MatchString ( ` ^[0-9a-f] { 2}$ ` , subDir )
if err != nil {
return err
}
if ! ok {
continue
}
// 删除目录
err = os . RemoveAll ( dir + "/" + subDir )
if err != nil {
return err
}
}
return nil
}
2021-05-12 21:38:44 +08:00
// Purge 清理过期的缓存
2020-12-23 21:28:50 +08:00
func ( this * FileStorage ) Purge ( keys [ ] string , urlType string ) error {
2020-10-04 14:30:42 +08:00
this . locker . Lock ( )
defer this . locker . Unlock ( )
2021-03-02 19:43:05 +08:00
// 先尝试内存缓存
if this . memoryStorage != nil {
_ = this . memoryStorage . Purge ( keys , urlType )
}
2020-12-23 21:28:50 +08:00
// 目录
if urlType == "dir" {
resultKeys := [ ] string { }
for _ , key := range keys {
2021-05-19 12:07:35 +08:00
subKeys , err := this . list . FindKeysWithPrefix ( key )
if err != nil {
return err
}
resultKeys = append ( resultKeys , subKeys ... )
2020-12-23 21:28:50 +08:00
}
keys = resultKeys
}
// 文件
2020-10-04 14:30:42 +08:00
for _ , key := range keys {
hash , path := this . keyPath ( key )
2021-05-19 12:07:35 +08:00
exists , err := this . list . Exist ( hash )
if err != nil {
return err
}
if ! exists {
2020-10-04 14:30:42 +08:00
err := os . Remove ( path )
if err != nil && ! os . IsNotExist ( err ) {
return err
}
continue
}
2021-05-19 12:07:35 +08:00
err = os . Remove ( path )
2020-10-04 14:30:42 +08:00
if err != nil && ! os . IsNotExist ( err ) {
return err
}
2021-05-19 12:07:35 +08:00
err = this . list . Remove ( hash )
if err != nil {
return err
}
2020-10-04 14:30:42 +08:00
}
return nil
}
2021-05-12 21:38:44 +08:00
// Stop 停止
2020-10-04 14:30:42 +08:00
func ( this * FileStorage ) Stop ( ) {
this . locker . Lock ( )
defer this . locker . Unlock ( )
2021-03-02 19:43:05 +08:00
// 先尝试内存缓存
if this . memoryStorage != nil {
this . memoryStorage . Stop ( )
}
2021-05-24 09:23:51 +08:00
_ = this . list . Reset ( )
2020-10-04 14:30:42 +08:00
if this . ticker != nil {
this . ticker . Stop ( )
}
}
2021-05-13 11:50:36 +08:00
// TotalDiskSize 消耗的磁盘尺寸
func ( this * FileStorage ) TotalDiskSize ( ) int64 {
return atomic . LoadInt64 ( & this . totalSize )
}
// TotalMemorySize 内存尺寸
func ( this * FileStorage ) TotalMemorySize ( ) int64 {
if this . memoryStorage == nil {
return 0
}
return this . memoryStorage . TotalMemorySize ( )
}
2020-10-04 14:30:42 +08:00
// 绝对路径
func ( this * FileStorage ) dir ( ) string {
return this . cacheConfig . Dir + "/p" + strconv . FormatInt ( this . policy . Id , 10 ) + "/"
}
// 获取Key对应的文件路径
func ( this * FileStorage ) keyPath ( key string ) ( hash string , path string ) {
hash = stringutil . Md5 ( key )
dir := this . cacheConfig . Dir + "/p" + strconv . FormatInt ( this . policy . Id , 10 ) + "/" + hash [ : 2 ] + "/" + hash [ 2 : 4 ]
path = dir + "/" + hash + ".cache"
return
}
// 获取Hash对应的文件路径
func ( this * FileStorage ) hashPath ( hash string ) ( path string ) {
if len ( hash ) != 32 {
return ""
}
dir := this . cacheConfig . Dir + "/p" + strconv . FormatInt ( this . policy . Id , 10 ) + "/" + hash [ : 2 ] + "/" + hash [ 2 : 4 ]
path = dir + "/" + hash + ".cache"
return
}
// 初始化List
func ( this * FileStorage ) initList ( ) error {
2021-05-19 12:07:35 +08:00
err := this . list . Reset ( )
if err != nil {
return err
}
2020-10-04 14:30:42 +08:00
2021-05-23 22:59:00 +08:00
// 使用异步防止阻塞主线程
go func ( ) {
dir := this . dir ( )
2021-01-13 12:02:50 +08:00
2021-05-23 22:59:00 +08:00
// 清除tmp
files , err := filepath . Glob ( dir + "/*/*/*.cache.tmp" )
if err == nil && len ( files ) > 0 {
for _ , path := range files {
_ = os . Remove ( path )
}
}
} ( )
2021-01-13 12:02:50 +08:00
2020-10-04 14:30:42 +08:00
// 启动定时清理任务
this . ticker = utils . NewTicker ( 30 * time . Second )
2020-10-28 11:19:06 +08:00
events . On ( events . EventQuit , func ( ) {
2020-12-17 17:36:10 +08:00
remotelogs . Println ( "CACHE" , "quit clean timer" )
2020-10-28 11:19:06 +08:00
var ticker = this . ticker
if ticker != nil {
ticker . Stop ( )
}
} )
2020-10-04 14:30:42 +08:00
go func ( ) {
for this . ticker . Next ( ) {
this . purgeLoop ( )
}
} ( )
return nil
}
// 解析文件信息
func ( this * FileStorage ) decodeFile ( path string ) ( * Item , error ) {
fp , err := os . OpenFile ( path , os . O_RDONLY , 0444 )
if err != nil {
return nil , err
}
2021-01-13 12:02:50 +08:00
isAllOk := false
2020-10-04 14:30:42 +08:00
defer func ( ) {
_ = fp . Close ( )
2021-01-13 12:02:50 +08:00
if ! isAllOk {
_ = os . Remove ( path )
}
2020-10-04 14:30:42 +08:00
} ( )
2021-01-13 12:02:50 +08:00
item := & Item {
2021-03-02 19:43:05 +08:00
Type : ItemTypeFile ,
2021-01-13 12:02:50 +08:00
MetaSize : SizeMeta ,
}
bytes4 := make ( [ ] byte , 4 )
// 过期时间
ok , err := this . readToBuff ( fp , bytes4 )
2020-10-04 14:30:42 +08:00
if err != nil {
return nil , err
}
2021-01-13 12:02:50 +08:00
if ! ok {
2020-10-04 14:30:42 +08:00
return nil , ErrNotFound
}
2021-01-13 12:02:50 +08:00
item . ExpiredAt = int64 ( binary . BigEndian . Uint32 ( bytes4 ) )
// 是否已过期
if item . ExpiredAt < time . Now ( ) . Unix ( ) {
2020-10-04 14:30:42 +08:00
return nil , ErrNotFound
}
2021-01-13 12:02:50 +08:00
// URL Size
_ , err = fp . Seek ( int64 ( SizeExpiresAt + SizeStatus ) , io . SeekStart )
2020-10-04 14:30:42 +08:00
if err != nil {
return nil , err
}
2021-01-13 12:02:50 +08:00
ok , err = this . readToBuff ( fp , bytes4 )
if err != nil {
return nil , err
}
if ! ok {
return nil , ErrNotFound
}
urlSize := binary . BigEndian . Uint32 ( bytes4 )
2020-10-04 14:30:42 +08:00
2021-01-13 12:02:50 +08:00
// Header Size
ok , err = this . readToBuff ( fp , bytes4 )
2020-10-04 14:30:42 +08:00
if err != nil {
return nil , err
}
2021-01-13 12:02:50 +08:00
if ! ok {
2020-10-04 14:30:42 +08:00
return nil , ErrNotFound
}
2021-01-13 12:02:50 +08:00
item . HeaderSize = int64 ( binary . BigEndian . Uint32 ( bytes4 ) )
2020-10-04 14:30:42 +08:00
2021-01-13 12:02:50 +08:00
// Body Size
bytes8 := make ( [ ] byte , 8 )
ok , err = this . readToBuff ( fp , bytes8 )
2020-10-04 14:30:42 +08:00
if err != nil {
return nil , err
}
2021-01-13 12:02:50 +08:00
if ! ok {
return nil , ErrNotFound
}
item . BodySize = int64 ( binary . BigEndian . Uint64 ( bytes8 ) )
// URL
if urlSize > 0 {
data := utils . BytePool1024 . Get ( )
result , ok , err := this . readN ( fp , data , int ( urlSize ) )
utils . BytePool1024 . Put ( data )
if err != nil {
return nil , err
}
if ! ok {
return nil , ErrNotFound
}
item . Key = string ( result )
}
isAllOk = true
2020-10-04 14:30:42 +08:00
return item , nil
}
// 清理任务
func ( this * FileStorage ) purgeLoop ( ) {
2021-05-19 12:07:35 +08:00
_ = this . list . Purge ( 1000 , func ( hash string ) error {
2020-10-04 14:30:42 +08:00
path := this . hashPath ( hash )
err := os . Remove ( path )
if err != nil && ! os . IsNotExist ( err ) {
2020-12-17 17:36:10 +08:00
remotelogs . Error ( "CACHE" , "purge '" + path + "' error: " + err . Error ( ) )
2020-10-04 14:30:42 +08:00
}
2021-05-19 12:07:35 +08:00
return nil
2020-10-04 14:30:42 +08:00
} )
}
2021-01-13 12:02:50 +08:00
func ( this * FileStorage ) readToBuff ( fp * os . File , buf [ ] byte ) ( ok bool , err error ) {
n , err := fp . Read ( buf )
if err != nil {
return false , err
}
ok = n == len ( buf )
return
}
func ( this * FileStorage ) readN ( fp * os . File , buf [ ] byte , total int ) ( result [ ] byte , ok bool , err error ) {
for {
n , err := fp . Read ( buf )
if err != nil {
return nil , false , err
}
if n > 0 {
if n >= total {
result = append ( result , buf [ : total ] ... )
ok = true
return result , ok , nil
} else {
total -= n
result = append ( result , buf [ : n ] ... )
}
}
}
}
2021-05-12 21:38:44 +08:00
func ( this * FileStorage ) diskCapacityBytes ( ) int64 {
c1 := this . policy . CapacityBytes ( )
if SharedManager . MaxDiskCapacity != nil {
c2 := SharedManager . MaxDiskCapacity . Bytes ( )
if c2 > 0 {
return c2
}
}
return c1
}