Skip to content

Commit d29a2b9

Browse files
authored
Add a dedicated cache container for the zstd compression algorithm (#1828)
Add a dedicated cache container for the zstd compression algorithm to prevent discrepancies between the response content and the implied Content-Encoding in certain scenarios.Fix: #1827 (comment)
1 parent 5cc0ea1 commit d29a2b9

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

fs.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,7 @@ func newCacheManager(fs *FS) cacheManager {
819819
cache: make(map[string]*fsFile),
820820
cacheBrotli: make(map[string]*fsFile),
821821
cacheGzip: make(map[string]*fsFile),
822+
cacheZstd: make(map[string]*fsFile),
822823
}
823824

824825
go instance.handleCleanCache(fs.CleanStop)
@@ -850,6 +851,7 @@ type inMemoryCacheManager struct {
850851
cache map[string]*fsFile
851852
cacheBrotli map[string]*fsFile
852853
cacheGzip map[string]*fsFile
854+
cacheZstd map[string]*fsFile
853855
cacheDuration time.Duration
854856
cacheLock sync.Mutex
855857
}
@@ -869,6 +871,8 @@ func (cm *inMemoryCacheManager) getFsCache(cacheKind CacheKind) map[string]*fsFi
869871
fileCache = cm.cacheBrotli
870872
case gzipCacheKind:
871873
fileCache = cm.cacheGzip
874+
case zstdCacheKind:
875+
fileCache = cm.cacheZstd
872876
}
873877

874878
return fileCache
@@ -959,6 +963,7 @@ func (cm *inMemoryCacheManager) cleanCache(pendingFiles []*fsFile) []*fsFile {
959963
pendingFiles, filesToRelease = cleanCacheNolock(cm.cache, pendingFiles, filesToRelease, cm.cacheDuration)
960964
pendingFiles, filesToRelease = cleanCacheNolock(cm.cacheBrotli, pendingFiles, filesToRelease, cm.cacheDuration)
961965
pendingFiles, filesToRelease = cleanCacheNolock(cm.cacheGzip, pendingFiles, filesToRelease, cm.cacheDuration)
966+
pendingFiles, filesToRelease = cleanCacheNolock(cm.cacheZstd, pendingFiles, filesToRelease, cm.cacheDuration)
962967

963968
cm.cacheLock.Unlock()
964969

fs_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"path/filepath"
1111
"runtime"
1212
"sort"
13+
"strings"
1314
"testing"
1415
"time"
1516
)
@@ -939,3 +940,66 @@ func TestServeFileDirectoryRedirect(t *testing.T) {
939940
t.Fatalf("Unexpected status code %d for file '/fs.go'. Expecting %d.", ctx.Response.StatusCode(), StatusOK)
940941
}
941942
}
943+
944+
func TestFileCacheForZstd(t *testing.T) {
945+
f, err := os.CreateTemp(os.TempDir(), "test")
946+
if err != nil {
947+
t.Fatal(err)
948+
}
949+
data := bytes.Repeat([]byte("1"), 1000)
950+
changedData := bytes.Repeat([]byte("2"), 1000)
951+
_, err = f.Write(data)
952+
if err != nil {
953+
t.Fatal(err)
954+
}
955+
err = f.Sync()
956+
if err != nil {
957+
t.Fatal(err)
958+
}
959+
fs := FS{Root: os.TempDir(), Compress: true, CacheDuration: time.Second * 60}
960+
h := fs.NewRequestHandler()
961+
var ctx RequestCtx
962+
var req Request
963+
req.Header.Set("Accept-Encoding", "zstd")
964+
req.SetRequestURI("http://foobar.com/" + strings.TrimPrefix(f.Name(), os.TempDir()))
965+
ctx.Init(&req, nil, nil)
966+
h(&ctx)
967+
if !bytes.Equal(ctx.Response.Header.ContentEncoding(), []byte("zstd")) {
968+
t.Fatalf("Unexpected 'Content-Encoding' %q. Expecting %q", ctx.Response.Header.ContentEncoding(), "zstd")
969+
}
970+
ctx.Response.Reset()
971+
_, err = f.Seek(0, io.SeekStart)
972+
if err != nil {
973+
t.Fatal(err)
974+
}
975+
_, err = f.Write(changedData)
976+
if err != nil {
977+
t.Fatal(err)
978+
}
979+
f.Close()
980+
h(&ctx)
981+
if !bytes.Equal(ctx.Response.Header.ContentEncoding(), []byte("zstd")) {
982+
t.Fatalf("Unexpected 'Content-Encoding' %q. Expecting %q", ctx.Response.Header.ContentEncoding(), "zstd")
983+
}
984+
d, err := acquireZstdReader(strings.NewReader(string(ctx.Response.Body())))
985+
if err != nil {
986+
t.Fatalf("invalid zstd reader")
987+
}
988+
plainText, err := io.ReadAll(d)
989+
d.Close()
990+
if err != nil {
991+
t.Fatal(err)
992+
}
993+
if !bytes.Equal(plainText, data) {
994+
t.Fatalf("Unexpected response body %q. Expecting %q . Zstd cache doesn't work", plainText, data)
995+
}
996+
ctx.Request.Header.Del("Accept-Encoding")
997+
ctx.Response.Reset()
998+
h(&ctx)
999+
if !bytes.Equal(ctx.Response.Header.ContentEncoding(), []byte("")) {
1000+
t.Fatalf("Unexpected 'Content-Encoding' %q. Expecting %q", ctx.Response.Header.ContentEncoding(), "")
1001+
}
1002+
if !bytes.Equal(ctx.Response.Body(), changedData) {
1003+
t.Fatalf("Unexpected response body %q. Expecting %q", ctx.Response.Body(), data)
1004+
}
1005+
}

0 commit comments

Comments
 (0)