2024-05-17 18:30:33 +08:00
|
|
|
|
// Copyright 2022 GoEdge goedge.cdn@gmail.com. All rights reserved.
|
2022-02-21 17:33:58 +08:00
|
|
|
|
|
|
|
|
|
|
package caches
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
|
"io"
|
2024-05-07 17:27:10 +08:00
|
|
|
|
"strings"
|
2022-02-21 17:33:58 +08:00
|
|
|
|
"sync"
|
2024-07-27 15:42:50 +08:00
|
|
|
|
|
|
|
|
|
|
fsutils "github.com/TeaOSLab/EdgeNode/internal/utils/fs"
|
|
|
|
|
|
"github.com/iwind/TeaGo/types"
|
2022-02-21 17:33:58 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type PartialFileWriter struct {
|
2024-05-01 15:53:49 +08:00
|
|
|
|
rawWriter *fsutils.File
|
2022-11-20 18:07:46 +08:00
|
|
|
|
key string
|
|
|
|
|
|
|
|
|
|
|
|
metaHeaderSize int
|
|
|
|
|
|
headerSize int64
|
|
|
|
|
|
|
|
|
|
|
|
metaBodySize int64
|
|
|
|
|
|
bodySize int64
|
|
|
|
|
|
|
|
|
|
|
|
expiredAt int64
|
|
|
|
|
|
endFunc func()
|
|
|
|
|
|
once sync.Once
|
2022-02-21 17:33:58 +08:00
|
|
|
|
|
|
|
|
|
|
isNew bool
|
|
|
|
|
|
isPartial bool
|
|
|
|
|
|
bodyOffset int64
|
2022-03-03 19:36:28 +08:00
|
|
|
|
|
|
|
|
|
|
ranges *PartialRanges
|
|
|
|
|
|
rangePath string
|
2024-05-07 16:20:22 +08:00
|
|
|
|
|
|
|
|
|
|
writtenBytes int64
|
2022-02-21 17:33:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-01 15:53:49 +08:00
|
|
|
|
func NewPartialFileWriter(rawWriter *fsutils.File, key string, expiredAt int64, metaHeaderSize int, metaBodySize int64, isNew bool, isPartial bool, bodyOffset int64, ranges *PartialRanges, endFunc func()) *PartialFileWriter {
|
2022-02-21 17:33:58 +08:00
|
|
|
|
return &PartialFileWriter{
|
2022-11-20 18:07:46 +08:00
|
|
|
|
key: key,
|
|
|
|
|
|
rawWriter: rawWriter,
|
|
|
|
|
|
expiredAt: expiredAt,
|
|
|
|
|
|
endFunc: endFunc,
|
|
|
|
|
|
isNew: isNew,
|
|
|
|
|
|
isPartial: isPartial,
|
|
|
|
|
|
bodyOffset: bodyOffset,
|
|
|
|
|
|
ranges: ranges,
|
2023-09-15 18:14:58 +08:00
|
|
|
|
rangePath: PartialRangesFilePath(rawWriter.Name()),
|
2022-11-20 18:07:46 +08:00
|
|
|
|
metaHeaderSize: metaHeaderSize,
|
|
|
|
|
|
metaBodySize: metaBodySize,
|
2022-02-21 17:33:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// WriteHeader 写入数据
|
|
|
|
|
|
func (this *PartialFileWriter) WriteHeader(data []byte) (n int, err error) {
|
|
|
|
|
|
if !this.isNew {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
n, err = this.rawWriter.Write(data)
|
|
|
|
|
|
this.headerSize += int64(n)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = this.Discard()
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
func (this *PartialFileWriter) AppendHeader(data []byte) error {
|
|
|
|
|
|
_, err := this.rawWriter.Write(data)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = this.Discard()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
var c = len(data)
|
|
|
|
|
|
this.headerSize += int64(c)
|
|
|
|
|
|
err = this.WriteHeaderLength(int(this.headerSize))
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = this.Discard()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-21 17:33:58 +08:00
|
|
|
|
// WriteHeaderLength 写入Header长度数据
|
|
|
|
|
|
func (this *PartialFileWriter) WriteHeaderLength(headerLength int) error {
|
2022-11-20 18:07:46 +08:00
|
|
|
|
if this.metaHeaderSize > 0 && this.metaHeaderSize == headerLength {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var bytes4 = make([]byte, 4)
|
2022-02-21 17:33:58 +08:00
|
|
|
|
binary.BigEndian.PutUint32(bytes4, uint32(headerLength))
|
|
|
|
|
|
_, err := this.rawWriter.Seek(SizeExpiresAt+SizeStatus+SizeURLLength, io.SeekStart)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = this.Discard()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
_, err = this.rawWriter.Write(bytes4)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = this.Discard()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Write 写入数据
|
|
|
|
|
|
func (this *PartialFileWriter) Write(data []byte) (n int, err error) {
|
|
|
|
|
|
n, err = this.rawWriter.Write(data)
|
|
|
|
|
|
this.bodySize += int64(n)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = this.Discard()
|
|
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// WriteAt 在指定位置写入数据
|
2022-03-03 19:36:28 +08:00
|
|
|
|
func (this *PartialFileWriter) WriteAt(offset int64, data []byte) error {
|
|
|
|
|
|
var c = int64(len(data))
|
|
|
|
|
|
if c == 0 {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
var end = offset + c - 1
|
|
|
|
|
|
|
|
|
|
|
|
// 是否已包含在内
|
|
|
|
|
|
if this.ranges.Contains(offset, end) {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-20 18:59:07 +08:00
|
|
|
|
// prevent extending too much space in a single writing
|
|
|
|
|
|
var maxOffset = this.ranges.Max()
|
|
|
|
|
|
if offset-maxOffset > 16<<20 {
|
2024-05-01 15:53:49 +08:00
|
|
|
|
var extendSizePerStep int64 = 1 << 20
|
2024-04-20 18:59:07 +08:00
|
|
|
|
var maxExtendSize int64 = 32 << 20
|
|
|
|
|
|
if fsutils.DiskIsExtremelyFast() {
|
|
|
|
|
|
maxExtendSize = 128 << 20
|
2024-05-01 15:53:49 +08:00
|
|
|
|
extendSizePerStep = 4 << 20
|
2024-04-20 18:59:07 +08:00
|
|
|
|
} else if fsutils.DiskIsFast() {
|
|
|
|
|
|
maxExtendSize = 64 << 20
|
2024-05-01 15:53:49 +08:00
|
|
|
|
extendSizePerStep = 2 << 20
|
2024-04-20 18:59:07 +08:00
|
|
|
|
}
|
|
|
|
|
|
if offset-maxOffset > maxExtendSize {
|
|
|
|
|
|
stat, err := this.rawWriter.Stat()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// extend min size to prepare for file tail
|
|
|
|
|
|
if stat.Size()+extendSizePerStep <= this.bodyOffset+offset+int64(len(data)) {
|
|
|
|
|
|
_ = this.rawWriter.Truncate(stat.Size() + extendSizePerStep)
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-21 17:33:58 +08:00
|
|
|
|
if this.bodyOffset == 0 {
|
2022-11-20 18:07:46 +08:00
|
|
|
|
var keyLength = 0
|
|
|
|
|
|
if this.ranges.Version == 0 { // 以往的版本包含有Key
|
|
|
|
|
|
keyLength = len(this.key)
|
|
|
|
|
|
}
|
|
|
|
|
|
this.bodyOffset = SizeMeta + int64(keyLength) + this.headerSize
|
2022-02-21 17:33:58 +08:00
|
|
|
|
}
|
2022-11-20 18:07:46 +08:00
|
|
|
|
|
2024-05-07 16:20:22 +08:00
|
|
|
|
n, err := this.rawWriter.WriteAt(data, this.bodyOffset+offset)
|
2022-03-03 19:36:28 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.ranges.Add(offset, end)
|
|
|
|
|
|
|
2024-05-07 16:20:22 +08:00
|
|
|
|
// 保存ranges内容到文件,当新增数据达到一定量时就更新,是为了及时更新ranges文件,以便于其他请求能够及时读取到已经缓存的部分内容
|
|
|
|
|
|
this.writtenBytes += int64(n)
|
|
|
|
|
|
if this.writtenBytes > (1 << 20) {
|
|
|
|
|
|
this.writtenBytes = 0
|
|
|
|
|
|
if len(this.rangePath) > 0 {
|
|
|
|
|
|
if this.bodySize > 0 {
|
|
|
|
|
|
this.ranges.BodySize = this.bodySize
|
|
|
|
|
|
}
|
|
|
|
|
|
_ = this.ranges.WriteToFile(this.rangePath)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SetBodyLength 设置内容总长度
|
|
|
|
|
|
func (this *PartialFileWriter) SetBodyLength(bodyLength int64) {
|
|
|
|
|
|
this.bodySize = bodyLength
|
2022-02-21 17:33:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-07 17:27:10 +08:00
|
|
|
|
// SetContentMD5 设置内容MD5
|
|
|
|
|
|
func (this *PartialFileWriter) SetContentMD5(contentMD5 string) {
|
|
|
|
|
|
if strings.Contains(contentMD5, "\n") || len(contentMD5) > 128 {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.ranges.ContentMD5 = contentMD5
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-02-21 17:33:58 +08:00
|
|
|
|
// WriteBodyLength 写入Body长度数据
|
|
|
|
|
|
func (this *PartialFileWriter) WriteBodyLength(bodyLength int64) error {
|
2022-11-20 18:07:46 +08:00
|
|
|
|
if this.metaBodySize > 0 && this.metaBodySize == bodyLength {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
var bytes8 = make([]byte, 8)
|
2022-02-21 17:33:58 +08:00
|
|
|
|
binary.BigEndian.PutUint64(bytes8, uint64(bodyLength))
|
|
|
|
|
|
_, err := this.rawWriter.Seek(SizeExpiresAt+SizeStatus+SizeURLLength+SizeHeaderLength, io.SeekStart)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = this.Discard()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
_, err = this.rawWriter.Write(bytes8)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = this.Discard()
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Close 关闭
|
|
|
|
|
|
func (this *PartialFileWriter) Close() error {
|
|
|
|
|
|
defer this.once.Do(func() {
|
|
|
|
|
|
this.endFunc()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2024-05-07 16:20:22 +08:00
|
|
|
|
if this.bodySize > 0 {
|
|
|
|
|
|
this.ranges.BodySize = this.bodySize
|
|
|
|
|
|
}
|
2022-03-03 19:36:28 +08:00
|
|
|
|
err := this.ranges.WriteToFile(this.rangePath)
|
|
|
|
|
|
if err != nil {
|
2022-11-19 21:20:53 +08:00
|
|
|
|
_ = this.rawWriter.Close()
|
|
|
|
|
|
this.remove()
|
2022-03-03 19:36:28 +08:00
|
|
|
|
return err
|
|
|
|
|
|
}
|
2022-02-21 17:33:58 +08:00
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
// 关闭当前writer
|
2022-02-21 17:33:58 +08:00
|
|
|
|
if this.isNew {
|
2022-03-03 19:36:28 +08:00
|
|
|
|
err = this.WriteHeaderLength(types.Int(this.headerSize))
|
2022-02-21 17:33:58 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = this.rawWriter.Close()
|
2022-03-03 19:36:28 +08:00
|
|
|
|
this.remove()
|
2022-02-21 17:33:58 +08:00
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
err = this.WriteBodyLength(this.bodySize)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
_ = this.rawWriter.Close()
|
2022-03-03 19:36:28 +08:00
|
|
|
|
this.remove()
|
2022-02-21 17:33:58 +08:00
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
err = this.rawWriter.Close()
|
2022-02-21 17:33:58 +08:00
|
|
|
|
if err != nil {
|
2022-03-03 19:36:28 +08:00
|
|
|
|
this.remove()
|
2022-02-21 17:33:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Discard 丢弃
|
|
|
|
|
|
func (this *PartialFileWriter) Discard() error {
|
|
|
|
|
|
defer this.once.Do(func() {
|
|
|
|
|
|
this.endFunc()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
_ = this.rawWriter.Close()
|
|
|
|
|
|
|
2024-04-20 17:44:23 +08:00
|
|
|
|
SharedPartialRangesQueue.Delete(this.rangePath)
|
2022-03-03 19:36:28 +08:00
|
|
|
|
|
2024-04-29 22:36:26 +08:00
|
|
|
|
_ = fsutils.Remove(this.rangePath)
|
|
|
|
|
|
|
|
|
|
|
|
err := fsutils.Remove(this.rawWriter.Name())
|
|
|
|
|
|
|
2022-02-21 17:33:58 +08:00
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *PartialFileWriter) HeaderSize() int64 {
|
|
|
|
|
|
return this.headerSize
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *PartialFileWriter) BodySize() int64 {
|
|
|
|
|
|
return this.bodySize
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *PartialFileWriter) ExpiredAt() int64 {
|
|
|
|
|
|
return this.expiredAt
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *PartialFileWriter) Key() string {
|
|
|
|
|
|
return this.key
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ItemType 获取内容类型
|
|
|
|
|
|
func (this *PartialFileWriter) ItemType() ItemType {
|
|
|
|
|
|
return ItemTypeFile
|
|
|
|
|
|
}
|
2022-03-03 19:36:28 +08:00
|
|
|
|
|
|
|
|
|
|
func (this *PartialFileWriter) IsNew() bool {
|
|
|
|
|
|
return this.isNew && len(this.ranges.Ranges) == 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-05-07 20:15:47 +08:00
|
|
|
|
func (this *PartialFileWriter) Ranges() *PartialRanges {
|
|
|
|
|
|
return this.ranges
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-03-03 19:36:28 +08:00
|
|
|
|
func (this *PartialFileWriter) remove() {
|
2024-04-29 22:36:26 +08:00
|
|
|
|
_ = fsutils.Remove(this.rawWriter.Name())
|
2024-04-20 17:44:23 +08:00
|
|
|
|
|
|
|
|
|
|
SharedPartialRangesQueue.Delete(this.rangePath)
|
2024-04-29 22:36:26 +08:00
|
|
|
|
|
|
|
|
|
|
_ = fsutils.Remove(this.rangePath)
|
2022-03-03 19:36:28 +08:00
|
|
|
|
}
|