290 lines
7.3 KiB
Go
290 lines
7.3 KiB
Go
|
|
|
|
package common
|
|
|
|
|
|
import "bytes"
|
|
import "fmt"
|
|
import "regexp"
|
|
import "sort"
|
|
import "strconv"
|
|
|
|
|
|
|
|
|
|
func MetadataEncodeHttp (_metadata map[string]string) ([]byte, error) {
|
|
|
|
if len (_metadata) > 128 {
|
|
return nil, fmt.Errorf ("[c56fce8f] invalid metadata: too large!")
|
|
}
|
|
|
|
_metadataArray := make ([][2]string, 0, len (_metadata))
|
|
for _key, _value := range _metadata {
|
|
if _value == "" {
|
|
continue
|
|
}
|
|
_metadataArray = append (_metadataArray, [2]string {_key, _value})
|
|
}
|
|
sort.Slice (_metadataArray,
|
|
func (i int, j int) (bool) {
|
|
return _metadataArray[i][0] < _metadataArray[j][0]
|
|
})
|
|
|
|
_buffer := & bytes.Buffer {}
|
|
|
|
for _, _metadata := range _metadataArray {
|
|
_key := _metadata[0]
|
|
_value := _metadata[1]
|
|
if ! metadataKeyRegex.MatchString (_key) {
|
|
return nil, fmt.Errorf ("[2f761e02] invalid metadata key: `%s`", _key)
|
|
}
|
|
if ! metadataValueRegex.MatchString (_value) {
|
|
return nil, fmt.Errorf ("[e8faf5bd] invalid metadata value: `%s`", _value)
|
|
}
|
|
_buffer.Write ([]byte (_key))
|
|
_buffer.Write ([]byte (": "))
|
|
_buffer.Write ([]byte (_value))
|
|
_buffer.Write ([]byte ("\r\n"))
|
|
}
|
|
|
|
_data := _buffer.Bytes ()
|
|
return _data, nil
|
|
}
|
|
|
|
|
|
|
|
|
|
func MetadataDecodeHttp (_data []byte) ([][2]string, error) {
|
|
_metadata := make ([][2]string, 0, 16)
|
|
_metadataAppend := func (_key []byte, _value []byte) () {
|
|
_metadata = append (_metadata, [2]string { string (_key), string (_value) })
|
|
}
|
|
if _error := MetadataDecodeHttpIterate (_data, _metadataAppend); _error != nil {
|
|
return nil, _error
|
|
} else {
|
|
return _metadata, nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
func MetadataDecodeHttpIterate (_data []byte, _callback func ([]byte, []byte) ()) (error) {
|
|
|
|
_dataSize := len (_data)
|
|
_headerOffset := 0
|
|
|
|
for {
|
|
|
|
if _headerOffset == _dataSize {
|
|
return nil
|
|
}
|
|
|
|
_data := _data[_headerOffset :]
|
|
_headerLimit := bytes.Index (_data, []byte ("\r\n"))
|
|
if (_headerLimit == -1) {
|
|
return fmt.Errorf ("[2d0d442a] invalid metadata encoding")
|
|
}
|
|
|
|
_data = _data[: _headerLimit]
|
|
_separator := bytes.Index (_data, []byte (": "))
|
|
if _separator == -1 {
|
|
return fmt.Errorf ("[41f3756c] invalid metadata encoding")
|
|
}
|
|
|
|
_key := _data[: _separator]
|
|
_value := _data[_separator + 2 :]
|
|
if _separator == 0 {
|
|
return fmt.Errorf ("[c3f5e8f3] invalid metadata encoding (empty key)")
|
|
}
|
|
if _separator == (_headerLimit - 2) {
|
|
return fmt.Errorf ("[d6a923b6] invalid metadata encoding (empty value)")
|
|
}
|
|
|
|
_callback (_key, _value)
|
|
|
|
_headerOffset += _headerLimit + 2
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
func MetadataEncodeBinary (_metadata map[string]string) ([]byte, error) {
|
|
|
|
if len (_metadata) > 128 {
|
|
return nil, fmt.Errorf ("[2249daa0] invalid metadata: too large!")
|
|
}
|
|
|
|
_metadataArray := make ([][2]string, 0, len (_metadata))
|
|
for _key, _value := range _metadata {
|
|
if _value == "" {
|
|
continue
|
|
}
|
|
_metadataArray = append (_metadataArray, [2]string {_key, _value})
|
|
}
|
|
sort.Slice (_metadataArray,
|
|
func (i int, j int) (bool) {
|
|
return _metadataArray[i][0] < _metadataArray[j][0]
|
|
})
|
|
|
|
_buffer := & bytes.Buffer {}
|
|
|
|
for _, _metadata := range _metadataArray {
|
|
_key := _metadata[0]
|
|
_value := _metadata[1]
|
|
if (_key != "") && (_key[0] == '!') {
|
|
if _key == "!Status" {
|
|
if _value, _error := strconv.Atoi (_value); _error == nil {
|
|
if (_value >= 200) && (_value <= 599) {
|
|
// NOP
|
|
} else {
|
|
return nil, fmt.Errorf ("[08d97429] invalid metadata value: `%d`", _value)
|
|
}
|
|
} else {
|
|
return nil, fmt.Errorf ("[7a36c814] invalid metadata value: `%s`", _value)
|
|
}
|
|
} else {
|
|
return nil, fmt.Errorf ("[777a334d] invalid metadata key: `%s`", _key)
|
|
}
|
|
} else {
|
|
if ! metadataKeyRegex.MatchString (_key) {
|
|
return nil, fmt.Errorf ("[9c53ceb6] invalid metadata key: `%s`", _key)
|
|
}
|
|
if ! metadataValueRegex.MatchString (_value) {
|
|
return nil, fmt.Errorf ("[f932f38f] invalid metadata value: `%s`", _value)
|
|
}
|
|
}
|
|
|
|
_keyId, _keyFound := CanonicalHeaderNamesToKey[_key]
|
|
_valueId, _valueFound := CanonicalHeaderValuesToKey[_value]
|
|
if !_keyFound {
|
|
return nil, fmt.Errorf ("[a2a62863] invalid metadata key: `%s` (not canonical)", _key)
|
|
}
|
|
if _valueFound {
|
|
var _pairBuffer [16]byte
|
|
if _error := EncodeKeysPairToBytes_0 (NamespaceHeaderName, _keyId, NamespaceHeaderValue, _valueId, _pairBuffer[:]); _error != nil {
|
|
return nil, _error
|
|
}
|
|
_buffer.Write (_pairBuffer[:])
|
|
} else {
|
|
_valueSize := len (_value)
|
|
if _valueSize > 4096 {
|
|
return nil, fmt.Errorf ("[85c25588] invalid metadata value: `%s`", _value)
|
|
}
|
|
var _pairBuffer [12]byte
|
|
if _error := EncodeKeyToBytes_0 (NamespaceHeaderName, _keyId, _pairBuffer[:8]); _error != nil {
|
|
return nil, _error
|
|
}
|
|
_pairBuffer[8] = 'Z'
|
|
_pairBuffer[9] = byte (_valueSize / 25 / 25) + 'a'
|
|
_pairBuffer[10] = byte (_valueSize / 25 % 25) + 'a'
|
|
_pairBuffer[11] = byte (_valueSize % 25) + 'a'
|
|
_buffer.Write (_pairBuffer[:])
|
|
_buffer.Write ([]byte (_value))
|
|
}
|
|
}
|
|
|
|
_data := _buffer.Bytes ()
|
|
return _data, nil
|
|
}
|
|
|
|
|
|
|
|
|
|
func MetadataDecodeBinary (_data []byte) ([][2]string, error) {
|
|
_metadata := make ([][2]string, 0, 16)
|
|
_metadataAppend := func (_key []byte, _value []byte) () {
|
|
_metadata = append (_metadata, [2]string { string (_key), string (_value) })
|
|
}
|
|
if _error := MetadataDecodeBinaryIterate (_data, _metadataAppend); _error != nil {
|
|
return nil, _error
|
|
} else {
|
|
return _metadata, nil
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
func MetadataDecodeBinaryIterate (_data []byte, _callback func ([]byte, []byte) ()) (error) {
|
|
|
|
_dataLimit := len (_data)
|
|
_dataOffset := 0
|
|
|
|
for {
|
|
|
|
_sliceSize := _dataLimit - _dataOffset
|
|
if _sliceSize == 0 {
|
|
return nil
|
|
}
|
|
_slice := _data[_dataOffset:]
|
|
|
|
if _slice[0] != NamespaceHeaderNamePrefix {
|
|
return fmt.Errorf ("[f49c93cb] invalid metadata encoding")
|
|
}
|
|
if _sliceSize < 8 {
|
|
return fmt.Errorf ("[e8d008dc] invalid metadata encoding")
|
|
}
|
|
var _key []byte
|
|
if _key_0, _found := CanonicalHeaderNamesFromKey[DecodeKey_9 (_slice[0:8])]; _found {
|
|
_key = StringToBytes (_key_0)
|
|
} else {
|
|
return fmt.Errorf ("[7aa09c0f] invalid metadata encoding")
|
|
}
|
|
|
|
_dataOffset += 8
|
|
|
|
_sliceSize = _dataLimit - _dataOffset
|
|
if _sliceSize == 0 {
|
|
return fmt.Errorf ("[77c8bef7] invalid metadata encoding")
|
|
}
|
|
_slice = _data[_dataOffset:]
|
|
|
|
var _value []byte
|
|
if _slice[0] == NamespaceHeaderValuePrefix {
|
|
|
|
if _sliceSize < 8 {
|
|
return fmt.Errorf ("[7cd40b03] invalid metadata encoding")
|
|
}
|
|
if _value_0, _found := CanonicalHeaderValuesFromKey[DecodeKey_9 (_slice[0:8])]; _found {
|
|
_value = StringToBytes (_value_0)
|
|
} else {
|
|
return fmt.Errorf ("[334e65ef] invalid metadata encoding")
|
|
}
|
|
|
|
_dataOffset += 8
|
|
|
|
} else if _slice[0] == 'Z' {
|
|
|
|
if _sliceSize < 4 {
|
|
return fmt.Errorf ("[e52b70b0] invalid metadata encoding")
|
|
}
|
|
|
|
_valueSize := 0
|
|
_valueSize += int (_slice[1] - 'a') * 25 * 25
|
|
_valueSize += int (_slice[2] - 'a') * 25
|
|
_valueSize += int (_slice[3] - 'a')
|
|
|
|
if _sliceSize < (4 + _valueSize) {
|
|
return fmt.Errorf ("[3c4a6b51] invalid metadata encoding")
|
|
}
|
|
|
|
_value = _slice[4 : 4 + _valueSize]
|
|
|
|
_dataOffset += 4 + _valueSize
|
|
|
|
} else {
|
|
return fmt.Errorf ("[2b43651c] invalid metadata encoding")
|
|
}
|
|
|
|
_callback (_key, _value)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
var metadataKeyRegex = regexp.MustCompile (`\A(?:[A-Z0-9](?:[a-z0-9]?[a-z]+)(?:-[A-Z0-9](?:[a-z0-9]?[a-z]+))*)|ETag\z`)
|
|
var metadataValueRegex = regexp.MustCompile (`\A[[:graph:]](?: ?[[:graph:]]+)*\z`)
|
|
|