forked from TrueCloudLab/frostfs-node
[#744] util/attributes: Support escaped symbols
To encode attributes with semicolon or slash, use backslash as escaped character. Example: User-Agent:NeoFS\/0.23 Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
parent
c3e2738a46
commit
6527f9157c
2 changed files with 44 additions and 1 deletions
|
@ -20,7 +20,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ParseV2Attributes parses strings like "K1:V1/K2:V2/K3:V3" into netmap
|
// ParseV2Attributes parses strings like "K1:V1/K2:V2/K3:V3" into netmap
|
||||||
// attributes.
|
// attributes. Supports escaped symbols "\:", "\/" and "\\".
|
||||||
func ParseV2Attributes(attrs []string, excl []string) ([]*netmap.NodeAttribute, error) {
|
func ParseV2Attributes(attrs []string, excl []string) ([]*netmap.NodeAttribute, error) {
|
||||||
restricted := make(map[string]struct{}, len(excl))
|
restricted := make(map[string]struct{}, len(excl))
|
||||||
for i := range excl {
|
for i := range excl {
|
||||||
|
@ -31,6 +31,7 @@ func ParseV2Attributes(attrs []string, excl []string) ([]*netmap.NodeAttribute,
|
||||||
|
|
||||||
for i := range attrs {
|
for i := range attrs {
|
||||||
line := strings.Trim(attrs[i], pairSeparator)
|
line := strings.Trim(attrs[i], pairSeparator)
|
||||||
|
line = replaceEscaping(line, false) // replaced escaped symbols with non-printable symbols
|
||||||
chain := strings.Split(line, pairSeparator)
|
chain := strings.Split(line, pairSeparator)
|
||||||
if len(chain) == 0 {
|
if len(chain) == 0 {
|
||||||
return nil, errEmptyChain
|
return nil, errEmptyChain
|
||||||
|
@ -55,6 +56,10 @@ func ParseV2Attributes(attrs []string, excl []string) ([]*netmap.NodeAttribute,
|
||||||
return nil, errUnexpectedKey
|
return nil, errUnexpectedKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// replace non-printable symbols with escaped symbols without escape character
|
||||||
|
key = replaceEscaping(key, true)
|
||||||
|
value = replaceEscaping(value, true)
|
||||||
|
|
||||||
attribute := netmap.NewNodeAttribute()
|
attribute := netmap.NewNodeAttribute()
|
||||||
attribute.SetKey(key)
|
attribute.SetKey(key)
|
||||||
attribute.SetValue(value)
|
attribute.SetValue(value)
|
||||||
|
@ -75,3 +80,29 @@ func ParseV2Attributes(attrs []string, excl []string) ([]*netmap.NodeAttribute,
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func replaceEscaping(target string, rollback bool) (s string) {
|
||||||
|
const escChar = `\`
|
||||||
|
|
||||||
|
var (
|
||||||
|
oldPairSep = escChar + pairSeparator
|
||||||
|
oldKVSep = escChar + keyValueSeparator
|
||||||
|
oldEsc = escChar + escChar
|
||||||
|
newPairSep = string(uint8(1))
|
||||||
|
newKVSep = string(uint8(2))
|
||||||
|
newEsc = string(uint8(3))
|
||||||
|
)
|
||||||
|
|
||||||
|
if rollback {
|
||||||
|
oldPairSep, oldKVSep, oldEsc = newPairSep, newKVSep, newEsc
|
||||||
|
newPairSep = pairSeparator
|
||||||
|
newKVSep = keyValueSeparator
|
||||||
|
newEsc = escChar
|
||||||
|
}
|
||||||
|
|
||||||
|
s = strings.ReplaceAll(target, oldEsc, newEsc)
|
||||||
|
s = strings.ReplaceAll(s, oldPairSep, newPairSep)
|
||||||
|
s = strings.ReplaceAll(s, oldKVSep, newKVSep)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -57,4 +57,16 @@ func TestParseV2Attributes(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, attrs, 5)
|
require.Len(t, attrs, 5)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("escape characters", func(t *testing.T) {
|
||||||
|
from := []string{
|
||||||
|
`/K\:ey1:V\/alue\\/Ke\/y2:Va\:lue`,
|
||||||
|
}
|
||||||
|
attrs, err := attributes.ParseV2Attributes(from, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, attrs[0].Key(), `K:ey1`)
|
||||||
|
require.Equal(t, attrs[0].Value(), `V/alue\`)
|
||||||
|
require.Equal(t, attrs[1].Key(), `Ke/y2`)
|
||||||
|
require.Equal(t, attrs[1].Value(), `Va:lue`)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue