From a1052c04db16252d374af465dfd38aaa30350bbf Mon Sep 17 00:00:00 2001 From: Alex Vanin Date: Thu, 1 Sep 2022 12:21:27 +0300 Subject: [PATCH] [#198] Fix expiration epoch calculation Previous implementation does not provide 'at least' lifetime guarantee. Signed-off-by: Alex Vanin --- uploader/filter.go | 18 +++++++++++++++--- uploader/filter_test.go | 20 ++++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/uploader/filter.go b/uploader/filter.go index 0152920..e9e9395 100644 --- a/uploader/filter.go +++ b/uploader/filter.go @@ -3,6 +3,7 @@ package uploader import ( "bytes" "fmt" + "math" "strconv" "time" @@ -121,7 +122,18 @@ func prepareExpirationHeader(headers map[string]string, epochDurations *epochDur } func updateExpirationHeader(headers map[string]string, durations *epochDurations, expDuration time.Duration) { - epochDuration := durations.msPerBlock * int64(durations.blockPerEpoch) - numEpoch := expDuration.Milliseconds() / epochDuration - headers[object.SysAttributeExpEpoch] = strconv.FormatInt(int64(durations.currentEpoch)+numEpoch, 10) + epochDuration := uint64(durations.msPerBlock) * durations.blockPerEpoch + currentEpoch := durations.currentEpoch + numEpoch := uint64(expDuration.Milliseconds()) / epochDuration + + if uint64(expDuration.Milliseconds())%epochDuration != 0 { + numEpoch++ + } + + expirationEpoch := uint64(math.MaxUint64) + if numEpoch < math.MaxUint64-currentEpoch { + expirationEpoch = currentEpoch + numEpoch + } + + headers[object.SysAttributeExpEpoch] = strconv.FormatUint(expirationEpoch, 10) } diff --git a/uploader/filter_test.go b/uploader/filter_test.go index 913f076..f1e300d 100644 --- a/uploader/filter_test.go +++ b/uploader/filter_test.go @@ -1,6 +1,7 @@ package uploader import ( + "math" "strconv" "testing" "time" @@ -52,8 +53,13 @@ func TestPrepareExpirationHeader(t *testing.T) { blockPerEpoch: 101, } - epochPerDay := (24 * time.Hour).Milliseconds() / int64(defaultDurations.blockPerEpoch) / defaultDurations.msPerBlock - defaultExpEpoch := strconv.FormatInt(int64(defaultDurations.currentEpoch)+epochPerDay, 10) + msPerBlock := defaultDurations.blockPerEpoch * uint64(defaultDurations.msPerBlock) + epochPerDay := uint64((24 * time.Hour).Milliseconds()) / msPerBlock + if uint64((24*time.Hour).Milliseconds())%msPerBlock != 0 { + epochPerDay++ + } + + defaultExpEpoch := strconv.FormatUint(defaultDurations.currentEpoch+epochPerDay, 10) for _, tc := range []struct { name string @@ -130,6 +136,16 @@ func TestPrepareExpirationHeader(t *testing.T) { durations: defaultDurations, expected: map[string]string{object.SysAttributeExpEpoch: defaultExpEpoch}, }, + { + name: "valid max uint 64", + headers: map[string]string{utils.ExpirationRFC3339Attr: tomorrow.Format(time.RFC3339)}, + durations: &epochDurations{ + currentEpoch: math.MaxUint64 - 1, + msPerBlock: defaultDurations.msPerBlock, + blockPerEpoch: defaultDurations.blockPerEpoch, + }, + expected: map[string]string{object.SysAttributeExpEpoch: strconv.FormatUint(uint64(math.MaxUint64), 10)}, + }, { name: "invalid timestamp sec", headers: map[string]string{utils.ExpirationTimestampAttr: "abc"},