From f75e79047a527ff756b47e54398de5e299f93680 Mon Sep 17 00:00:00 2001 From: jyxjjj <773933146@qq.com> Date: Sun, 20 Sep 2026 00:55:25 +0800 Subject: [PATCH] fix(webdav): stabilize fallback ETags - Use second-precision modification times for reproducible fallback ETags - Cover ETag stability across cache refresh timestamp normalization Co-authored-by: Codex <267193182+codex@users.noreply.github.com> Signed-off-by: jyxjjj <16695261+jyxjjj@users.noreply.github.com> --- server/common/etag_test.go | 21 +++++++++++---------- server/common/proxy.go | 5 +++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/server/common/etag_test.go b/server/common/etag_test.go index d80d6deb8..d4784727e 100644 --- a/server/common/etag_test.go +++ b/server/common/etag_test.go @@ -8,18 +8,19 @@ import ( "github.com/OpenListTeam/OpenList/v4/pkg/utils" ) -func TestGetEtagSubsecondChanges(t *testing.T) { - file := &model.Object{Size: 4, Modified: time.Unix(1700000000, 100)} - before := GetEtag(file, file.Size) - if got := GetEtag(file, file.Size); got != before { - t.Fatalf("unchanged file ETag = %q, want %q", got, before) +func TestGetEtagStableAcrossCacheRefresh(t *testing.T) { + uploaded := &model.Object{Size: 4, Modified: time.Unix(1700000000, 123456789)} + before := GetEtag(uploaded, uploaded.Size) + refreshed := &model.Object{Size: uploaded.Size, Modified: time.Unix(1700000000, 0)} + if got := GetEtag(refreshed, refreshed.Size); got != before { + t.Fatalf("refreshed file ETag = %q, want %q", got, before) } - file.Modified = file.Modified.Add(time.Nanosecond) - if got := GetEtag(file, file.Size); got == before { - t.Errorf("same-size overwrite within one second retained ETag %q", got) + refreshed.Modified = refreshed.Modified.Add(time.Second) + if got := GetEtag(refreshed, refreshed.Size); got == before { + t.Errorf("changed modification second retained ETag %q", got) } - file.Modified = file.Modified.Add(-time.Nanosecond) - if got := GetEtag(file, file.Size+1); got == before { + refreshed.Modified = uploaded.Modified + if got := GetEtag(refreshed, refreshed.Size+1); got == before { t.Errorf("changed size retained ETag %q", got) } } diff --git a/server/common/proxy.go b/server/common/proxy.go index b67aaa43b..5579eeca1 100644 --- a/server/common/proxy.go +++ b/server/common/proxy.go @@ -97,8 +97,9 @@ func GetEtag(file model.Obj, size int64) string { if len(hash) > 0 { return fmt.Sprintf(`"%s"`, hash) } - // Preserve sub-second changes when files of the same size are overwritten. - return fmt.Sprintf(`"%x-%x"`, file.ModTime().UnixNano(), size) + // Storage backends may not preserve sub-second timestamps across cache + // refreshes, so only use the reproducible second precision here. + return fmt.Sprintf(`"%x-%x"`, file.ModTime().Unix(), size) } func ProxyRange(ctx context.Context, link *model.Link, size int64) *model.Link {