forked from TrueCloudLab/frostfs-http-gw
[#111] Move attributes to a separate file
Signed-off-by: Angira Kekteeva <kira@nspcc.ru>
This commit is contained in:
parent
79501077ff
commit
dbbc9e05cf
6 changed files with 52 additions and 47 deletions
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-http-gw/response"
|
"github.com/nspcc-dev/neofs-http-gw/response"
|
||||||
"github.com/nspcc-dev/neofs-http-gw/tokens"
|
"github.com/nspcc-dev/neofs-http-gw/tokens"
|
||||||
|
"github.com/nspcc-dev/neofs-http-gw/utils"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/client"
|
"github.com/nspcc-dev/neofs-sdk-go/client"
|
||||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
|
@ -152,7 +153,7 @@ func (r request) receiveFile(clnt pool.Object, objectAddress *object.Address) {
|
||||||
if !isValidToken(key) || !isValidValue(val) {
|
if !isValidToken(key) || !isValidValue(val) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r.Response.Header.Set("X-Attribute-"+key, val)
|
r.Response.Header.Set(utils.UserAttributeHeaderPrefix+key, val)
|
||||||
switch key {
|
switch key {
|
||||||
case object.AttributeFileName:
|
case object.AttributeFileName:
|
||||||
filename = val
|
filename = val
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-http-gw/response"
|
"github.com/nspcc-dev/neofs-http-gw/response"
|
||||||
"github.com/nspcc-dev/neofs-http-gw/tokens"
|
"github.com/nspcc-dev/neofs-http-gw/tokens"
|
||||||
|
"github.com/nspcc-dev/neofs-http-gw/utils"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/client"
|
"github.com/nspcc-dev/neofs-sdk-go/client"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/object"
|
"github.com/nspcc-dev/neofs-sdk-go/object"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pool"
|
||||||
|
@ -40,7 +41,7 @@ func (r request) headObject(clnt pool.Object, objectAddress *object.Address) {
|
||||||
if !isValidToken(key) || !isValidValue(val) {
|
if !isValidToken(key) || !isValidValue(val) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
r.Response.Header.Set("X-Attribute-"+key, val)
|
r.Response.Header.Set(utils.UserAttributeHeaderPrefix+key, val)
|
||||||
switch key {
|
switch key {
|
||||||
case object.AttributeTimestamp:
|
case object.AttributeTimestamp:
|
||||||
value, err := strconv.ParseInt(val, 10, 64)
|
value, err := strconv.ParseInt(val, 10, 64)
|
||||||
|
|
|
@ -7,24 +7,16 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/object"
|
"github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||||
|
"github.com/nspcc-dev/neofs-http-gw/utils"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
userAttributeHeaderPrefix = "X-Attribute-"
|
|
||||||
systemAttributePrefix = "__NEOFS__"
|
|
||||||
|
|
||||||
expirationDurationAttr = systemAttributePrefix + "EXPIRATION_DURATION"
|
|
||||||
expirationTimestampAttr = systemAttributePrefix + "EXPIRATION_TIMESTAMP"
|
|
||||||
expirationRFC3339Attr = systemAttributePrefix + "EXPIRATION_RFC3339"
|
|
||||||
)
|
|
||||||
|
|
||||||
var neofsAttributeHeaderPrefixes = [...][]byte{[]byte("Neofs-"), []byte("NEOFS-"), []byte("neofs-")}
|
var neofsAttributeHeaderPrefixes = [...][]byte{[]byte("Neofs-"), []byte("NEOFS-"), []byte("neofs-")}
|
||||||
|
|
||||||
func systemTranslator(key, prefix []byte) []byte {
|
func systemTranslator(key, prefix []byte) []byte {
|
||||||
// replace specified prefix with `__NEOFS__`
|
// replace specified prefix with `__NEOFS__`
|
||||||
key = bytes.Replace(key, prefix, []byte(systemAttributePrefix), 1)
|
key = bytes.Replace(key, prefix, []byte(utils.SystemAttributePrefix), 1)
|
||||||
|
|
||||||
// replace `-` with `_`
|
// replace `-` with `_`
|
||||||
key = bytes.ReplaceAll(key, []byte("-"), []byte("_"))
|
key = bytes.ReplaceAll(key, []byte("-"), []byte("_"))
|
||||||
|
@ -35,7 +27,7 @@ func systemTranslator(key, prefix []byte) []byte {
|
||||||
|
|
||||||
func filterHeaders(l *zap.Logger, header *fasthttp.RequestHeader) map[string]string {
|
func filterHeaders(l *zap.Logger, header *fasthttp.RequestHeader) map[string]string {
|
||||||
result := make(map[string]string)
|
result := make(map[string]string)
|
||||||
prefix := []byte(userAttributeHeaderPrefix)
|
prefix := []byte(utils.UserAttributeHeaderPrefix)
|
||||||
|
|
||||||
header.VisitAll(func(key, val []byte) {
|
header.VisitAll(func(key, val []byte) {
|
||||||
// checks that key and val not empty
|
// checks that key and val not empty
|
||||||
|
@ -80,45 +72,45 @@ func filterHeaders(l *zap.Logger, header *fasthttp.RequestHeader) map[string]str
|
||||||
func prepareExpirationHeader(headers map[string]string, epochDurations *epochDurations) error {
|
func prepareExpirationHeader(headers map[string]string, epochDurations *epochDurations) error {
|
||||||
expirationInEpoch := headers[object.SysAttributeExpEpoch]
|
expirationInEpoch := headers[object.SysAttributeExpEpoch]
|
||||||
|
|
||||||
if timeRFC3339, ok := headers[expirationRFC3339Attr]; ok {
|
if timeRFC3339, ok := headers[utils.ExpirationRFC3339Attr]; ok {
|
||||||
expTime, err := time.Parse(time.RFC3339, timeRFC3339)
|
expTime, err := time.Parse(time.RFC3339, timeRFC3339)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't parse value %s of header %s", timeRFC3339, expirationRFC3339Attr)
|
return fmt.Errorf("couldn't parse value %s of header %s", timeRFC3339, utils.ExpirationRFC3339Attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
if expTime.Before(now) {
|
if expTime.Before(now) {
|
||||||
return fmt.Errorf("value %s of header %s must be in the future", timeRFC3339, expirationRFC3339Attr)
|
return fmt.Errorf("value %s of header %s must be in the future", timeRFC3339, utils.ExpirationRFC3339Attr)
|
||||||
}
|
}
|
||||||
updateExpirationHeader(headers, epochDurations, expTime.Sub(now))
|
updateExpirationHeader(headers, epochDurations, expTime.Sub(now))
|
||||||
delete(headers, expirationRFC3339Attr)
|
delete(headers, utils.ExpirationRFC3339Attr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if timestamp, ok := headers[expirationTimestampAttr]; ok {
|
if timestamp, ok := headers[utils.ExpirationTimestampAttr]; ok {
|
||||||
value, err := strconv.ParseInt(timestamp, 10, 64)
|
value, err := strconv.ParseInt(timestamp, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't parse value %s of header %s", timestamp, expirationTimestampAttr)
|
return fmt.Errorf("couldn't parse value %s of header %s", timestamp, utils.ExpirationTimestampAttr)
|
||||||
}
|
}
|
||||||
expTime := time.Unix(value, 0)
|
expTime := time.Unix(value, 0)
|
||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
if expTime.Before(now) {
|
if expTime.Before(now) {
|
||||||
return fmt.Errorf("value %s of header %s must be in the future", timestamp, expirationTimestampAttr)
|
return fmt.Errorf("value %s of header %s must be in the future", timestamp, utils.ExpirationTimestampAttr)
|
||||||
}
|
}
|
||||||
updateExpirationHeader(headers, epochDurations, expTime.Sub(now))
|
updateExpirationHeader(headers, epochDurations, expTime.Sub(now))
|
||||||
delete(headers, expirationTimestampAttr)
|
delete(headers, utils.ExpirationTimestampAttr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if duration, ok := headers[expirationDurationAttr]; ok {
|
if duration, ok := headers[utils.ExpirationDurationAttr]; ok {
|
||||||
expDuration, err := time.ParseDuration(duration)
|
expDuration, err := time.ParseDuration(duration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't parse value %s of header %s", duration, expirationDurationAttr)
|
return fmt.Errorf("couldn't parse value %s of header %s", duration, utils.ExpirationDurationAttr)
|
||||||
}
|
}
|
||||||
if expDuration <= 0 {
|
if expDuration <= 0 {
|
||||||
return fmt.Errorf("value %s of header %s must be positive", expDuration, expirationDurationAttr)
|
return fmt.Errorf("value %s of header %s must be positive", expDuration, utils.ExpirationDurationAttr)
|
||||||
}
|
}
|
||||||
updateExpirationHeader(headers, epochDurations, expDuration)
|
updateExpirationHeader(headers, epochDurations, expDuration)
|
||||||
delete(headers, expirationDurationAttr)
|
delete(headers, utils.ExpirationDurationAttr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if expirationInEpoch != "" {
|
if expirationInEpoch != "" {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-api-go/v2/object"
|
"github.com/nspcc-dev/neofs-api-go/v2/object"
|
||||||
|
"github.com/nspcc-dev/neofs-http-gw/utils"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/logger"
|
"github.com/nspcc-dev/neofs-sdk-go/logger"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
|
@ -71,8 +71,8 @@ func TestPrepareExpirationHeader(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "valid epoch, valid duration",
|
name: "valid epoch, valid duration",
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
object.SysAttributeExpEpoch: epoch,
|
object.SysAttributeExpEpoch: epoch,
|
||||||
expirationDurationAttr: duration,
|
utils.ExpirationDurationAttr: duration,
|
||||||
},
|
},
|
||||||
durations: defaultDurations,
|
durations: defaultDurations,
|
||||||
expected: map[string]string{object.SysAttributeExpEpoch: epoch},
|
expected: map[string]string{object.SysAttributeExpEpoch: epoch},
|
||||||
|
@ -81,7 +81,7 @@ func TestPrepareExpirationHeader(t *testing.T) {
|
||||||
name: "valid epoch, valid rfc3339",
|
name: "valid epoch, valid rfc3339",
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
object.SysAttributeExpEpoch: epoch,
|
object.SysAttributeExpEpoch: epoch,
|
||||||
expirationRFC3339Attr: tomorrow.Format(time.RFC3339),
|
utils.ExpirationRFC3339Attr: tomorrow.Format(time.RFC3339),
|
||||||
},
|
},
|
||||||
durations: defaultDurations,
|
durations: defaultDurations,
|
||||||
expected: map[string]string{object.SysAttributeExpEpoch: epoch},
|
expected: map[string]string{object.SysAttributeExpEpoch: epoch},
|
||||||
|
@ -89,8 +89,8 @@ func TestPrepareExpirationHeader(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "valid epoch, valid timestamp sec",
|
name: "valid epoch, valid timestamp sec",
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
object.SysAttributeExpEpoch: epoch,
|
object.SysAttributeExpEpoch: epoch,
|
||||||
expirationTimestampAttr: timestampSec,
|
utils.ExpirationTimestampAttr: timestampSec,
|
||||||
},
|
},
|
||||||
durations: defaultDurations,
|
durations: defaultDurations,
|
||||||
expected: map[string]string{object.SysAttributeExpEpoch: epoch},
|
expected: map[string]string{object.SysAttributeExpEpoch: epoch},
|
||||||
|
@ -98,8 +98,8 @@ func TestPrepareExpirationHeader(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "valid epoch, valid timestamp milli",
|
name: "valid epoch, valid timestamp milli",
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
object.SysAttributeExpEpoch: epoch,
|
object.SysAttributeExpEpoch: epoch,
|
||||||
expirationTimestampAttr: timestampMilli,
|
utils.ExpirationTimestampAttr: timestampMilli,
|
||||||
},
|
},
|
||||||
durations: defaultDurations,
|
durations: defaultDurations,
|
||||||
expected: map[string]string{object.SysAttributeExpEpoch: epoch},
|
expected: map[string]string{object.SysAttributeExpEpoch: epoch},
|
||||||
|
@ -107,58 +107,58 @@ func TestPrepareExpirationHeader(t *testing.T) {
|
||||||
{
|
{
|
||||||
name: "valid epoch, valid timestamp nano",
|
name: "valid epoch, valid timestamp nano",
|
||||||
headers: map[string]string{
|
headers: map[string]string{
|
||||||
object.SysAttributeExpEpoch: epoch,
|
object.SysAttributeExpEpoch: epoch,
|
||||||
expirationTimestampAttr: timestampNano,
|
utils.ExpirationTimestampAttr: timestampNano,
|
||||||
},
|
},
|
||||||
durations: defaultDurations,
|
durations: defaultDurations,
|
||||||
expected: map[string]string{object.SysAttributeExpEpoch: epoch},
|
expected: map[string]string{object.SysAttributeExpEpoch: epoch},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid timestamp sec",
|
name: "valid timestamp sec",
|
||||||
headers: map[string]string{expirationTimestampAttr: timestampSec},
|
headers: map[string]string{utils.ExpirationTimestampAttr: timestampSec},
|
||||||
durations: defaultDurations,
|
durations: defaultDurations,
|
||||||
expected: map[string]string{object.SysAttributeExpEpoch: defaultExpEpoch},
|
expected: map[string]string{object.SysAttributeExpEpoch: defaultExpEpoch},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid duration",
|
name: "valid duration",
|
||||||
headers: map[string]string{expirationDurationAttr: duration},
|
headers: map[string]string{utils.ExpirationDurationAttr: duration},
|
||||||
durations: defaultDurations,
|
durations: defaultDurations,
|
||||||
expected: map[string]string{object.SysAttributeExpEpoch: defaultExpEpoch},
|
expected: map[string]string{object.SysAttributeExpEpoch: defaultExpEpoch},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "valid rfc3339",
|
name: "valid rfc3339",
|
||||||
headers: map[string]string{expirationRFC3339Attr: tomorrow.Format(time.RFC3339)},
|
headers: map[string]string{utils.ExpirationRFC3339Attr: tomorrow.Format(time.RFC3339)},
|
||||||
durations: defaultDurations,
|
durations: defaultDurations,
|
||||||
expected: map[string]string{object.SysAttributeExpEpoch: defaultExpEpoch},
|
expected: map[string]string{object.SysAttributeExpEpoch: defaultExpEpoch},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid timestamp sec",
|
name: "invalid timestamp sec",
|
||||||
headers: map[string]string{expirationTimestampAttr: "abc"},
|
headers: map[string]string{utils.ExpirationTimestampAttr: "abc"},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid timestamp sec zero",
|
name: "invalid timestamp sec zero",
|
||||||
headers: map[string]string{expirationTimestampAttr: "0"},
|
headers: map[string]string{utils.ExpirationTimestampAttr: "0"},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid duration",
|
name: "invalid duration",
|
||||||
headers: map[string]string{expirationDurationAttr: "1d"},
|
headers: map[string]string{utils.ExpirationDurationAttr: "1d"},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid duration negative",
|
name: "invalid duration negative",
|
||||||
headers: map[string]string{expirationDurationAttr: "-5h"},
|
headers: map[string]string{utils.ExpirationDurationAttr: "-5h"},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid rfc3339",
|
name: "invalid rfc3339",
|
||||||
headers: map[string]string{expirationRFC3339Attr: "abc"},
|
headers: map[string]string{utils.ExpirationRFC3339Attr: "abc"},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid rfc3339 zero",
|
name: "invalid rfc3339 zero",
|
||||||
headers: map[string]string{expirationRFC3339Attr: time.RFC3339},
|
headers: map[string]string{utils.ExpirationRFC3339Attr: time.RFC3339},
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neofs-http-gw/response"
|
"github.com/nspcc-dev/neofs-http-gw/response"
|
||||||
"github.com/nspcc-dev/neofs-http-gw/tokens"
|
"github.com/nspcc-dev/neofs-http-gw/tokens"
|
||||||
|
"github.com/nspcc-dev/neofs-http-gw/utils"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/client"
|
"github.com/nspcc-dev/neofs-sdk-go/client"
|
||||||
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
||||||
|
@ -220,8 +221,8 @@ func getEpochDurations(ctx context.Context, p pool.Pool) (*epochDurations, error
|
||||||
}
|
}
|
||||||
|
|
||||||
func needParseExpiration(headers map[string]string) bool {
|
func needParseExpiration(headers map[string]string) bool {
|
||||||
_, ok1 := headers[expirationDurationAttr]
|
_, ok1 := headers[utils.ExpirationDurationAttr]
|
||||||
_, ok2 := headers[expirationRFC3339Attr]
|
_, ok2 := headers[utils.ExpirationRFC3339Attr]
|
||||||
_, ok3 := headers[expirationTimestampAttr]
|
_, ok3 := headers[utils.ExpirationTimestampAttr]
|
||||||
return ok1 || ok2 || ok3
|
return ok1 || ok2 || ok3
|
||||||
}
|
}
|
||||||
|
|
10
utils/attributes.go
Normal file
10
utils/attributes.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
const (
|
||||||
|
UserAttributeHeaderPrefix = "X-Attribute-"
|
||||||
|
SystemAttributePrefix = "__NEOFS__"
|
||||||
|
|
||||||
|
ExpirationDurationAttr = SystemAttributePrefix + "EXPIRATION_DURATION"
|
||||||
|
ExpirationTimestampAttr = SystemAttributePrefix + "EXPIRATION_TIMESTAMP"
|
||||||
|
ExpirationRFC3339Attr = SystemAttributePrefix + "EXPIRATION_RFC3339"
|
||||||
|
)
|
Loading…
Reference in a new issue