diff options
author | vitalyisaev <vitalyisaev@ydb.tech> | 2023-12-12 21:55:07 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@ydb.tech> | 2023-12-12 22:25:10 +0300 |
commit | 4967f99474a4040ba150eb04995de06342252718 (patch) | |
tree | c9c118836513a8fab6e9fcfb25be5d404338bca7 /vendor/github.com/aws/smithy-go/encoding | |
parent | 2ce9cccb9b0bdd4cd7a3491dc5cbf8687cda51de (diff) | |
download | ydb-4967f99474a4040ba150eb04995de06342252718.tar.gz |
YQ Connector: prepare code base for S3 integration
1. Кодовая база Коннектора переписана с помощью Go дженериков так, чтобы добавление нового источника данных (в частности S3 + csv) максимально переиспользовало имеющийся код (чтобы сохранялась логика нарезания на блоки данных, учёт трафика и пр.)
2. API Connector расширено для работы с S3, но ещё пока не протестировано.
Diffstat (limited to 'vendor/github.com/aws/smithy-go/encoding')
49 files changed, 4913 insertions, 0 deletions
diff --git a/vendor/github.com/aws/smithy-go/encoding/doc.go b/vendor/github.com/aws/smithy-go/encoding/doc.go new file mode 100644 index 0000000000..792fdfa08b --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/doc.go @@ -0,0 +1,4 @@ +// Package encoding provides utilities for encoding values for specific +// document encodings. + +package encoding diff --git a/vendor/github.com/aws/smithy-go/encoding/encoding.go b/vendor/github.com/aws/smithy-go/encoding/encoding.go new file mode 100644 index 0000000000..2fdfb52250 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/encoding.go @@ -0,0 +1,40 @@ +package encoding + +import ( + "fmt" + "math" + "strconv" +) + +// EncodeFloat encodes a float value as per the stdlib encoder for json and xml protocol +// This encodes a float value into dst while attempting to conform to ES6 ToString for Numbers +// +// Based on encoding/json floatEncoder from the Go Standard Library +// https://golang.org/src/encoding/json/encode.go +func EncodeFloat(dst []byte, v float64, bits int) []byte { + if math.IsInf(v, 0) || math.IsNaN(v) { + panic(fmt.Sprintf("invalid float value: %s", strconv.FormatFloat(v, 'g', -1, bits))) + } + + abs := math.Abs(v) + fmt := byte('f') + + if abs != 0 { + if bits == 64 && (abs < 1e-6 || abs >= 1e21) || bits == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) { + fmt = 'e' + } + } + + dst = strconv.AppendFloat(dst, v, fmt, -1, bits) + + if fmt == 'e' { + // clean up e-09 to e-9 + n := len(dst) + if n >= 4 && dst[n-4] == 'e' && dst[n-3] == '-' && dst[n-2] == '0' { + dst[n-2] = dst[n-1] + dst = dst[:n-1] + } + } + + return dst +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/encode.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/encode.go new file mode 100644 index 0000000000..543e7cf038 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/encode.go @@ -0,0 +1,123 @@ +package httpbinding + +import ( + "fmt" + "net/http" + "net/url" + "strconv" + "strings" +) + +const ( + contentLengthHeader = "Content-Length" + floatNaN = "NaN" + floatInfinity = "Infinity" + floatNegInfinity = "-Infinity" +) + +// An Encoder provides encoding of REST URI path, query, and header components +// of an HTTP request. Can also encode a stream as the payload. +// +// Does not support SetFields. +type Encoder struct { + path, rawPath, pathBuffer []byte + + query url.Values + header http.Header +} + +// NewEncoder creates a new encoder from the passed in request. It assumes that +// raw path contains no valuable information at this point, so it passes in path +// as path and raw path for subsequent trans +func NewEncoder(path, query string, headers http.Header) (*Encoder, error) { + return NewEncoderWithRawPath(path, path, query, headers) +} + +// NewHTTPBindingEncoder creates a new encoder from the passed in request. All query and +// header values will be added on top of the request's existing values. Overwriting +// duplicate values. +func NewEncoderWithRawPath(path, rawPath, query string, headers http.Header) (*Encoder, error) { + parseQuery, err := url.ParseQuery(query) + if err != nil { + return nil, fmt.Errorf("failed to parse query string: %w", err) + } + + e := &Encoder{ + path: []byte(path), + rawPath: []byte(rawPath), + query: parseQuery, + header: headers.Clone(), + } + + return e, nil +} + +// Encode returns a REST protocol encoder for encoding HTTP bindings. +// +// Due net/http requiring `Content-Length` to be specified on the http.Request#ContentLength directly. Encode +// will look for whether the header is present, and if so will remove it and set the respective value on http.Request. +// +// Returns any error occurring during encoding. +func (e *Encoder) Encode(req *http.Request) (*http.Request, error) { + req.URL.Path, req.URL.RawPath = string(e.path), string(e.rawPath) + req.URL.RawQuery = e.query.Encode() + + // net/http ignores Content-Length header and requires it to be set on http.Request + if v := e.header.Get(contentLengthHeader); len(v) > 0 { + iv, err := strconv.ParseInt(v, 10, 64) + if err != nil { + return nil, err + } + req.ContentLength = iv + e.header.Del(contentLengthHeader) + } + + req.Header = e.header + + return req, nil +} + +// AddHeader returns a HeaderValue for appending to the given header name +func (e *Encoder) AddHeader(key string) HeaderValue { + return newHeaderValue(e.header, key, true) +} + +// SetHeader returns a HeaderValue for setting the given header name +func (e *Encoder) SetHeader(key string) HeaderValue { + return newHeaderValue(e.header, key, false) +} + +// Headers returns a Header used for encoding headers with the given prefix +func (e *Encoder) Headers(prefix string) Headers { + return Headers{ + header: e.header, + prefix: strings.TrimSpace(prefix), + } +} + +// HasHeader returns if a header with the key specified exists with one or +// more value. +func (e Encoder) HasHeader(key string) bool { + return len(e.header[key]) != 0 +} + +// SetURI returns a URIValue used for setting the given path key +func (e *Encoder) SetURI(key string) URIValue { + return newURIValue(&e.path, &e.rawPath, &e.pathBuffer, key) +} + +// SetQuery returns a QueryValue used for setting the given query key +func (e *Encoder) SetQuery(key string) QueryValue { + return NewQueryValue(e.query, key, false) +} + +// AddQuery returns a QueryValue used for appending the given query key +func (e *Encoder) AddQuery(key string) QueryValue { + return NewQueryValue(e.query, key, true) +} + +// HasQuery returns if a query with the key specified exists with one or +// more values. +func (e *Encoder) HasQuery(key string) bool { + return len(e.query.Get(key)) != 0 +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/encode_test.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/encode_test.go new file mode 100644 index 0000000000..6d972ae508 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/encode_test.go @@ -0,0 +1,146 @@ +package httpbinding + +import ( + "net/http" + "net/url" + "reflect" + "testing" +) + +func TestEncoder(t *testing.T) { + actual := &http.Request{ + Header: http.Header{ + "custom-user-header": {"someValue"}, + }, + URL: &url.URL{ + Path: "/some/{pathKeyOne}/{pathKeyTwo}", + RawQuery: "someExistingKeys=foobar", + }, + } + + expected := &http.Request{ + Header: map[string][]string{ + "custom-user-header": {"someValue"}, + "x-amzn-header-foo": {"someValue"}, + "x-amzn-meta-foo": {"someValue"}, + }, + URL: &url.URL{ + Path: "/some/someValue/path", + RawPath: "/some/someValue/path", + RawQuery: "someExistingKeys=foobar&someKey=someValue&someKey=otherValue", + }, + } + + encoder, err := NewEncoder(actual.URL.Path, actual.URL.RawQuery, actual.Header) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + // Headers + encoder.AddHeader("x-amzn-header-foo").String("someValue") + encoder.Headers("x-amzn-meta-").AddHeader("foo").String("someValue") + + // Query + encoder.SetQuery("someKey").String("someValue") + encoder.AddQuery("someKey").String("otherValue") + + // URI + if err := encoder.SetURI("pathKeyOne").String("someValue"); err != nil { + t.Errorf("expected no err, but got %v", err) + } + + // URI + if err := encoder.SetURI("pathKeyTwo").String("path"); err != nil { + t.Errorf("expected no err, but got %v", err) + } + + if actual, err = encoder.Encode(actual); err != nil { + t.Errorf("expected no err, but got %v", err) + } + + if !reflect.DeepEqual(expected, actual) { + t.Errorf("expected %v, but got %v", expected, actual) + } +} + +func TestEncoderHasHeader(t *testing.T) { + encoder, err := NewEncoder("/", "", http.Header{}) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if h := "i-dont-exist"; encoder.HasHeader(h) { + t.Errorf("expect %v not to be set", h) + } + + encoder.AddHeader("I-do-exist").String("some value") + + if h := "I-do-exist"; !encoder.HasHeader(h) { + t.Errorf("expect %v to be set", h) + } + +} + +func TestEncoderHasQuery(t *testing.T) { + encoder, err := NewEncoder("/", "", http.Header{}) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if q := "i-dont-exist"; encoder.HasQuery(q) { + t.Errorf("expect %v not to be set", q) + } + + encoder.AddQuery("I-do-exist").String("some value") + + if q := "I-do-exist"; !encoder.HasQuery(q) { + t.Errorf("expect %v to be set", q) + } + +} + +func TestEncodeContentLength(t *testing.T) { + cases := map[string]struct { + headerValue string + expected int64 + wantErr bool + }{ + "valid number": { + headerValue: "1024", + expected: 1024, + }, + "invalid number": { + headerValue: "1024.5", + wantErr: true, + }, + "not a number": { + headerValue: "NaN", + wantErr: true, + }, + } + + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + encoder, err := NewEncoder("/", "", http.Header{}) + if err != nil { + t.Fatalf("expect no error, got %v", err) + } + + encoder.SetHeader("Content-Length").String(tt.headerValue) + + req := &http.Request{URL: &url.URL{}} + req, err = encoder.Encode(req) + if (err != nil) != tt.wantErr { + t.Fatalf("unexpected error value wantErr=%v", tt.wantErr) + } else if tt.wantErr { + return + } + if e, a := tt.expected, req.ContentLength; e != a { + t.Errorf("expect %v, got %v", e, a) + } + if v := req.Header.Get("Content-Length"); len(v) > 0 { + t.Errorf("expect header not to be set") + } + }) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/gotest/ya.make b/vendor/github.com/aws/smithy-go/encoding/httpbinding/gotest/ya.make new file mode 100644 index 0000000000..c8a86501ea --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/gotest/ya.make @@ -0,0 +1,5 @@ +GO_TEST_FOR(vendor/github.com/aws/smithy-go/encoding/httpbinding) + +LICENSE(Apache-2.0) + +END() diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/header.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/header.go new file mode 100644 index 0000000000..f9256e175f --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/header.go @@ -0,0 +1,122 @@ +package httpbinding + +import ( + "encoding/base64" + "math" + "math/big" + "net/http" + "strconv" + "strings" +) + +// Headers is used to encode header keys using a provided prefix +type Headers struct { + header http.Header + prefix string +} + +// AddHeader returns a HeaderValue used to append values to prefix+key +func (h Headers) AddHeader(key string) HeaderValue { + return h.newHeaderValue(key, true) +} + +// SetHeader returns a HeaderValue used to set the value of prefix+key +func (h Headers) SetHeader(key string) HeaderValue { + return h.newHeaderValue(key, false) +} + +func (h Headers) newHeaderValue(key string, append bool) HeaderValue { + return newHeaderValue(h.header, h.prefix+strings.TrimSpace(key), append) +} + +// HeaderValue is used to encode values to an HTTP header +type HeaderValue struct { + header http.Header + key string + append bool +} + +func newHeaderValue(header http.Header, key string, append bool) HeaderValue { + return HeaderValue{header: header, key: strings.TrimSpace(key), append: append} +} + +func (h HeaderValue) modifyHeader(value string) { + if h.append { + h.header[h.key] = append(h.header[h.key], value) + } else { + h.header[h.key] = append(h.header[h.key][:0], value) + } +} + +// String encodes the value v as the header string value +func (h HeaderValue) String(v string) { + h.modifyHeader(v) +} + +// Byte encodes the value v as a query string value +func (h HeaderValue) Byte(v int8) { + h.Long(int64(v)) +} + +// Short encodes the value v as a query string value +func (h HeaderValue) Short(v int16) { + h.Long(int64(v)) +} + +// Integer encodes the value v as the header string value +func (h HeaderValue) Integer(v int32) { + h.Long(int64(v)) +} + +// Long encodes the value v as the header string value +func (h HeaderValue) Long(v int64) { + h.modifyHeader(strconv.FormatInt(v, 10)) +} + +// Boolean encodes the value v as a query string value +func (h HeaderValue) Boolean(v bool) { + h.modifyHeader(strconv.FormatBool(v)) +} + +// Float encodes the value v as a query string value +func (h HeaderValue) Float(v float32) { + h.float(float64(v), 32) +} + +// Double encodes the value v as a query string value +func (h HeaderValue) Double(v float64) { + h.float(v, 64) +} + +func (h HeaderValue) float(v float64, bitSize int) { + switch { + case math.IsNaN(v): + h.String(floatNaN) + case math.IsInf(v, 1): + h.String(floatInfinity) + case math.IsInf(v, -1): + h.String(floatNegInfinity) + default: + h.modifyHeader(strconv.FormatFloat(v, 'f', -1, bitSize)) + } +} + +// BigInteger encodes the value v as a query string value +func (h HeaderValue) BigInteger(v *big.Int) { + h.modifyHeader(v.String()) +} + +// BigDecimal encodes the value v as a query string value +func (h HeaderValue) BigDecimal(v *big.Float) { + if i, accuracy := v.Int64(); accuracy == big.Exact { + h.Long(i) + return + } + h.modifyHeader(v.Text('e', -1)) +} + +// Blob encodes the value v as a base64 header string value +func (h HeaderValue) Blob(v []byte) { + encodeToString := base64.StdEncoding.EncodeToString(v) + h.modifyHeader(encodeToString) +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/header_test.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/header_test.go new file mode 100644 index 0000000000..64e555c32b --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/header_test.go @@ -0,0 +1,295 @@ +package httpbinding + +import ( + "fmt" + "math/big" + "net/http" + "reflect" + "testing" +) + +func TestHeaderValue(t *testing.T) { + const keyName = "test-key" + const expectedKeyName = "test-key" + + cases := map[string]struct { + header http.Header + args []interface{} + append bool + expected http.Header + }{ + "set blob": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{[]byte("baz")}, + expected: map[string][]string{ + expectedKeyName: {"YmF6"}, + }, + }, + "set boolean": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{true}, + expected: map[string][]string{ + expectedKeyName: {"true"}, + }, + }, + "set string": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{"string value"}, + expected: map[string][]string{ + expectedKeyName: {"string value"}, + }, + }, + "set byte": { + header: http.Header{expectedKeyName: []string{"127"}}, + args: []interface{}{int8(127)}, + expected: map[string][]string{ + expectedKeyName: {"127"}, + }, + }, + "set short": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{int16(32767)}, + expected: map[string][]string{ + expectedKeyName: {"32767"}, + }, + }, + "set integer": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{int32(2147483647)}, + expected: map[string][]string{ + expectedKeyName: {"2147483647"}, + }, + }, + "set long": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{int64(9223372036854775807)}, + expected: map[string][]string{ + expectedKeyName: {"9223372036854775807"}, + }, + }, + "set float": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{float32(3.14159)}, + expected: map[string][]string{ + expectedKeyName: {"3.14159"}, + }, + }, + "set double": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{float64(3.14159)}, + expected: map[string][]string{ + expectedKeyName: {"3.14159"}, + }, + }, + "set bigInteger": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{new(big.Int).SetInt64(42)}, + expected: map[string][]string{ + expectedKeyName: {"42"}, + }, + }, + "set bigDecimal": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{new(big.Float).SetFloat64(1024.10241024)}, + expected: map[string][]string{ + expectedKeyName: {"1.02410241024e+03"}, + }, + }, + "add blob": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{[]byte("baz")}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"foobar", "YmF6"}, + }, + }, + "add bool": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{true}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"foobar", "true"}, + }, + }, + "add string": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{"string value"}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"foobar", "string value"}, + }, + }, + "add byte": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{int8(127)}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"foobar", "127"}, + }, + }, + "add short": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{int16(32767)}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"foobar", "32767"}, + }, + }, + "add integer": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{int32(2147483647)}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"foobar", "2147483647"}, + }, + }, + "add long": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{int64(9223372036854775807)}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"foobar", "9223372036854775807"}, + }, + }, + "add float": { + header: http.Header{expectedKeyName: []string{"1.61803"}}, + args: []interface{}{float32(3.14159)}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"1.61803", "3.14159"}, + }, + }, + "add double": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{float64(3.14159)}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"foobar", "3.14159"}, + }, + }, + "add bigInteger": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{new(big.Int).SetInt64(42)}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"foobar", "42"}, + }, + }, + "add bigDecimal": { + header: http.Header{expectedKeyName: []string{"foobar"}}, + args: []interface{}{new(big.Float).SetFloat64(1024.10241024)}, + append: true, + expected: map[string][]string{ + expectedKeyName: {"foobar", "1.02410241024e+03"}, + }, + }, + } + + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + if tt.header == nil { + tt.header = http.Header{} + } + + hv := newHeaderValue(tt.header, keyName, tt.append) + + if err := setHeader(hv, tt.args); err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if e, a := tt.expected, hv.header; !reflect.DeepEqual(e, a) { + t.Errorf("expected %v, got %v", e, a) + } + }) + } +} + +func TestHeaders(t *testing.T) { + const prefix = "X-Amzn-Meta-" + cases := map[string]struct { + headers http.Header + values map[string]string + append bool + expected http.Header + }{ + "set": { + headers: http.Header{ + "X-Amzn-Meta-Foo": {"bazValue"}, + }, + values: map[string]string{ + "Foo": "fooValue", + " Bar ": "barValue", + }, + expected: http.Header{ + "X-Amzn-Meta-Foo": {"fooValue"}, + "X-Amzn-Meta-Bar": {"barValue"}, + }, + }, + "add": { + headers: http.Header{ + "X-Amzn-Meta-Foo": {"bazValue"}, + }, + values: map[string]string{ + "Foo": "fooValue", + " Bar ": "barValue", + }, + append: true, + expected: http.Header{ + "X-Amzn-Meta-Foo": {"bazValue", "fooValue"}, + "X-Amzn-Meta-Bar": {"barValue"}, + }, + }, + } + + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + headers := Headers{header: tt.headers, prefix: prefix} + + var f func(key string) HeaderValue + if tt.append { + f = headers.AddHeader + } else { + f = headers.SetHeader + } + + for key, value := range tt.values { + f(key).String(value) + } + + if e, a := tt.expected, tt.headers; !reflect.DeepEqual(e, a) { + t.Errorf("expected %v, but got %v", e, a) + } + }) + } +} + +func setHeader(hv HeaderValue, args []interface{}) error { + value := args[0] + + switch value.(type) { + case []byte: + return reflectCall(reflect.ValueOf(hv.Blob), args) + case bool: + return reflectCall(reflect.ValueOf(hv.Boolean), args) + case string: + return reflectCall(reflect.ValueOf(hv.String), args) + case int8: + return reflectCall(reflect.ValueOf(hv.Byte), args) + case int16: + return reflectCall(reflect.ValueOf(hv.Short), args) + case int32: + return reflectCall(reflect.ValueOf(hv.Integer), args) + case int64: + return reflectCall(reflect.ValueOf(hv.Long), args) + case float32: + return reflectCall(reflect.ValueOf(hv.Float), args) + case float64: + return reflectCall(reflect.ValueOf(hv.Double), args) + case *big.Int: + return reflectCall(reflect.ValueOf(hv.BigInteger), args) + case *big.Float: + return reflectCall(reflect.ValueOf(hv.BigDecimal), args) + default: + return fmt.Errorf("unhandled header value type") + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/path_replace.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/path_replace.go new file mode 100644 index 0000000000..e78926c9a5 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/path_replace.go @@ -0,0 +1,108 @@ +package httpbinding + +import ( + "bytes" + "fmt" +) + +const ( + uriTokenStart = '{' + uriTokenStop = '}' + uriTokenSkip = '+' +) + +func bufCap(b []byte, n int) []byte { + if cap(b) < n { + return make([]byte, 0, n) + } + + return b[0:0] +} + +// replacePathElement replaces a single element in the path []byte. +// Escape is used to control whether the value will be escaped using Amazon path escape style. +func replacePathElement(path, fieldBuf []byte, key, val string, escape bool) ([]byte, []byte, error) { + fieldBuf = bufCap(fieldBuf, len(key)+3) // { <key> [+] } + fieldBuf = append(fieldBuf, uriTokenStart) + fieldBuf = append(fieldBuf, key...) + + start := bytes.Index(path, fieldBuf) + end := start + len(fieldBuf) + if start < 0 || len(path[end:]) == 0 { + // TODO what to do about error? + return path, fieldBuf, fmt.Errorf("invalid path index, start=%d,end=%d. %s", start, end, path) + } + + encodeSep := true + if path[end] == uriTokenSkip { + // '+' token means do not escape slashes + encodeSep = false + end++ + } + + if escape { + val = EscapePath(val, encodeSep) + } + + if path[end] != uriTokenStop { + return path, fieldBuf, fmt.Errorf("invalid path element, does not contain token stop, %s", path) + } + end++ + + fieldBuf = bufCap(fieldBuf, len(val)) + fieldBuf = append(fieldBuf, val...) + + keyLen := end - start + valLen := len(fieldBuf) + + if keyLen == valLen { + copy(path[start:], fieldBuf) + return path, fieldBuf, nil + } + + newLen := len(path) + (valLen - keyLen) + if len(path) < newLen { + path = path[:cap(path)] + } + if cap(path) < newLen { + newURI := make([]byte, newLen) + copy(newURI, path) + path = newURI + } + + // shift + copy(path[start+valLen:], path[end:]) + path = path[:newLen] + copy(path[start:], fieldBuf) + + return path, fieldBuf, nil +} + +// EscapePath escapes part of a URL path in Amazon style. +func EscapePath(path string, encodeSep bool) string { + var buf bytes.Buffer + for i := 0; i < len(path); i++ { + c := path[i] + if noEscape[c] || (c == '/' && !encodeSep) { + buf.WriteByte(c) + } else { + fmt.Fprintf(&buf, "%%%02X", c) + } + } + return buf.String() +} + +var noEscape [256]bool + +func init() { + for i := 0; i < len(noEscape); i++ { + // AWS expects every character except these to be escaped + noEscape[i] = (i >= 'A' && i <= 'Z') || + (i >= 'a' && i <= 'z') || + (i >= '0' && i <= '9') || + i == '-' || + i == '.' || + i == '_' || + i == '~' + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/path_replace_test.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/path_replace_test.go new file mode 100644 index 0000000000..689733ca00 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/path_replace_test.go @@ -0,0 +1,67 @@ +package httpbinding + +import ( + "bytes" + "testing" +) + +func TestPathReplace(t *testing.T) { + cases := []struct { + Orig, ExpPath, ExpRawPath []byte + Key, Val string + }{ + { + Orig: []byte("/{bucket}/{key+}"), + ExpPath: []byte("/123/{key+}"), + ExpRawPath: []byte("/123/{key+}"), + Key: "bucket", Val: "123", + }, + { + Orig: []byte("/{bucket}/{key+}"), + ExpPath: []byte("/{bucket}/abc"), + ExpRawPath: []byte("/{bucket}/abc"), + Key: "key", Val: "abc", + }, + { + Orig: []byte("/{bucket}/{key+}"), + ExpPath: []byte("/{bucket}/a/b/c"), + ExpRawPath: []byte("/{bucket}/a/b/c"), + Key: "key", Val: "a/b/c", + }, + { + Orig: []byte("/{bucket}/{key+}"), + ExpPath: []byte("/1/2/3/{key+}"), + ExpRawPath: []byte("/1%2F2%2F3/{key+}"), + Key: "bucket", Val: "1/2/3", + }, + { + Orig: []byte("/{bucket}/{key+}"), + ExpPath: []byte("/reallylongvaluegoesheregrowingarray/{key+}"), + ExpRawPath: []byte("/reallylongvaluegoesheregrowingarray/{key+}"), + Key: "bucket", Val: "reallylongvaluegoesheregrowingarray", + }, + } + + var buffer [64]byte + + for i, c := range cases { + origRaw := make([]byte, len(c.Orig)) + copy(origRaw, c.Orig) + + path, _, err := replacePathElement(c.Orig, buffer[:0], c.Key, c.Val, false) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + rawPath, _, err := replacePathElement(origRaw, buffer[:0], c.Key, c.Val, true) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if e, a := c.ExpPath, path; bytes.Compare(e, a) != 0 { + t.Errorf("%d, expect uri path to be %q got %q", i, e, a) + } + if e, a := c.ExpRawPath, rawPath; bytes.Compare(e, a) != 0 { + t.Errorf("%d, expect uri raw path to be %q got %q", i, e, a) + } + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/query.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/query.go new file mode 100644 index 0000000000..c2e7d0a20f --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/query.go @@ -0,0 +1,107 @@ +package httpbinding + +import ( + "encoding/base64" + "math" + "math/big" + "net/url" + "strconv" +) + +// QueryValue is used to encode query key values +type QueryValue struct { + query url.Values + key string + append bool +} + +// NewQueryValue creates a new QueryValue which enables encoding +// a query value into the given url.Values. +func NewQueryValue(query url.Values, key string, append bool) QueryValue { + return QueryValue{ + query: query, + key: key, + append: append, + } +} + +func (qv QueryValue) updateKey(value string) { + if qv.append { + qv.query.Add(qv.key, value) + } else { + qv.query.Set(qv.key, value) + } +} + +// Blob encodes v as a base64 query string value +func (qv QueryValue) Blob(v []byte) { + encodeToString := base64.StdEncoding.EncodeToString(v) + qv.updateKey(encodeToString) +} + +// Boolean encodes v as a query string value +func (qv QueryValue) Boolean(v bool) { + qv.updateKey(strconv.FormatBool(v)) +} + +// String encodes v as a query string value +func (qv QueryValue) String(v string) { + qv.updateKey(v) +} + +// Byte encodes v as a query string value +func (qv QueryValue) Byte(v int8) { + qv.Long(int64(v)) +} + +// Short encodes v as a query string value +func (qv QueryValue) Short(v int16) { + qv.Long(int64(v)) +} + +// Integer encodes v as a query string value +func (qv QueryValue) Integer(v int32) { + qv.Long(int64(v)) +} + +// Long encodes v as a query string value +func (qv QueryValue) Long(v int64) { + qv.updateKey(strconv.FormatInt(v, 10)) +} + +// Float encodes v as a query string value +func (qv QueryValue) Float(v float32) { + qv.float(float64(v), 32) +} + +// Double encodes v as a query string value +func (qv QueryValue) Double(v float64) { + qv.float(v, 64) +} + +func (qv QueryValue) float(v float64, bitSize int) { + switch { + case math.IsNaN(v): + qv.String(floatNaN) + case math.IsInf(v, 1): + qv.String(floatInfinity) + case math.IsInf(v, -1): + qv.String(floatNegInfinity) + default: + qv.updateKey(strconv.FormatFloat(v, 'f', -1, bitSize)) + } +} + +// BigInteger encodes v as a query string value +func (qv QueryValue) BigInteger(v *big.Int) { + qv.updateKey(v.String()) +} + +// BigDecimal encodes v as a query string value +func (qv QueryValue) BigDecimal(v *big.Float) { + if i, accuracy := v.Int64(); accuracy == big.Exact { + qv.Long(i) + return + } + qv.updateKey(v.Text('e', -1)) +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/query_test.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/query_test.go new file mode 100644 index 0000000000..62cf25cd67 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/query_test.go @@ -0,0 +1,234 @@ +package httpbinding + +import ( + "fmt" + "math/big" + "net/url" + "reflect" + "testing" +) + +func TestQueryValue(t *testing.T) { + const queryKey = "someKey" + + cases := map[string]struct { + values url.Values + args []interface{} + append bool + expected url.Values + }{ + "set blob": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{[]byte("baz")}, + expected: map[string][]string{ + queryKey: {"YmF6"}, + }, + }, + "set bool": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{true}, + expected: map[string][]string{ + queryKey: {"true"}, + }, + }, + "set string": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{"string value"}, + expected: map[string][]string{ + queryKey: {"string value"}, + }, + }, + "set byte": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{int8(127)}, + expected: map[string][]string{ + queryKey: {"127"}, + }, + }, + "set short": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{int16(32767)}, + expected: map[string][]string{ + queryKey: {"32767"}, + }, + }, + "set integer": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{int32(2147483647)}, + expected: map[string][]string{ + queryKey: {"2147483647"}, + }, + }, + "set long": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{int64(9223372036854775807)}, + expected: map[string][]string{ + queryKey: {"9223372036854775807"}, + }, + }, + "set float": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{float32(3.14159)}, + expected: map[string][]string{ + queryKey: {"3.14159"}, + }, + }, + "set double": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{float64(3.14159)}, + expected: map[string][]string{ + queryKey: {"3.14159"}, + }, + }, + "set bigInteger": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{new(big.Int).SetInt64(1)}, + expected: map[string][]string{ + queryKey: {"1"}, + }, + }, + "set bigDecimal": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{new(big.Float).SetFloat64(1024.10241024)}, + expected: map[string][]string{ + queryKey: {"1.02410241024e+03"}, + }, + }, + "add blob": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{[]byte("baz")}, + append: true, + expected: map[string][]string{ + queryKey: {"foobar", "YmF6"}, + }, + }, + "add bool": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{true}, + append: true, + expected: map[string][]string{ + queryKey: {"foobar", "true"}, + }, + }, + "add string": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{"string value"}, + append: true, + expected: map[string][]string{ + queryKey: {"foobar", "string value"}, + }, + }, + "add byte": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{int8(127)}, + append: true, + expected: map[string][]string{ + queryKey: {"foobar", "127"}, + }, + }, + "add short": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{int16(32767)}, + append: true, + expected: map[string][]string{ + queryKey: {"foobar", "32767"}, + }, + }, + "add integer": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{int32(2147483647)}, + expected: map[string][]string{ + queryKey: {"2147483647"}, + }, + }, + "add long": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{int64(9223372036854775807)}, + append: true, + expected: map[string][]string{ + queryKey: {"foobar", "9223372036854775807"}, + }, + }, + "add float": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{float32(3.14159)}, + append: true, + expected: map[string][]string{ + queryKey: {"foobar", "3.14159"}, + }, + }, + "add double": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{float64(3.14159)}, + append: true, + expected: map[string][]string{ + queryKey: {"foobar", "3.14159"}, + }, + }, + "add bigInteger": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{new(big.Int).SetInt64(1)}, + append: true, + expected: map[string][]string{ + queryKey: {"foobar", "1"}, + }, + }, + "add bigDecimal": { + values: url.Values{queryKey: []string{"foobar"}}, + args: []interface{}{new(big.Float).SetFloat64(1024.10241024)}, + append: true, + expected: map[string][]string{ + queryKey: {"foobar", "1.02410241024e+03"}, + }, + }, + } + + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + if tt.values == nil { + tt.values = url.Values{} + } + + qv := NewQueryValue(tt.values, queryKey, tt.append) + + if err := setQueryValue(qv, tt.args); err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if e, a := tt.expected, qv.query; !reflect.DeepEqual(e, a) { + t.Errorf("expected %v, got %v", e, a) + } + }) + } +} + +func setQueryValue(qv QueryValue, args []interface{}) error { + value := args[0] + + switch value.(type) { + case []byte: + return reflectCall(reflect.ValueOf(qv.Blob), args) + case bool: + return reflectCall(reflect.ValueOf(qv.Boolean), args) + case string: + return reflectCall(reflect.ValueOf(qv.String), args) + case int8: + return reflectCall(reflect.ValueOf(qv.Byte), args) + case int16: + return reflectCall(reflect.ValueOf(qv.Short), args) + case int32: + return reflectCall(reflect.ValueOf(qv.Integer), args) + case int64: + return reflectCall(reflect.ValueOf(qv.Long), args) + case float32: + return reflectCall(reflect.ValueOf(qv.Float), args) + case float64: + return reflectCall(reflect.ValueOf(qv.Double), args) + case *big.Int: + return reflectCall(reflect.ValueOf(qv.BigInteger), args) + case *big.Float: + return reflectCall(reflect.ValueOf(qv.BigDecimal), args) + default: + return fmt.Errorf("unhandled query value type") + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/shared_test.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/shared_test.go new file mode 100644 index 0000000000..2b7518e1c7 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/shared_test.go @@ -0,0 +1,36 @@ +package httpbinding + +import ( + "fmt" + "reflect" +) + +func reflectCall(funcValue reflect.Value, args []interface{}) error { + argValues := make([]reflect.Value, len(args)) + + for i, v := range args { + value := reflect.ValueOf(v) + argValues[i] = value + } + + retValues := funcValue.Call(argValues) + if len(retValues) > 0 { + errValue := retValues[0] + + if typeName := errValue.Type().Name(); typeName != "error" { + panic(fmt.Sprintf("expected first return argument to be error but got %v", typeName)) + } + + if errValue.IsNil() { + return nil + } + + if err, ok := errValue.Interface().(error); ok { + return err + } + + panic(fmt.Sprintf("expected %v to return error type, but got %v", funcValue.Type().String(), retValues[0].Type().String())) + } + + return nil +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/uri.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/uri.go new file mode 100644 index 0000000000..f04e11984a --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/uri.go @@ -0,0 +1,111 @@ +package httpbinding + +import ( + "math" + "math/big" + "strconv" + "strings" +) + +// URIValue is used to encode named URI parameters +type URIValue struct { + path, rawPath, buffer *[]byte + + key string +} + +func newURIValue(path *[]byte, rawPath *[]byte, buffer *[]byte, key string) URIValue { + return URIValue{path: path, rawPath: rawPath, buffer: buffer, key: key} +} + +func (u URIValue) modifyURI(value string) (err error) { + *u.path, *u.buffer, err = replacePathElement(*u.path, *u.buffer, u.key, value, false) + if err != nil { + return err + } + *u.rawPath, *u.buffer, err = replacePathElement(*u.rawPath, *u.buffer, u.key, value, true) + return err +} + +// Boolean encodes v as a URI string value +func (u URIValue) Boolean(v bool) error { + return u.modifyURI(strconv.FormatBool(v)) +} + +// String encodes v as a URI string value +func (u URIValue) String(v string) error { + return u.modifyURI(v) +} + +// Byte encodes v as a URI string value +func (u URIValue) Byte(v int8) error { + return u.Long(int64(v)) +} + +// Short encodes v as a URI string value +func (u URIValue) Short(v int16) error { + return u.Long(int64(v)) +} + +// Integer encodes v as a URI string value +func (u URIValue) Integer(v int32) error { + return u.Long(int64(v)) +} + +// Long encodes v as a URI string value +func (u URIValue) Long(v int64) error { + return u.modifyURI(strconv.FormatInt(v, 10)) +} + +// Float encodes v as a query string value +func (u URIValue) Float(v float32) error { + return u.float(float64(v), 32) +} + +// Double encodes v as a query string value +func (u URIValue) Double(v float64) error { + return u.float(v, 64) +} + +func (u URIValue) float(v float64, bitSize int) error { + switch { + case math.IsNaN(v): + return u.String(floatNaN) + case math.IsInf(v, 1): + return u.String(floatInfinity) + case math.IsInf(v, -1): + return u.String(floatNegInfinity) + default: + return u.modifyURI(strconv.FormatFloat(v, 'f', -1, bitSize)) + } +} + +// BigInteger encodes v as a query string value +func (u URIValue) BigInteger(v *big.Int) error { + return u.modifyURI(v.String()) +} + +// BigDecimal encodes v as a query string value +func (u URIValue) BigDecimal(v *big.Float) error { + if i, accuracy := v.Int64(); accuracy == big.Exact { + return u.Long(i) + } + return u.modifyURI(v.Text('e', -1)) +} + +// SplitURI parses a Smithy HTTP binding trait URI +func SplitURI(uri string) (path, query string) { + queryStart := strings.IndexRune(uri, '?') + if queryStart == -1 { + path = uri + return path, query + } + + path = uri[:queryStart] + if queryStart+1 >= len(uri) { + return path, query + } + query = uri[queryStart+1:] + + return path, query +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/uri_test.go b/vendor/github.com/aws/smithy-go/encoding/httpbinding/uri_test.go new file mode 100644 index 0000000000..039e1e5baa --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/uri_test.go @@ -0,0 +1,198 @@ +package httpbinding + +import ( + "fmt" + "math/big" + "reflect" + "strconv" + "testing" +) + +func TestURIValue(t *testing.T) { + const uriKey = "someKey" + const path = "/some/{someKey}/{path+}" + + type expected struct { + path string + raw string + } + + cases := map[string]struct { + path string + args []interface{} + expected expected + }{ + "bool": { + path: path, + args: []interface{}{true}, + expected: expected{ + path: "/some/true/{path+}", + raw: "/some/true/{path+}", + }, + }, + "string": { + path: path, + args: []interface{}{"someValue"}, + expected: expected{ + path: "/some/someValue/{path+}", + raw: "/some/someValue/{path+}", + }, + }, + "byte": { + path: path, + args: []interface{}{int8(127)}, + expected: expected{ + path: "/some/127/{path+}", + raw: "/some/127/{path+}", + }, + }, + "short": { + path: path, + args: []interface{}{int16(32767)}, + expected: expected{ + path: "/some/32767/{path+}", + raw: "/some/32767/{path+}", + }, + }, + "integer": { + path: path, + args: []interface{}{int32(2147483647)}, + expected: expected{ + path: "/some/2147483647/{path+}", + raw: "/some/2147483647/{path+}", + }, + }, + "long": { + path: path, + args: []interface{}{int64(9223372036854775807)}, + expected: expected{ + path: "/some/9223372036854775807/{path+}", + raw: "/some/9223372036854775807/{path+}", + }, + }, + "float32": { + path: path, + args: []interface{}{float32(3.14159)}, + expected: expected{ + path: "/some/3.14159/{path+}", + raw: "/some/3.14159/{path+}", + }, + }, + "float64": { + path: path, + args: []interface{}{float64(3.14159)}, + expected: expected{ + path: "/some/3.14159/{path+}", + raw: "/some/3.14159/{path+}", + }, + }, + "bigInteger": { + path: path, + args: []interface{}{new(big.Int).SetInt64(1)}, + expected: expected{ + path: "/some/1/{path+}", + raw: "/some/1/{path+}", + }, + }, + "bigDecimal": { + path: path, + args: []interface{}{new(big.Float).SetFloat64(1024.10241024)}, + expected: expected{ + path: "/some/1.02410241024e+03/{path+}", + raw: "/some/1.02410241024e%2B03/{path+}", + }, + }, + } + + buffer := make([]byte, 1024) + + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + pBytes, rBytes := []byte(tt.path), []byte(tt.path) + + uv := newURIValue(&pBytes, &rBytes, &buffer, uriKey) + + if err := setURI(uv, tt.args); err != nil { + t.Fatalf("expected no error, %v", err) + } + + if e, a := tt.expected.path, string(pBytes); e != a { + t.Errorf("expected %v, got %v", e, a) + } + + if e, a := tt.expected.raw, string(rBytes); e != a { + t.Errorf("expected %v, got %v", e, a) + } + }) + } +} + +func setURI(uv URIValue, args []interface{}) error { + value := args[0] + + switch value.(type) { + case bool: + return reflectCall(reflect.ValueOf(uv.Boolean), args) + case string: + return reflectCall(reflect.ValueOf(uv.String), args) + case int8: + return reflectCall(reflect.ValueOf(uv.Byte), args) + case int16: + return reflectCall(reflect.ValueOf(uv.Short), args) + case int32: + return reflectCall(reflect.ValueOf(uv.Integer), args) + case int64: + return reflectCall(reflect.ValueOf(uv.Long), args) + case float32: + return reflectCall(reflect.ValueOf(uv.Float), args) + case float64: + return reflectCall(reflect.ValueOf(uv.Double), args) + case *big.Int: + return reflectCall(reflect.ValueOf(uv.BigInteger), args) + case *big.Float: + return reflectCall(reflect.ValueOf(uv.BigDecimal), args) + default: + return fmt.Errorf("unhandled value type") + } +} + +func TestParseURI(t *testing.T) { + cases := []struct { + Value string + Path string + Query string + }{ + { + Value: "/my/uri/foo/bar/baz", + Path: "/my/uri/foo/bar/baz", + Query: "", + }, + { + Value: "/path?requiredKey", + Path: "/path", + Query: "requiredKey", + }, + { + Value: "/path?", + Path: "/path", + Query: "", + }, + { + Value: "?", + Path: "", + Query: "", + }, + } + + for i, tt := range cases { + t.Run(strconv.Itoa(i), func(t *testing.T) { + path, query := SplitURI(tt.Value) + if e, a := tt.Path, path; e != a { + t.Errorf("expected %v, got %v", e, a) + } + if e, a := tt.Query, query; e != a { + t.Errorf("expected %v, got %v", e, a) + } + }) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/httpbinding/ya.make b/vendor/github.com/aws/smithy-go/encoding/httpbinding/ya.make new file mode 100644 index 0000000000..f1f0698636 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/httpbinding/ya.make @@ -0,0 +1,26 @@ +GO_LIBRARY() + +LICENSE(Apache-2.0) + +SRCS( + encode.go + header.go + path_replace.go + query.go + uri.go +) + +GO_TEST_SRCS( + encode_test.go + header_test.go + path_replace_test.go + query_test.go + shared_test.go + uri_test.go +) + +END() + +RECURSE( + gotest +) diff --git a/vendor/github.com/aws/smithy-go/encoding/json/array.go b/vendor/github.com/aws/smithy-go/encoding/json/array.go new file mode 100644 index 0000000000..7a232f660f --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/array.go @@ -0,0 +1,35 @@ +package json + +import ( + "bytes" +) + +// Array represents the encoding of a JSON Array +type Array struct { + w *bytes.Buffer + writeComma bool + scratch *[]byte +} + +func newArray(w *bytes.Buffer, scratch *[]byte) *Array { + w.WriteRune(leftBracket) + return &Array{w: w, scratch: scratch} +} + +// Value adds a new element to the JSON Array. +// Returns a Value type that is used to encode +// the array element. +func (a *Array) Value() Value { + if a.writeComma { + a.w.WriteRune(comma) + } else { + a.writeComma = true + } + + return newValue(a.w, a.scratch) +} + +// Close encodes the end of the JSON Array +func (a *Array) Close() { + a.w.WriteRune(rightBracket) +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/array_test.go b/vendor/github.com/aws/smithy-go/encoding/json/array_test.go new file mode 100644 index 0000000000..072a424129 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/array_test.go @@ -0,0 +1,21 @@ +package json + +import ( + "bytes" + "testing" +) + +func TestArray(t *testing.T) { + buffer := bytes.NewBuffer(nil) + scratch := make([]byte, 64) + + array := newArray(buffer, &scratch) + array.Value().String("bar") + array.Value().String("baz") + array.Close() + + e := []byte(`["bar","baz"]`) + if a := buffer.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/constants.go b/vendor/github.com/aws/smithy-go/encoding/json/constants.go new file mode 100644 index 0000000000..91044092ae --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/constants.go @@ -0,0 +1,15 @@ +package json + +const ( + leftBrace = '{' + rightBrace = '}' + + leftBracket = '[' + rightBracket = ']' + + comma = ',' + quote = '"' + colon = ':' + + null = "null" +) diff --git a/vendor/github.com/aws/smithy-go/encoding/json/decoder_util.go b/vendor/github.com/aws/smithy-go/encoding/json/decoder_util.go new file mode 100644 index 0000000000..7050c85b3c --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/decoder_util.go @@ -0,0 +1,139 @@ +package json + +import ( + "bytes" + "encoding/json" + "fmt" + "io" +) + +// DiscardUnknownField discards unknown fields from a decoder body. +// This function is useful while deserializing a JSON body with additional +// unknown information that should be discarded. +func DiscardUnknownField(decoder *json.Decoder) error { + // This deliberately does not share logic with CollectUnknownField, even + // though it could, because if we were to delegate to that then we'd incur + // extra allocations and general memory usage. + v, err := decoder.Token() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + + if _, ok := v.(json.Delim); ok { + for decoder.More() { + err = DiscardUnknownField(decoder) + } + endToken, err := decoder.Token() + if err != nil { + return err + } + if _, ok := endToken.(json.Delim); !ok { + return fmt.Errorf("invalid JSON : expected json delimiter, found %T %v", + endToken, endToken) + } + } + + return nil +} + +// CollectUnknownField grabs the contents of unknown fields from the decoder body +// and returns them as a byte slice. This is useful for skipping unknown fields without +// completely discarding them. +func CollectUnknownField(decoder *json.Decoder) ([]byte, error) { + result, err := collectUnknownField(decoder) + if err != nil { + return nil, err + } + + buff := bytes.NewBuffer(nil) + encoder := json.NewEncoder(buff) + + if err := encoder.Encode(result); err != nil { + return nil, err + } + + return buff.Bytes(), nil +} + +func collectUnknownField(decoder *json.Decoder) (interface{}, error) { + // Grab the initial value. This could either be a concrete value like a string or a a + // delimiter. + token, err := decoder.Token() + if err == io.EOF { + return nil, nil + } + if err != nil { + return nil, err + } + + // If it's an array or object, we'll need to recurse. + delim, ok := token.(json.Delim) + if ok { + var result interface{} + if delim == '{' { + result, err = collectUnknownObject(decoder) + if err != nil { + return nil, err + } + } else { + result, err = collectUnknownArray(decoder) + if err != nil { + return nil, err + } + } + + // Discard the closing token. decoder.Token handles checking for matching delimiters + if _, err := decoder.Token(); err != nil { + return nil, err + } + return result, nil + } + + return token, nil +} + +func collectUnknownArray(decoder *json.Decoder) ([]interface{}, error) { + // We need to create an empty array here instead of a nil array, since by getting + // into this function at all we necessarily have seen a non-nil list. + array := []interface{}{} + + for decoder.More() { + value, err := collectUnknownField(decoder) + if err != nil { + return nil, err + } + array = append(array, value) + } + + return array, nil +} + +func collectUnknownObject(decoder *json.Decoder) (map[string]interface{}, error) { + object := make(map[string]interface{}) + + for decoder.More() { + key, err := collectUnknownField(decoder) + if err != nil { + return nil, err + } + + // Keys have to be strings, which is particularly important as the encoder + // won't except a map with interface{} keys + stringKey, ok := key.(string) + if !ok { + return nil, fmt.Errorf("expected string key, found %T", key) + } + + value, err := collectUnknownField(decoder) + if err != nil { + return nil, err + } + + object[stringKey] = value + } + + return object, nil +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/decoder_util_test.go b/vendor/github.com/aws/smithy-go/encoding/json/decoder_util_test.go new file mode 100644 index 0000000000..e4cb38e8b7 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/decoder_util_test.go @@ -0,0 +1,65 @@ +package json + +import ( + "bytes" + "encoding/json" + "testing" + + smithytesting "github.com/aws/smithy-go/testing" +) + +func TestDiscardUnknownField(t *testing.T) { + cases := map[string][]byte{ + "empty object": []byte(`{}`), + "simple object": []byte(`{"foo": "bar"}`), + "nested object": []byte(`{"foo": {"bar": "baz"}}`), + "empty list": []byte(`[]`), + "simple list": []byte(`["foo", "bar", "baz"]`), + "nested list": []byte(`["foo", ["bar", ["baz"]]]`), + "number": []byte(`1`), + "boolean": []byte(`true`), + "null": []byte(`null`), + "string": []byte(`"foo"`), + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + buff := bytes.NewBuffer(c) + decoder := json.NewDecoder(buff) + err := DiscardUnknownField(decoder) + if err != nil { + t.Fatalf("failed to discard, %v", err) + } + if decoder.More() { + t.Fatalf("failed to discard entire contents") + } + }) + } +} + +func TestCollectUnknownField(t *testing.T) { + cases := map[string][]byte{ + "empty object": []byte(`{}`), + "simple object": []byte(`{"foo": "bar"}`), + "nested object": []byte(`{"foo": {"bar": "baz"}}`), + "empty list": []byte(`[]`), + "simple list": []byte(`["foo", "bar", "baz"]`), + "nested list": []byte(`["foo", ["bar", ["baz"]]]`), + "number": []byte(`1`), + "boolean": []byte(`true`), + "null": []byte(`null`), + "string": []byte(`"foo"`), + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + buff := bytes.NewBuffer(c) + decoder := json.NewDecoder(buff) + actual, err := CollectUnknownField(decoder) + if err != nil { + t.Fatalf("failed to collect, %v", err) + } + smithytesting.AssertJSONEqual(t, c, actual) + }) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/encoder.go b/vendor/github.com/aws/smithy-go/encoding/json/encoder.go new file mode 100644 index 0000000000..8772953f1e --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/encoder.go @@ -0,0 +1,30 @@ +package json + +import ( + "bytes" +) + +// Encoder is JSON encoder that supports construction of JSON values +// using methods. +type Encoder struct { + w *bytes.Buffer + Value +} + +// NewEncoder returns a new JSON encoder +func NewEncoder() *Encoder { + writer := bytes.NewBuffer(nil) + scratch := make([]byte, 64) + + return &Encoder{w: writer, Value: newValue(writer, &scratch)} +} + +// String returns the String output of the JSON encoder +func (e Encoder) String() string { + return e.w.String() +} + +// Bytes returns the []byte slice of the JSON encoder +func (e Encoder) Bytes() []byte { + return e.w.Bytes() +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/encoder_test.go b/vendor/github.com/aws/smithy-go/encoding/json/encoder_test.go new file mode 100644 index 0000000000..d9fbd4ba82 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/encoder_test.go @@ -0,0 +1,34 @@ +package json_test + +import ( + "bytes" + "testing" + + "github.com/aws/smithy-go/encoding/json" +) + +func TestEncoder(t *testing.T) { + encoder := json.NewEncoder() + + object := encoder.Object() + + object.Key("stringKey").String("stringValue") + object.Key("integerKey").Long(1024) + object.Key("floatKey").Double(3.14) + + subObj := object.Key("foo").Object() + + subObj.Key("byteSlice").Base64EncodeBytes([]byte("foo bar")) + subObj.Close() + + object.Close() + + e := []byte(`{"stringKey":"stringValue","integerKey":1024,"floatKey":3.14,"foo":{"byteSlice":"Zm9vIGJhcg=="}}`) + if a := encoder.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } + + if a := encoder.String(); string(e) != a { + t.Errorf("expected %s, but got %s", e, a) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/escape.go b/vendor/github.com/aws/smithy-go/encoding/json/escape.go new file mode 100644 index 0000000000..d984d0cdca --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/escape.go @@ -0,0 +1,198 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copied and modified from Go 1.8 stdlib's encoding/json/#safeSet + +package json + +import ( + "bytes" + "unicode/utf8" +) + +// safeSet holds the value true if the ASCII character with the given array +// position can be represented inside a JSON string without any further +// escaping. +// +// All values are true except for the ASCII control characters (0-31), the +// double quote ("), and the backslash character ("\"). +var safeSet = [utf8.RuneSelf]bool{ + ' ': true, + '!': true, + '"': false, + '#': true, + '$': true, + '%': true, + '&': true, + '\'': true, + '(': true, + ')': true, + '*': true, + '+': true, + ',': true, + '-': true, + '.': true, + '/': true, + '0': true, + '1': true, + '2': true, + '3': true, + '4': true, + '5': true, + '6': true, + '7': true, + '8': true, + '9': true, + ':': true, + ';': true, + '<': true, + '=': true, + '>': true, + '?': true, + '@': true, + 'A': true, + 'B': true, + 'C': true, + 'D': true, + 'E': true, + 'F': true, + 'G': true, + 'H': true, + 'I': true, + 'J': true, + 'K': true, + 'L': true, + 'M': true, + 'N': true, + 'O': true, + 'P': true, + 'Q': true, + 'R': true, + 'S': true, + 'T': true, + 'U': true, + 'V': true, + 'W': true, + 'X': true, + 'Y': true, + 'Z': true, + '[': true, + '\\': false, + ']': true, + '^': true, + '_': true, + '`': true, + 'a': true, + 'b': true, + 'c': true, + 'd': true, + 'e': true, + 'f': true, + 'g': true, + 'h': true, + 'i': true, + 'j': true, + 'k': true, + 'l': true, + 'm': true, + 'n': true, + 'o': true, + 'p': true, + 'q': true, + 'r': true, + 's': true, + 't': true, + 'u': true, + 'v': true, + 'w': true, + 'x': true, + 'y': true, + 'z': true, + '{': true, + '|': true, + '}': true, + '~': true, + '\u007f': true, +} + +// copied from Go 1.8 stdlib's encoding/json/#hex +var hex = "0123456789abcdef" + +// escapeStringBytes escapes and writes the passed in string bytes to the dst +// buffer +// +// Copied and modifed from Go 1.8 stdlib's encodeing/json/#encodeState.stringBytes +func escapeStringBytes(e *bytes.Buffer, s []byte) { + e.WriteByte('"') + start := 0 + for i := 0; i < len(s); { + if b := s[i]; b < utf8.RuneSelf { + if safeSet[b] { + i++ + continue + } + if start < i { + e.Write(s[start:i]) + } + switch b { + case '\\', '"': + e.WriteByte('\\') + e.WriteByte(b) + case '\n': + e.WriteByte('\\') + e.WriteByte('n') + case '\r': + e.WriteByte('\\') + e.WriteByte('r') + case '\t': + e.WriteByte('\\') + e.WriteByte('t') + default: + // This encodes bytes < 0x20 except for \t, \n and \r. + // If escapeHTML is set, it also escapes <, >, and & + // because they can lead to security holes when + // user-controlled strings are rendered into JSON + // and served to some browsers. + e.WriteString(`\u00`) + e.WriteByte(hex[b>>4]) + e.WriteByte(hex[b&0xF]) + } + i++ + start = i + continue + } + c, size := utf8.DecodeRune(s[i:]) + if c == utf8.RuneError && size == 1 { + if start < i { + e.Write(s[start:i]) + } + e.WriteString(`\ufffd`) + i += size + start = i + continue + } + // U+2028 is LINE SEPARATOR. + // U+2029 is PARAGRAPH SEPARATOR. + // They are both technically valid characters in JSON strings, + // but don't work in JSONP, which has to be evaluated as JavaScript, + // and can lead to security holes there. It is valid JSON to + // escape them, so we do so unconditionally. + // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. + if c == '\u2028' || c == '\u2029' { + if start < i { + e.Write(s[start:i]) + } + e.WriteString(`\u202`) + e.WriteByte(hex[c&0xF]) + i += size + start = i + continue + } + i += size + } + if start < len(s) { + e.Write(s[start:]) + } + e.WriteByte('"') +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/escape_test.go b/vendor/github.com/aws/smithy-go/encoding/json/escape_test.go new file mode 100644 index 0000000000..c3a07a1260 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/escape_test.go @@ -0,0 +1,49 @@ +package json + +import ( + "bytes" + "testing" +) + +func TestEscapeStringBytes(t *testing.T) { + cases := map[string]struct { + expected string + input []byte + }{ + "safeSet only": { + expected: `"mountainPotato"`, + input: []byte("mountainPotato"), + }, + "parenthesis": { + expected: `"foo\""`, + input: []byte(`foo"`), + }, + "double escape": { + expected: `"hello\\\\world"`, + input: []byte(`hello\\world`), + }, + "new line": { + expected: `"foo\nbar"`, + input: []byte("foo\nbar"), + }, + "carriage return": { + expected: `"foo\rbar"`, + input: []byte("foo\rbar"), + }, + "tab": { + expected: `"foo\tbar"`, + input: []byte("foo\tbar"), + }, + } + for name, c := range cases { + t.Run(name, func(t *testing.T) { + var buffer bytes.Buffer + escapeStringBytes(&buffer, c.input) + expected := c.expected + actual := buffer.String() + if expected != actual { + t.Errorf("\nexpected %v \nactual %v", expected, actual) + } + }) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/gotest/ya.make b/vendor/github.com/aws/smithy-go/encoding/json/gotest/ya.make new file mode 100644 index 0000000000..8ae1b85797 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/gotest/ya.make @@ -0,0 +1,5 @@ +GO_TEST_FOR(vendor/github.com/aws/smithy-go/encoding/json) + +LICENSE(Apache-2.0) + +END() diff --git a/vendor/github.com/aws/smithy-go/encoding/json/object.go b/vendor/github.com/aws/smithy-go/encoding/json/object.go new file mode 100644 index 0000000000..722346d035 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/object.go @@ -0,0 +1,40 @@ +package json + +import ( + "bytes" +) + +// Object represents the encoding of a JSON Object type +type Object struct { + w *bytes.Buffer + writeComma bool + scratch *[]byte +} + +func newObject(w *bytes.Buffer, scratch *[]byte) *Object { + w.WriteRune(leftBrace) + return &Object{w: w, scratch: scratch} +} + +func (o *Object) writeKey(key string) { + escapeStringBytes(o.w, []byte(key)) + o.w.WriteRune(colon) +} + +// Key adds the given named key to the JSON object. +// Returns a Value encoder that should be used to encode +// a JSON value type. +func (o *Object) Key(name string) Value { + if o.writeComma { + o.w.WriteRune(comma) + } else { + o.writeComma = true + } + o.writeKey(name) + return newValue(o.w, o.scratch) +} + +// Close encodes the end of the JSON Object +func (o *Object) Close() { + o.w.WriteRune(rightBrace) +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/object_test.go b/vendor/github.com/aws/smithy-go/encoding/json/object_test.go new file mode 100644 index 0000000000..1e0830e052 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/object_test.go @@ -0,0 +1,34 @@ +package json + +import ( + "bytes" + "testing" +) + +func TestObject(t *testing.T) { + buffer := bytes.NewBuffer(nil) + scratch := make([]byte, 64) + + object := newObject(buffer, &scratch) + object.Key("foo").String("bar") + object.Key("faz").String("baz") + object.Close() + + e := []byte(`{"foo":"bar","faz":"baz"}`) + if a := buffer.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } +} + +func TestObjectKey_escaped(t *testing.T) { + jsonEncoder := NewEncoder() + object := jsonEncoder.Object() + object.Key("foo\"").String("bar") + object.Key("faz").String("baz") + object.Close() + + e := []byte(`{"foo\"":"bar","faz":"baz"}`) + if a := object.w.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/value.go b/vendor/github.com/aws/smithy-go/encoding/json/value.go new file mode 100644 index 0000000000..b41ff1e15c --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/value.go @@ -0,0 +1,149 @@ +package json + +import ( + "bytes" + "encoding/base64" + "math/big" + "strconv" + + "github.com/aws/smithy-go/encoding" +) + +// Value represents a JSON Value type +// JSON Value types: Object, Array, String, Number, Boolean, and Null +type Value struct { + w *bytes.Buffer + scratch *[]byte +} + +// newValue returns a new Value encoder +func newValue(w *bytes.Buffer, scratch *[]byte) Value { + return Value{w: w, scratch: scratch} +} + +// String encodes v as a JSON string +func (jv Value) String(v string) { + escapeStringBytes(jv.w, []byte(v)) +} + +// Byte encodes v as a JSON number +func (jv Value) Byte(v int8) { + jv.Long(int64(v)) +} + +// Short encodes v as a JSON number +func (jv Value) Short(v int16) { + jv.Long(int64(v)) +} + +// Integer encodes v as a JSON number +func (jv Value) Integer(v int32) { + jv.Long(int64(v)) +} + +// Long encodes v as a JSON number +func (jv Value) Long(v int64) { + *jv.scratch = strconv.AppendInt((*jv.scratch)[:0], v, 10) + jv.w.Write(*jv.scratch) +} + +// ULong encodes v as a JSON number +func (jv Value) ULong(v uint64) { + *jv.scratch = strconv.AppendUint((*jv.scratch)[:0], v, 10) + jv.w.Write(*jv.scratch) +} + +// Float encodes v as a JSON number +func (jv Value) Float(v float32) { + jv.float(float64(v), 32) +} + +// Double encodes v as a JSON number +func (jv Value) Double(v float64) { + jv.float(v, 64) +} + +func (jv Value) float(v float64, bits int) { + *jv.scratch = encoding.EncodeFloat((*jv.scratch)[:0], v, bits) + jv.w.Write(*jv.scratch) +} + +// Boolean encodes v as a JSON boolean +func (jv Value) Boolean(v bool) { + *jv.scratch = strconv.AppendBool((*jv.scratch)[:0], v) + jv.w.Write(*jv.scratch) +} + +// Base64EncodeBytes writes v as a base64 value in JSON string +func (jv Value) Base64EncodeBytes(v []byte) { + encodeByteSlice(jv.w, (*jv.scratch)[:0], v) +} + +// Write writes v directly to the JSON document +func (jv Value) Write(v []byte) { + jv.w.Write(v) +} + +// Array returns a new Array encoder +func (jv Value) Array() *Array { + return newArray(jv.w, jv.scratch) +} + +// Object returns a new Object encoder +func (jv Value) Object() *Object { + return newObject(jv.w, jv.scratch) +} + +// Null encodes a null JSON value +func (jv Value) Null() { + jv.w.WriteString(null) +} + +// BigInteger encodes v as JSON value +func (jv Value) BigInteger(v *big.Int) { + jv.w.Write([]byte(v.Text(10))) +} + +// BigDecimal encodes v as JSON value +func (jv Value) BigDecimal(v *big.Float) { + if i, accuracy := v.Int64(); accuracy == big.Exact { + jv.Long(i) + return + } + // TODO: Should this try to match ES6 ToString similar to stdlib JSON? + jv.w.Write([]byte(v.Text('e', -1))) +} + +// Based on encoding/json encodeByteSlice from the Go Standard Library +// https://golang.org/src/encoding/json/encode.go +func encodeByteSlice(w *bytes.Buffer, scratch []byte, v []byte) { + if v == nil { + w.WriteString(null) + return + } + + w.WriteRune(quote) + + encodedLen := base64.StdEncoding.EncodedLen(len(v)) + if encodedLen <= len(scratch) { + // If the encoded bytes fit in e.scratch, avoid an extra + // allocation and use the cheaper Encoding.Encode. + dst := scratch[:encodedLen] + base64.StdEncoding.Encode(dst, v) + w.Write(dst) + } else if encodedLen <= 1024 { + // The encoded bytes are short enough to allocate for, and + // Encoding.Encode is still cheaper. + dst := make([]byte, encodedLen) + base64.StdEncoding.Encode(dst, v) + w.Write(dst) + } else { + // The encoded bytes are too long to cheaply allocate, and + // Encoding.Encode is no longer noticeably cheaper. + enc := base64.NewEncoder(base64.StdEncoding, w) + enc.Write(v) + enc.Close() + } + + w.WriteRune(quote) +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/value_test.go b/vendor/github.com/aws/smithy-go/encoding/json/value_test.go new file mode 100644 index 0000000000..29f0cc562c --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/value_test.go @@ -0,0 +1,156 @@ +package json + +import ( + "bytes" + "math" + "math/big" + "strconv" + "testing" +) + +var ( + oneInt = new(big.Int).SetInt64(1) + oneFloat = new(big.Float).SetFloat64(1.0) +) + +func TestValue(t *testing.T) { + cases := map[string]struct { + setter func(Value) + expected string + }{ + "string value": { + setter: func(value Value) { + value.String("foo") + }, + expected: `"foo"`, + }, + "string escaped": { + setter: func(value Value) { + value.String(`{"foo":"bar"}`) + }, + expected: `"{\"foo\":\"bar\"}"`, + }, + "integer": { + setter: func(value Value) { + value.Long(1024) + }, + expected: `1024`, + }, + "float": { + setter: func(value Value) { + value.Double(1e20) + }, + expected: `100000000000000000000`, + }, + "float exponent component": { + setter: func(value Value) { + value.Double(3e22) + }, + expected: `3e+22`, + }, + "boolean true": { + setter: func(value Value) { + value.Boolean(true) + }, + expected: `true`, + }, + "boolean false": { + setter: func(value Value) { + value.Boolean(false) + }, + expected: `false`, + }, + "encode bytes": { + setter: func(value Value) { + value.Base64EncodeBytes([]byte("foo bar")) + }, + expected: `"Zm9vIGJhcg=="`, + }, + "encode bytes nil": { + setter: func(value Value) { + value.Base64EncodeBytes(nil) + }, + expected: `null`, + }, + "object": { + setter: func(value Value) { + o := value.Object() + defer o.Close() + o.Key("key").String("value") + }, + expected: `{"key":"value"}`, + }, + "array": { + setter: func(value Value) { + o := value.Array() + defer o.Close() + o.Value().String("value1") + o.Value().String("value2") + }, + expected: `["value1","value2"]`, + }, + "null": { + setter: func(value Value) { + value.Null() + }, + expected: `null`, + }, + "write bytes": { + setter: func(value Value) { + o := value.Object() + o.Key("inline").Write([]byte(`{"nested":"value"}`)) + defer o.Close() + }, + expected: `{"inline":{"nested":"value"}}`, + }, + "bigInteger": { + setter: func(value Value) { + v := new(big.Int).SetInt64(math.MaxInt64) + value.BigInteger(v.Sub(v, oneInt)) + }, + expected: strconv.FormatInt(math.MaxInt64-1, 10), + }, + "bigInteger > int64": { + setter: func(value Value) { + v := new(big.Int).SetInt64(math.MaxInt64) + value.BigInteger(v.Add(v, oneInt)) + }, + expected: "9223372036854775808", + }, + "bigInteger < int64": { + setter: func(value Value) { + v := new(big.Int).SetInt64(math.MinInt64) + value.BigInteger(v.Sub(v, oneInt)) + }, + expected: "-9223372036854775809", + }, + "bigFloat": { + setter: func(value Value) { + v := new(big.Float).SetFloat64(math.MaxFloat64) + value.BigDecimal(v.Sub(v, oneFloat)) + }, + expected: strconv.FormatFloat(math.MaxFloat64-1, 'e', -1, 64), + }, + "bigFloat fits in int64": { + setter: func(value Value) { + v := new(big.Float).SetInt64(math.MaxInt64) + value.BigDecimal(v) + }, + expected: "9223372036854775807", + }, + } + scratch := make([]byte, 64) + + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + var b bytes.Buffer + value := newValue(&b, &scratch) + + tt.setter(value) + + if e, a := []byte(tt.expected), b.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } + }) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/json/ya.make b/vendor/github.com/aws/smithy-go/encoding/json/ya.make new file mode 100644 index 0000000000..f43773b0d8 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/json/ya.make @@ -0,0 +1,29 @@ +GO_LIBRARY() + +LICENSE(Apache-2.0) + +SRCS( + array.go + constants.go + decoder_util.go + encoder.go + escape.go + object.go + value.go +) + +GO_TEST_SRCS( + array_test.go + decoder_util_test.go + escape_test.go + object_test.go + value_test.go +) + +GO_XTEST_SRCS(encoder_test.go) + +END() + +RECURSE( + gotest +) diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/array.go b/vendor/github.com/aws/smithy-go/encoding/xml/array.go new file mode 100644 index 0000000000..508f3c997e --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/array.go @@ -0,0 +1,49 @@ +package xml + +// arrayMemberWrapper is the default member wrapper tag name for XML Array type +var arrayMemberWrapper = StartElement{ + Name: Name{Local: "member"}, +} + +// Array represents the encoding of a XML array type +type Array struct { + w writer + scratch *[]byte + + // member start element is the array member wrapper start element + memberStartElement StartElement + + // isFlattened indicates if the array is a flattened array. + isFlattened bool +} + +// newArray returns an array encoder. +// It also takes in the member start element, array start element. +// It takes in a isFlattened bool, indicating that an array is flattened array. +// +// A wrapped array ["value1", "value2"] is represented as +// `<List><member>value1</member><member>value2</member></List>`. + +// A flattened array `someList: ["value1", "value2"]` is represented as +// `<someList>value1</someList><someList>value2</someList>`. +func newArray(w writer, scratch *[]byte, memberStartElement StartElement, arrayStartElement StartElement, isFlattened bool) *Array { + var memberWrapper = memberStartElement + if isFlattened { + memberWrapper = arrayStartElement + } + + return &Array{ + w: w, + scratch: scratch, + memberStartElement: memberWrapper, + isFlattened: isFlattened, + } +} + +// Member adds a new member to the XML array. +// It returns a Value encoder. +func (a *Array) Member() Value { + v := newValue(a.w, a.scratch, a.memberStartElement) + v.isFlattened = a.isFlattened + return v +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/array_test.go b/vendor/github.com/aws/smithy-go/encoding/xml/array_test.go new file mode 100644 index 0000000000..b0fbbde561 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/array_test.go @@ -0,0 +1,52 @@ +package xml + +import ( + "bytes" + "testing" +) + +func TestWrappedArray(t *testing.T) { + buffer := bytes.NewBuffer(nil) + scratch := make([]byte, 64) + + root := StartElement{Name: Name{Local: "array"}} + a := newArray(buffer, &scratch, arrayMemberWrapper, root, false) + a.Member().String("bar") + a.Member().String("baz") + + e := []byte(`<member>bar</member><member>baz</member>`) + if a := buffer.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } +} + +func TestWrappedArrayWithCustomName(t *testing.T) { + buffer := bytes.NewBuffer(nil) + scratch := make([]byte, 64) + + root := StartElement{Name: Name{Local: "array"}} + item := StartElement{Name: Name{Local: "item"}} + a := newArray(buffer, &scratch, item, root, false) + a.Member().String("bar") + a.Member().String("baz") + + e := []byte(`<item>bar</item><item>baz</item>`) + if a := buffer.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } +} + +func TestFlattenedArray(t *testing.T) { + buffer := bytes.NewBuffer(nil) + scratch := make([]byte, 64) + + root := StartElement{Name: Name{Local: "array"}} + a := newArray(buffer, &scratch, arrayMemberWrapper, root, true) + a.Member().String("bar") + a.Member().String("bix") + + e := []byte(`<array>bar</array><array>bix</array>`) + if a := buffer.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/constants.go b/vendor/github.com/aws/smithy-go/encoding/xml/constants.go new file mode 100644 index 0000000000..ccee90a636 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/constants.go @@ -0,0 +1,10 @@ +package xml + +const ( + leftAngleBracket = '<' + rightAngleBracket = '>' + forwardSlash = '/' + colon = ':' + equals = '=' + quote = '"' +) diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/doc.go b/vendor/github.com/aws/smithy-go/encoding/xml/doc.go new file mode 100644 index 0000000000..f9200093e8 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/doc.go @@ -0,0 +1,49 @@ +/* +Package xml holds the XMl encoder utility. This utility is written in accordance to our design to delegate to +shape serializer function in which a xml.Value will be passed around. + +Resources followed: https://smithy.io/2.0/spec/protocol-traits.html#xml-bindings + +Member Element + +Member element should be used to encode xml shapes into xml elements except for flattened xml shapes. Member element +write their own element start tag. These elements should always be closed. + +Flattened Element + +Flattened element should be used to encode shapes marked with flattened trait into xml elements. Flattened element +do not write a start tag, and thus should not be closed. + +Simple types encoding + +All simple type methods on value such as String(), Long() etc; auto close the associated member element. + +Array + +Array returns the collection encoder. It has two modes, wrapped and flattened encoding. + +Wrapped arrays have two methods Array() and ArrayWithCustomName() which facilitate array member wrapping. +By default, a wrapped array members are wrapped with `member` named start element. + + <wrappedArray><member>apple</member><member>tree</member></wrappedArray> + +Flattened arrays rely on Value being marked as flattened. +If a shape is marked as flattened, Array() will use the shape element name as wrapper for array elements. + + <flattenedAarray>apple</flattenedArray><flattenedArray>tree</flattenedArray> + +Map + +Map is the map encoder. It has two modes, wrapped and flattened encoding. + +Wrapped map has Array() method, which facilitate map member wrapping. +By default, a wrapped map members are wrapped with `entry` named start element. + + <wrappedMap><entry><Key>apple</Key><Value>tree</Value></entry><entry><Key>snow</Key><Value>ice</Value></entry></wrappedMap> + +Flattened map rely on Value being marked as flattened. +If a shape is marked as flattened, Map() will use the shape element name as wrapper for map entry elements. + + <flattenedMap><Key>apple</Key><Value>tree</Value></flattenedMap><flattenedMap><Key>snow</Key><Value>ice</Value></flattenedMap> +*/ +package xml diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/element.go b/vendor/github.com/aws/smithy-go/encoding/xml/element.go new file mode 100644 index 0000000000..ae84e7999e --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/element.go @@ -0,0 +1,91 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copied and modified from Go 1.14 stdlib's encoding/xml + +package xml + +// A Name represents an XML name (Local) annotated +// with a name space identifier (Space). +// In tokens returned by Decoder.Token, the Space identifier +// is given as a canonical URL, not the short prefix used +// in the document being parsed. +type Name struct { + Space, Local string +} + +// An Attr represents an attribute in an XML element (Name=Value). +type Attr struct { + Name Name + Value string +} + +/* +NewAttribute returns a pointer to an attribute. +It takes in a local name aka attribute name, and value +representing the attribute value. +*/ +func NewAttribute(local, value string) Attr { + return Attr{ + Name: Name{ + Local: local, + }, + Value: value, + } +} + +/* +NewNamespaceAttribute returns a pointer to an attribute. +It takes in a local name aka attribute name, and value +representing the attribute value. + +NewNamespaceAttribute appends `xmlns:` in front of namespace +prefix. + +For creating a name space attribute representing +`xmlns:prefix="http://example.com`, the breakdown would be: +local = "prefix" +value = "http://example.com" +*/ +func NewNamespaceAttribute(local, value string) Attr { + attr := NewAttribute(local, value) + + // default name space identifier + attr.Name.Space = "xmlns" + return attr +} + +// A StartElement represents an XML start element. +type StartElement struct { + Name Name + Attr []Attr +} + +// Copy creates a new copy of StartElement. +func (e StartElement) Copy() StartElement { + attrs := make([]Attr, len(e.Attr)) + copy(attrs, e.Attr) + e.Attr = attrs + return e +} + +// End returns the corresponding XML end element. +func (e StartElement) End() EndElement { + return EndElement{e.Name} +} + +// returns true if start element local name is empty +func (e StartElement) isZero() bool { + return len(e.Name.Local) == 0 +} + +// An EndElement represents an XML end element. +type EndElement struct { + Name Name +} + +// returns true if end element local name is empty +func (e EndElement) isZero() bool { + return len(e.Name.Local) == 0 +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/encoder.go b/vendor/github.com/aws/smithy-go/encoding/xml/encoder.go new file mode 100644 index 0000000000..16fb3dddb0 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/encoder.go @@ -0,0 +1,51 @@ +package xml + +// writer interface used by the xml encoder to write an encoded xml +// document in a writer. +type writer interface { + + // Write takes in a byte slice and returns number of bytes written and error + Write(p []byte) (n int, err error) + + // WriteRune takes in a rune and returns number of bytes written and error + WriteRune(r rune) (n int, err error) + + // WriteString takes in a string and returns number of bytes written and error + WriteString(s string) (n int, err error) + + // String method returns a string + String() string + + // Bytes return a byte slice. + Bytes() []byte +} + +// Encoder is an XML encoder that supports construction of XML values +// using methods. The encoder takes in a writer and maintains a scratch buffer. +type Encoder struct { + w writer + scratch *[]byte +} + +// NewEncoder returns an XML encoder +func NewEncoder(w writer) *Encoder { + scratch := make([]byte, 64) + + return &Encoder{w: w, scratch: &scratch} +} + +// String returns the string output of the XML encoder +func (e Encoder) String() string { + return e.w.String() +} + +// Bytes returns the []byte slice of the XML encoder +func (e Encoder) Bytes() []byte { + return e.w.Bytes() +} + +// RootElement builds a root element encoding +// It writes it's start element tag. The value should be closed. +func (e Encoder) RootElement(element StartElement) Value { + return newValue(e.w, e.scratch, element) +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/encoder_test.go b/vendor/github.com/aws/smithy-go/encoding/xml/encoder_test.go new file mode 100644 index 0000000000..c8e31e1bde --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/encoder_test.go @@ -0,0 +1,560 @@ +package xml_test + +import ( + "bytes" + "log" + "sort" + "testing" + + "github.com/aws/smithy-go/encoding/xml" +) + +var root = xml.StartElement{Name: xml.Name{Local: "root"}} + +func TestEncoder(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + root := encoder.RootElement(root) + defer root.Close() + + stringKey := xml.StartElement{Name: xml.Name{Local: "stringKey"}} + integerKey := xml.StartElement{Name: xml.Name{Local: "integerKey"}} + floatKey := xml.StartElement{Name: xml.Name{Local: "floatKey"}} + foo := xml.StartElement{Name: xml.Name{Local: "foo"}} + byteSlice := xml.StartElement{Name: xml.Name{Local: "byteSlice"}} + + root.MemberElement(stringKey).String("stringValue") + root.MemberElement(integerKey).Integer(1024) + root.MemberElement(floatKey).Float(3.14) + + ns := root.MemberElement(foo) + defer ns.Close() + ns.MemberElement(byteSlice).String("Zm9vIGJhcg==") + }() + + e := []byte(`<root><stringKey>stringValue</stringKey><integerKey>1024</integerKey><floatKey>3.14</floatKey><foo><byteSlice>Zm9vIGJhcg==</byteSlice></foo></root>`) + verify(t, encoder, e) +} + +func TestEncodeAttribute(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := xml.StartElement{ + Name: xml.Name{Local: "payload", Space: "baz"}, + Attr: []xml.Attr{ + xml.NewAttribute("attrkey", "value"), + }, + } + + obj := encoder.RootElement(r) + obj.String("") + }() + + expect := `<baz:payload attrkey="value"></baz:payload>` + + verify(t, encoder, []byte(expect)) +} + +func TestEncodeNamespace(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + root := encoder.RootElement(root) + defer root.Close() + + key := xml.StartElement{ + Name: xml.Name{Local: "namespace"}, + Attr: []xml.Attr{ + xml.NewNamespaceAttribute("prefix", "https://example.com"), + }, + } + + n := root.MemberElement(key) + defer n.Close() + + prefix := xml.StartElement{Name: xml.Name{Local: "user"}} + n.MemberElement(prefix).String("abc") + }() + + e := []byte(`<root><namespace xmlns:prefix="https://example.com"><user>abc</user></namespace></root>`) + verify(t, encoder, e) +} + +func TestEncodeEmptyNamespacePrefix(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + func() { + root := encoder.RootElement(root) + defer root.Close() + + key := xml.StartElement{ + Name: xml.Name{Local: "namespace"}, + Attr: []xml.Attr{ + xml.NewNamespaceAttribute("", "https://example.com"), + }, + } + + n := root.MemberElement(key) + defer n.Close() + + prefix := xml.StartElement{Name: xml.Name{Local: "user"}} + n.MemberElement(prefix).String("abc") + }() + + e := []byte(`<root><namespace xmlns="https://example.com"><user>abc</user></namespace></root>`) + verify(t, encoder, e) +} + +func verify(t *testing.T, encoder *xml.Encoder, e []byte) { + if a := encoder.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } + + if a := encoder.String(); string(encoder.Bytes()) != a { + t.Errorf("expected %s, but got %s", e, a) + } +} + +func TestEncodeNestedShape(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + + // nested `nested` shape + nested := xml.StartElement{Name: xml.Name{Local: "nested"}} + n1 := r.MemberElement(nested) + defer n1.Close() + + // nested `value` shape + value := xml.StartElement{Name: xml.Name{Local: "value"}} + n1.MemberElement(value).String("expected value") + }() + + e := []byte(`<root><nested><value>expected value</value></nested></root>`) + defer verify(t, encoder, e) +} + +func TestEncodeMapString(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + func() { + r := encoder.RootElement(root) + defer r.Close() + + // nested `mapStr` shape + mapstr := xml.StartElement{Name: xml.Name{Local: "mapstr"}} + mapElement := r.MemberElement(mapstr) + defer mapElement.Close() + + m := mapElement.Map() + + key := xml.StartElement{Name: xml.Name{Local: "key"}} + value := xml.StartElement{Name: xml.Name{Local: "value"}} + + e := m.Entry() + defer e.Close() + e.MemberElement(key).String("abc") + e.MemberElement(value).Integer(123) + }() + + ex := []byte(`<root><mapstr><entry><key>abc</key><value>123</value></entry></mapstr></root>`) + verify(t, encoder, ex) +} + +func TestEncodeMapFlatten(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + // nested `mapStr` shape + mapstr := xml.StartElement{Name: xml.Name{Local: "mapstr"}} + flatElement := r.FlattenedElement(mapstr) + + m := flatElement.Map() + e := m.Entry() + defer e.Close() + + key := xml.StartElement{Name: xml.Name{Local: "key"}} + e.MemberElement(key).String("abc") + + value := xml.StartElement{Name: xml.Name{Local: "value"}} + e.MemberElement(value).Integer(123) + }() + + ex := []byte(`<root><mapstr><key>abc</key><value>123</value></mapstr></root>`) + verify(t, encoder, ex) +} + +func TestEncodeMapNamed(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + // nested `mapStr` shape + mapstr := xml.StartElement{Name: xml.Name{Local: "mapNamed"}} + mapElement := r.MemberElement(mapstr) + defer mapElement.Close() + + m := mapElement.Map() + e := m.Entry() + defer e.Close() + + key := xml.StartElement{Name: xml.Name{Local: "namedKey"}} + e.MemberElement(key).String("abc") + + value := xml.StartElement{Name: xml.Name{Local: "namedValue"}} + e.MemberElement(value).Integer(123) + }() + + ex := []byte(`<root><mapNamed><entry><namedKey>abc</namedKey><namedValue>123</namedValue></entry></mapNamed></root>`) + verify(t, encoder, ex) +} + +func TestEncodeMapShape(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + + // nested `mapStr` shape + mapstr := xml.StartElement{Name: xml.Name{Local: "mapShape"}} + mapElement := r.MemberElement(mapstr) + defer mapElement.Close() + + m := mapElement.Map() + + e := m.Entry() + defer e.Close() + + key := xml.StartElement{Name: xml.Name{Local: "key"}} + e.MemberElement(key).String("abc") + + value := xml.StartElement{Name: xml.Name{Local: "value"}} + n1 := e.MemberElement(value) + defer n1.Close() + + shapeVal := xml.StartElement{Name: xml.Name{Local: "shapeVal"}} + n1.MemberElement(shapeVal).Integer(1) + }() + + ex := []byte(`<root><mapShape><entry><key>abc</key><value><shapeVal>1</shapeVal></value></entry></mapShape></root>`) + verify(t, encoder, ex) +} + +func TestEncodeMapFlattenShape(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + // nested `mapStr` shape + mapstr := xml.StartElement{Name: xml.Name{Local: "mapShape"}} + flatElement := r.FlattenedElement(mapstr) + m := flatElement.Map() + + e := m.Entry() + defer e.Close() + + key := xml.StartElement{Name: xml.Name{Local: "key"}} + e.MemberElement(key).String("abc") + + value := xml.StartElement{Name: xml.Name{Local: "value"}} + n1 := e.MemberElement(value) + defer n1.Close() + + shapeVal := xml.StartElement{Name: xml.Name{Local: "shapeVal"}} + n1.MemberElement(shapeVal).Integer(1) + }() + ex := []byte(`<root><mapShape><key>abc</key><value><shapeVal>1</shapeVal></value></mapShape></root>`) + verify(t, encoder, ex) +} + +func TestEncodeMapNamedShape(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + + // nested `mapStr` shape + mapstr := xml.StartElement{Name: xml.Name{Local: "mapNamedShape"}} + mapElement := r.MemberElement(mapstr) + defer mapElement.Close() + + m := mapElement.Map() + e := m.Entry() + defer e.Close() + + key := xml.StartElement{Name: xml.Name{Local: "namedKey"}} + e.MemberElement(key).String("abc") + + value := xml.StartElement{Name: xml.Name{Local: "namedValue"}} + n1 := e.MemberElement(value) + defer n1.Close() + + shapeVal := xml.StartElement{Name: xml.Name{Local: "shapeVal"}} + n1.MemberElement(shapeVal).Integer(1) + }() + + ex := []byte(`<root><mapNamedShape><entry><namedKey>abc</namedKey><namedValue><shapeVal>1</shapeVal></namedValue></entry></mapNamedShape></root>`) + verify(t, encoder, ex) +} + +func TestEncodeListString(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + + // Object key `liststr` + liststr := xml.StartElement{Name: xml.Name{Local: "liststr"}} + m := r.MemberElement(liststr) + defer m.Close() + + a := m.Array() + a.Member().String("abc") + a.Member().Integer(123) + }() + + ex := []byte(`<root><liststr><member>abc</member><member>123</member></liststr></root>`) + verify(t, encoder, ex) +} + +func TestEncodeListFlatten(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + + // Object key `liststr` + liststr := xml.StartElement{Name: xml.Name{Local: "liststr"}} + m := r.FlattenedElement(liststr) + + a := m.Array() + a.Member().String("abc") + a.Member().Integer(123) + }() + + ex := []byte(`<root><liststr>abc</liststr><liststr>123</liststr></root>`) + verify(t, encoder, ex) +} + +func TestEncodeListNamed(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + + // Object key `liststr` + liststr := xml.StartElement{Name: xml.Name{Local: "liststr"}} + + namedMember := xml.StartElement{Name: xml.Name{Local: "namedMember"}} + m := r.MemberElement(liststr) + defer m.Close() + + a := m.ArrayWithCustomName(namedMember) + a.Member().String("abc") + a.Member().Integer(123) + }() + + ex := []byte(`<root><liststr><namedMember>abc</namedMember><namedMember>123</namedMember></liststr></root>`) + verify(t, encoder, ex) +} + +// +func TestEncodeListShape(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + + // Object key `liststr` + liststr := xml.StartElement{Name: xml.Name{Local: "liststr"}} + + m := r.MemberElement(liststr) + defer m.Close() + + a := m.Array() + + value := xml.StartElement{Name: xml.Name{Local: "value"}} + + m1 := a.Member() + m1.MemberElement(value).String("abc") + m1.Close() + + m2 := a.Member() + m2.MemberElement(value).Integer(123) + m2.Close() + }() + + ex := []byte(`<root><liststr><member><value>abc</value></member><member><value>123</value></member></liststr></root>`) + verify(t, encoder, ex) +} + +// +func TestEncodeListFlattenShape(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + + // Object key `liststr` + liststr := xml.StartElement{Name: xml.Name{Local: "liststr"}} + + m := r.FlattenedElement(liststr) + + a := m.Array() + value := xml.StartElement{Name: xml.Name{Local: "value"}} + + m1 := a.Member() + m1.MemberElement(value).String("abc") + m1.Close() + + m2 := a.Member() + m2.MemberElement(value).Integer(123) + m2.Close() + }() + + ex := []byte(`<root><liststr><value>abc</value></liststr><liststr><value>123</value></liststr></root>`) + verify(t, encoder, ex) +} + +// +func TestEncodeListNamedShape(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + + // Object key `liststr` + liststr := xml.StartElement{Name: xml.Name{Local: "liststr"}} + namedMember := xml.StartElement{Name: xml.Name{Local: "namedMember"}} + + // member element + m := r.MemberElement(liststr) + defer m.Close() + + // Build array + a := m.ArrayWithCustomName(namedMember) + + value := xml.StartElement{Name: xml.Name{Local: "value"}} + m1 := a.Member() + m1.MemberElement(value).String("abc") + m1.Close() + + m2 := a.Member() + m2.MemberElement(value).Integer(123) + m2.Close() + }() + + ex := []byte(`<root><liststr><namedMember><value>abc</value></namedMember><namedMember><value>123</value></namedMember></liststr></root>`) + verify(t, encoder, ex) +} + +func TestEncodeEscaping(t *testing.T) { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + func() { + r := encoder.RootElement(root) + defer r.Close() + + cases := map[string]rune{ + "quote": '"', + "apos": '\'', + "amp": '&', + "lt": '<', + "gt": '>', + "tab": '\t', + "newLine": '\n', + "carriageReturn": '\r', + "nextLine": '\u0085', + "lineSeparator": '\u2028', + } + + var sortedKeys []string + for name := range cases { + sortedKeys = append(sortedKeys, name) + } + + sort.Strings(sortedKeys) + + for _, name := range sortedKeys { + rr := cases[name] + + st := xml.StartElement{Name: xml.Name{Local: name}} + st.Attr = append(st.Attr, xml.Attr{ + Name: xml.Name{ + Local: "key", + }, + Value: name + string(rr) + name, + }) + value := r.MemberElement(st) + value.String(name + string(rr) + name) + } + }() + + ex := []byte(`<root><amp key="amp&amp">amp&amp</amp><apos key="apos'apos">apos'apos</apos><carriageReturn key="carriageReturn
carriageReturn">carriageReturn
carriageReturn</carriageReturn><gt key="gt>gt">gt>gt</gt><lineSeparator key="lineSeparator
lineSeparator">lineSeparator
lineSeparator</lineSeparator><lt key="lt<lt">lt<lt</lt><newLine key="newLine
newLine">newLine
newLine</newLine><nextLine key="nextLine…nextLine">nextLine…nextLine</nextLine><quote key="quote"quote">quote"quote</quote><tab key="tab	tab">tab	tab</tab></root>`) + verify(t, encoder, ex) +} + +// ExampleEncoder is the example function on how to use an encoder +func ExampleEncoder() { + b := bytes.NewBuffer(nil) + encoder := xml.NewEncoder(b) + + // expected encoded xml document is : + // `<root><liststr><namedMember><value>abc</value></namedMember><namedMember><value>123</value></namedMember></liststr></root>` + defer log.Printf("Encoded xml document: %v", encoder.String()) + + r := encoder.RootElement(root) + defer r.Close() + + // Object key `liststr` + liststr := xml.StartElement{Name: xml.Name{Local: "liststr"}} + namedMember := xml.StartElement{Name: xml.Name{Local: "namedMember"}} + + // member element + m := r.MemberElement(liststr) + defer m.Close() + + // Build array + a := m.ArrayWithCustomName(namedMember) + + value := xml.StartElement{Name: xml.Name{Local: "value"}} + m1 := a.Member() + m1.MemberElement(value).String("abc") + m1.Close() + + m2 := a.Member() + m2.MemberElement(value).Integer(123) + m2.Close() +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/error_utils.go b/vendor/github.com/aws/smithy-go/encoding/xml/error_utils.go new file mode 100644 index 0000000000..f3db6ccca8 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/error_utils.go @@ -0,0 +1,51 @@ +package xml + +import ( + "encoding/xml" + "fmt" + "io" +) + +// ErrorComponents represents the error response fields +// that will be deserialized from an xml error response body +type ErrorComponents struct { + Code string + Message string +} + +// GetErrorResponseComponents returns the error fields from an xml error response body +func GetErrorResponseComponents(r io.Reader, noErrorWrapping bool) (ErrorComponents, error) { + if noErrorWrapping { + var errResponse noWrappedErrorResponse + if err := xml.NewDecoder(r).Decode(&errResponse); err != nil && err != io.EOF { + return ErrorComponents{}, fmt.Errorf("error while deserializing xml error response: %w", err) + } + return ErrorComponents{ + Code: errResponse.Code, + Message: errResponse.Message, + }, nil + } + + var errResponse wrappedErrorResponse + if err := xml.NewDecoder(r).Decode(&errResponse); err != nil && err != io.EOF { + return ErrorComponents{}, fmt.Errorf("error while deserializing xml error response: %w", err) + } + return ErrorComponents{ + Code: errResponse.Code, + Message: errResponse.Message, + }, nil +} + +// noWrappedErrorResponse represents the error response body with +// no internal <Error></Error wrapping +type noWrappedErrorResponse struct { + Code string `xml:"Code"` + Message string `xml:"Message"` +} + +// wrappedErrorResponse represents the error response body +// wrapped within <Error>...</Error> +type wrappedErrorResponse struct { + Code string `xml:"Error>Code"` + Message string `xml:"Error>Message"` +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/error_utils_test.go b/vendor/github.com/aws/smithy-go/encoding/xml/error_utils_test.go new file mode 100644 index 0000000000..0077d166d0 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/error_utils_test.go @@ -0,0 +1,62 @@ +package xml + +import ( + "bytes" + "io" + "strings" + "testing" +) + +func TestGetResponseErrorCode(t *testing.T) { + cases := map[string]struct { + errorResponse io.Reader + noErrorWrappingEnabled bool + expectedErrorCode string + expectedErrorMessage string + }{ + "no error wrapping enabled": { + errorResponse: bytes.NewReader([]byte(`<ErrorResponse> + <Error> + <Type>Sender</Type> + <Code>InvalidGreeting</Code> + <Message>Hi</Message> + <AnotherSetting>setting</AnotherSetting> + </Error> + <RequestId>foo-id</RequestId> +</ErrorResponse>`)), + expectedErrorCode: "InvalidGreeting", + expectedErrorMessage: "Hi", + }, + "no error wrapping disabled": { + errorResponse: bytes.NewReader([]byte(`<ErrorResponse> + <Type>Sender</Type> + <Code>InvalidGreeting</Code> + <Message>Hi</Message> + <AnotherSetting>setting</AnotherSetting> + <RequestId>foo-id</RequestId> +</ErrorResponse>`)), + noErrorWrappingEnabled: true, + expectedErrorCode: "InvalidGreeting", + expectedErrorMessage: "Hi", + }, + "no response body": { + errorResponse: bytes.NewReader([]byte(``)), + }, + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + ec, err := GetErrorResponseComponents(c.errorResponse, c.noErrorWrappingEnabled) + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if e, a := c.expectedErrorCode, ec.Code; !strings.EqualFold(e, a) { + t.Fatalf("expected %v, got %v", e, a) + } + if e, a := c.expectedErrorMessage, ec.Message; !strings.EqualFold(e, a) { + t.Fatalf("expected %v, got %v", e, a) + } + }) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/escape.go b/vendor/github.com/aws/smithy-go/encoding/xml/escape.go new file mode 100644 index 0000000000..1c5479af67 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/escape.go @@ -0,0 +1,137 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copied and modified from Go 1.14 stdlib's encoding/xml + +package xml + +import ( + "unicode/utf8" +) + +// Copied from Go 1.14 stdlib's encoding/xml +var ( + escQuot = []byte(""") // shorter than """ + escApos = []byte("'") // shorter than "'" + escAmp = []byte("&") + escLT = []byte("<") + escGT = []byte(">") + escTab = []byte("	") + escNL = []byte("
") + escCR = []byte("
") + escFFFD = []byte("\uFFFD") // Unicode replacement character + + // Additional Escapes + escNextLine = []byte("…") + escLS = []byte("
") +) + +// Decide whether the given rune is in the XML Character Range, per +// the Char production of https://www.xml.com/axml/testaxml.htm, +// Section 2.2 Characters. +func isInCharacterRange(r rune) (inrange bool) { + return r == 0x09 || + r == 0x0A || + r == 0x0D || + r >= 0x20 && r <= 0xD7FF || + r >= 0xE000 && r <= 0xFFFD || + r >= 0x10000 && r <= 0x10FFFF +} + +// TODO: When do we need to escape the string? +// Based on encoding/xml escapeString from the Go Standard Library. +// https://golang.org/src/encoding/xml/xml.go +func escapeString(e writer, s string) { + var esc []byte + last := 0 + for i := 0; i < len(s); { + r, width := utf8.DecodeRuneInString(s[i:]) + i += width + switch r { + case '"': + esc = escQuot + case '\'': + esc = escApos + case '&': + esc = escAmp + case '<': + esc = escLT + case '>': + esc = escGT + case '\t': + esc = escTab + case '\n': + esc = escNL + case '\r': + esc = escCR + case '\u0085': + // Not escaped by stdlib + esc = escNextLine + case '\u2028': + // Not escaped by stdlib + esc = escLS + default: + if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) { + esc = escFFFD + break + } + continue + } + e.WriteString(s[last : i-width]) + e.Write(esc) + last = i + } + e.WriteString(s[last:]) +} + +// escapeText writes to w the properly escaped XML equivalent +// of the plain text data s. If escapeNewline is true, newline +// characters will be escaped. +// +// Based on encoding/xml escapeText from the Go Standard Library. +// https://golang.org/src/encoding/xml/xml.go +func escapeText(e writer, s []byte) { + var esc []byte + last := 0 + for i := 0; i < len(s); { + r, width := utf8.DecodeRune(s[i:]) + i += width + switch r { + case '"': + esc = escQuot + case '\'': + esc = escApos + case '&': + esc = escAmp + case '<': + esc = escLT + case '>': + esc = escGT + case '\t': + esc = escTab + case '\n': + // This always escapes newline, which is different than stdlib's optional + // escape of new line. + esc = escNL + case '\r': + esc = escCR + case '\u0085': + // Not escaped by stdlib + esc = escNextLine + case '\u2028': + // Not escaped by stdlib + esc = escLS + default: + if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) { + esc = escFFFD + break + } + continue + } + e.Write(s[last : i-width]) + e.Write(esc) + last = i + } + e.Write(s[last:]) +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/gotest/ya.make b/vendor/github.com/aws/smithy-go/encoding/xml/gotest/ya.make new file mode 100644 index 0000000000..a47e23603b --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/gotest/ya.make @@ -0,0 +1,5 @@ +GO_TEST_FOR(vendor/github.com/aws/smithy-go/encoding/xml) + +LICENSE(Apache-2.0) + +END() diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/map.go b/vendor/github.com/aws/smithy-go/encoding/xml/map.go new file mode 100644 index 0000000000..e42858965c --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/map.go @@ -0,0 +1,53 @@ +package xml + +// mapEntryWrapper is the default member wrapper start element for XML Map entry +var mapEntryWrapper = StartElement{ + Name: Name{Local: "entry"}, +} + +// Map represents the encoding of a XML map type +type Map struct { + w writer + scratch *[]byte + + // member start element is the map entry wrapper start element + memberStartElement StartElement + + // isFlattened returns true if the map is a flattened map + isFlattened bool +} + +// newMap returns a map encoder which sets the default map +// entry wrapper to `entry`. +// +// A map `someMap : {{key:"abc", value:"123"}}` is represented as +// `<someMap><entry><key>abc<key><value>123</value></entry></someMap>`. +func newMap(w writer, scratch *[]byte) *Map { + return &Map{ + w: w, + scratch: scratch, + memberStartElement: mapEntryWrapper, + } +} + +// newFlattenedMap returns a map encoder which sets the map +// entry wrapper to the passed in memberWrapper`. +// +// A flattened map `someMap : {{key:"abc", value:"123"}}` is represented as +// `<someMap><key>abc<key><value>123</value></someMap>`. +func newFlattenedMap(w writer, scratch *[]byte, memberWrapper StartElement) *Map { + return &Map{ + w: w, + scratch: scratch, + memberStartElement: memberWrapper, + isFlattened: true, + } +} + +// Entry returns a Value encoder with map's element. +// It writes the member wrapper start tag for each entry. +func (m *Map) Entry() Value { + v := newValue(m.w, m.scratch, m.memberStartElement) + v.isFlattened = m.isFlattened + return v +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/map_test.go b/vendor/github.com/aws/smithy-go/encoding/xml/map_test.go new file mode 100644 index 0000000000..022165bd6d --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/map_test.go @@ -0,0 +1,77 @@ +package xml + +import ( + "bytes" + "testing" +) + +func TestWrappedMap(t *testing.T) { + buffer := bytes.NewBuffer(nil) + scratch := make([]byte, 64) + + func() { + m := newMap(buffer, &scratch) + + key := StartElement{Name: Name{Local: "key"}} + value := StartElement{Name: Name{Local: "value"}} + + // map entry + e := m.Entry() + e.MemberElement(key).String("example-key1") + e.MemberElement(value).String("example1") + e.Close() + + // map entry + e = m.Entry() + e.MemberElement(key).String("example-key2") + e.MemberElement(value).String("example2") + e.Close() + + // map entry + e = m.Entry() + e.MemberElement(key).String("example-key3") + e.MemberElement(value).String("example3") + e.Close() + }() + + ex := []byte(`<entry><key>example-key1</key><value>example1</value></entry><entry><key>example-key2</key><value>example2</value></entry><entry><key>example-key3</key><value>example3</value></entry>`) + if a := buffer.Bytes(); bytes.Compare(ex, a) != 0 { + t.Errorf("expected %+q, but got %+q", ex, a) + } +} + +func TestFlattenedMapWithCustomName(t *testing.T) { + buffer := bytes.NewBuffer(nil) + scratch := make([]byte, 64) + + func() { + root := StartElement{Name: Name{Local: "flatMap"}} + m := newFlattenedMap(buffer, &scratch, root) + + key := StartElement{Name: Name{Local: "key"}} + value := StartElement{Name: Name{Local: "value"}} + + // map entry + e := m.Entry() + e.MemberElement(key).String("example-key1") + e.MemberElement(value).String("example1") + e.Close() + + // map entry + e = m.Entry() + e.MemberElement(key).String("example-key2") + e.MemberElement(value).String("example2") + e.Close() + + // map entry + e = m.Entry() + e.MemberElement(key).String("example-key3") + e.MemberElement(value).String("example3") + e.Close() + }() + + ex := []byte(`<flatMap><key>example-key1</key><value>example1</value></flatMap><flatMap><key>example-key2</key><value>example2</value></flatMap><flatMap><key>example-key3</key><value>example3</value></flatMap>`) + if a := buffer.Bytes(); bytes.Compare(ex, a) != 0 { + t.Errorf("expected %+q, but got %+q", ex, a) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/value.go b/vendor/github.com/aws/smithy-go/encoding/xml/value.go new file mode 100644 index 0000000000..09434b2c0b --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/value.go @@ -0,0 +1,302 @@ +package xml + +import ( + "encoding/base64" + "fmt" + "math/big" + "strconv" + + "github.com/aws/smithy-go/encoding" +) + +// Value represents an XML Value type +// XML Value types: Object, Array, Map, String, Number, Boolean. +type Value struct { + w writer + scratch *[]byte + + // xml start element is the associated start element for the Value + startElement StartElement + + // indicates if the Value represents a flattened shape + isFlattened bool +} + +// newFlattenedValue returns a Value encoder. newFlattenedValue does NOT write the start element tag +func newFlattenedValue(w writer, scratch *[]byte, startElement StartElement) Value { + return Value{ + w: w, + scratch: scratch, + startElement: startElement, + } +} + +// newValue writes the start element xml tag and returns a Value +func newValue(w writer, scratch *[]byte, startElement StartElement) Value { + writeStartElement(w, startElement) + return Value{w: w, scratch: scratch, startElement: startElement} +} + +// writeStartElement takes in a start element and writes it. +// It handles namespace, attributes in start element. +func writeStartElement(w writer, el StartElement) error { + if el.isZero() { + return fmt.Errorf("xml start element cannot be nil") + } + + w.WriteRune(leftAngleBracket) + + if len(el.Name.Space) != 0 { + escapeString(w, el.Name.Space) + w.WriteRune(colon) + } + escapeString(w, el.Name.Local) + for _, attr := range el.Attr { + w.WriteRune(' ') + writeAttribute(w, &attr) + } + + w.WriteRune(rightAngleBracket) + return nil +} + +// writeAttribute writes an attribute from a provided Attribute +// For a namespace attribute, the attr.Name.Space must be defined as "xmlns". +// https://www.w3.org/TR/REC-xml-names/#NT-DefaultAttName +func writeAttribute(w writer, attr *Attr) { + // if local, space both are not empty + if len(attr.Name.Space) != 0 && len(attr.Name.Local) != 0 { + escapeString(w, attr.Name.Space) + w.WriteRune(colon) + } + + // if prefix is empty, the default `xmlns` space should be used as prefix. + if len(attr.Name.Local) == 0 { + attr.Name.Local = attr.Name.Space + } + + escapeString(w, attr.Name.Local) + w.WriteRune(equals) + w.WriteRune(quote) + escapeString(w, attr.Value) + w.WriteRune(quote) +} + +// writeEndElement takes in a end element and writes it. +func writeEndElement(w writer, el EndElement) error { + if el.isZero() { + return fmt.Errorf("xml end element cannot be nil") + } + + w.WriteRune(leftAngleBracket) + w.WriteRune(forwardSlash) + + if len(el.Name.Space) != 0 { + escapeString(w, el.Name.Space) + w.WriteRune(colon) + } + escapeString(w, el.Name.Local) + w.WriteRune(rightAngleBracket) + + return nil +} + +// String encodes v as a XML string. +// It will auto close the parent xml element tag. +func (xv Value) String(v string) { + escapeString(xv.w, v) + xv.Close() +} + +// Byte encodes v as a XML number. +// It will auto close the parent xml element tag. +func (xv Value) Byte(v int8) { + xv.Long(int64(v)) +} + +// Short encodes v as a XML number. +// It will auto close the parent xml element tag. +func (xv Value) Short(v int16) { + xv.Long(int64(v)) +} + +// Integer encodes v as a XML number. +// It will auto close the parent xml element tag. +func (xv Value) Integer(v int32) { + xv.Long(int64(v)) +} + +// Long encodes v as a XML number. +// It will auto close the parent xml element tag. +func (xv Value) Long(v int64) { + *xv.scratch = strconv.AppendInt((*xv.scratch)[:0], v, 10) + xv.w.Write(*xv.scratch) + + xv.Close() +} + +// Float encodes v as a XML number. +// It will auto close the parent xml element tag. +func (xv Value) Float(v float32) { + xv.float(float64(v), 32) + xv.Close() +} + +// Double encodes v as a XML number. +// It will auto close the parent xml element tag. +func (xv Value) Double(v float64) { + xv.float(v, 64) + xv.Close() +} + +func (xv Value) float(v float64, bits int) { + *xv.scratch = encoding.EncodeFloat((*xv.scratch)[:0], v, bits) + xv.w.Write(*xv.scratch) +} + +// Boolean encodes v as a XML boolean. +// It will auto close the parent xml element tag. +func (xv Value) Boolean(v bool) { + *xv.scratch = strconv.AppendBool((*xv.scratch)[:0], v) + xv.w.Write(*xv.scratch) + + xv.Close() +} + +// Base64EncodeBytes writes v as a base64 value in XML string. +// It will auto close the parent xml element tag. +func (xv Value) Base64EncodeBytes(v []byte) { + encodeByteSlice(xv.w, (*xv.scratch)[:0], v) + xv.Close() +} + +// BigInteger encodes v big.Int as XML value. +// It will auto close the parent xml element tag. +func (xv Value) BigInteger(v *big.Int) { + xv.w.Write([]byte(v.Text(10))) + xv.Close() +} + +// BigDecimal encodes v big.Float as XML value. +// It will auto close the parent xml element tag. +func (xv Value) BigDecimal(v *big.Float) { + if i, accuracy := v.Int64(); accuracy == big.Exact { + xv.Long(i) + return + } + + xv.w.Write([]byte(v.Text('e', -1))) + xv.Close() +} + +// Write writes v directly to the xml document +// if escapeXMLText is set to true, write will escape text. +// It will auto close the parent xml element tag. +func (xv Value) Write(v []byte, escapeXMLText bool) { + // escape and write xml text + if escapeXMLText { + escapeText(xv.w, v) + } else { + // write xml directly + xv.w.Write(v) + } + + xv.Close() +} + +// MemberElement does member element encoding. It returns a Value. +// Member Element method should be used for all shapes except flattened shapes. +// +// A call to MemberElement will write nested element tags directly using the +// provided start element. The value returned by MemberElement should be closed. +func (xv Value) MemberElement(element StartElement) Value { + return newValue(xv.w, xv.scratch, element) +} + +// FlattenedElement returns flattened element encoding. It returns a Value. +// This method should be used for flattened shapes. +// +// Unlike MemberElement, flattened element will NOT write element tags +// directly for the associated start element. +// +// The value returned by the FlattenedElement does not need to be closed. +func (xv Value) FlattenedElement(element StartElement) Value { + v := newFlattenedValue(xv.w, xv.scratch, element) + v.isFlattened = true + return v +} + +// Array returns an array encoder. By default, the members of array are +// wrapped with `<member>` element tag. +// If value is marked as flattened, the start element is used to wrap the members instead of +// the `<member>` element. +func (xv Value) Array() *Array { + return newArray(xv.w, xv.scratch, arrayMemberWrapper, xv.startElement, xv.isFlattened) +} + +/* +ArrayWithCustomName returns an array encoder. + +It takes named start element as an argument, the named start element will used to wrap xml array entries. +for eg, `<someList><customName>entry1</customName></someList>` +Here `customName` named start element will be wrapped on each array member. +*/ +func (xv Value) ArrayWithCustomName(element StartElement) *Array { + return newArray(xv.w, xv.scratch, element, xv.startElement, xv.isFlattened) +} + +/* +Map returns a map encoder. By default, the map entries are +wrapped with `<entry>` element tag. + +If value is marked as flattened, the start element is used to wrap the entry instead of +the `<member>` element. +*/ +func (xv Value) Map() *Map { + // flattened map + if xv.isFlattened { + return newFlattenedMap(xv.w, xv.scratch, xv.startElement) + } + + // un-flattened map + return newMap(xv.w, xv.scratch) +} + +// encodeByteSlice is modified copy of json encoder's encodeByteSlice. +// It is used to base64 encode a byte slice. +func encodeByteSlice(w writer, scratch []byte, v []byte) { + if v == nil { + return + } + + encodedLen := base64.StdEncoding.EncodedLen(len(v)) + if encodedLen <= len(scratch) { + // If the encoded bytes fit in e.scratch, avoid an extra + // allocation and use the cheaper Encoding.Encode. + dst := scratch[:encodedLen] + base64.StdEncoding.Encode(dst, v) + w.Write(dst) + } else if encodedLen <= 1024 { + // The encoded bytes are short enough to allocate for, and + // Encoding.Encode is still cheaper. + dst := make([]byte, encodedLen) + base64.StdEncoding.Encode(dst, v) + w.Write(dst) + } else { + // The encoded bytes are too long to cheaply allocate, and + // Encoding.Encode is no longer noticeably cheaper. + enc := base64.NewEncoder(base64.StdEncoding, w) + enc.Write(v) + enc.Close() + } +} + +// IsFlattened returns true if value is for flattened shape. +func (xv Value) IsFlattened() bool { + return xv.isFlattened +} + +// Close closes the value. +func (xv Value) Close() { + writeEndElement(xv.w, xv.startElement.End()) +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/value_test.go b/vendor/github.com/aws/smithy-go/encoding/xml/value_test.go new file mode 100644 index 0000000000..cbce967bd9 --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/value_test.go @@ -0,0 +1,212 @@ +package xml + +import ( + "bytes" + "fmt" + "math" + "math/big" + "strconv" + "testing" +) + +var ( + oneInt = new(big.Int).SetInt64(1) + oneFloat = new(big.Float).SetFloat64(1.0) +) + +func TestValue(t *testing.T) { + nested := StartElement{Name: Name{Local: "nested"}} + + cases := map[string]struct { + setter func(Value) + expected string + }{ + "string value": { + setter: func(value Value) { + value.String("foo") + }, + expected: `foo`, + }, + "string escaped": { + setter: func(value Value) { + value.String("{\"foo\":\"bar\"}") + }, + expected: fmt.Sprintf("{%sfoo%s:%sbar%s}", escQuot, escQuot, escQuot, escQuot), + }, + "integer": { + setter: func(value Value) { + value.Long(1024) + }, + expected: `1024`, + }, + "float": { + setter: func(value Value) { + value.Double(1e20) + }, + expected: `100000000000000000000`, + }, + "float exponent component": { + setter: func(value Value) { + value.Double(3e22) + }, + expected: `3e+22`, + }, + "boolean true": { + setter: func(value Value) { + value.Boolean(true) + }, + expected: `true`, + }, + "boolean false": { + setter: func(value Value) { + value.Boolean(false) + }, + expected: `false`, + }, + "encode bytes": { + setter: func(value Value) { + value.Base64EncodeBytes([]byte("foo bar")) + }, + expected: `Zm9vIGJhcg==`, + }, + "encode bytes nil": { + setter: func(value Value) { + value.Base64EncodeBytes(nil) + }, + expected: ``, + }, + "object": { + setter: func(value Value) { + defer value.Close() + value.MemberElement(nested).String("value") + }, + expected: `<nested>value</nested>`, + }, + "null": { + setter: func(value Value) { + value.Close() + }, + expected: ``, + }, + "nullWithRoot": { + setter: func(value Value) { + defer value.Close() + o := value.MemberElement(nested) + defer o.Close() + }, + expected: `<nested></nested>`, + }, + "write text": { + setter: func(value Value) { + defer value.Close() + o := value.MemberElement(nested) + o.Write([]byte(`{"nested":"value"}`), false) + }, + expected: `<nested>{"nested":"value"}</nested>`, + }, + "write escaped text": { + setter: func(value Value) { + defer value.Close() + o := value.MemberElement(nested) + o.Write([]byte(`{"nested":"value"}`), true) + }, + expected: fmt.Sprintf("<nested>{%snested%s:%svalue%s}</nested>", escQuot, escQuot, escQuot, escQuot), + }, + "bigInteger": { + setter: func(value Value) { + v := new(big.Int).SetInt64(math.MaxInt64) + value.BigInteger(v.Sub(v, oneInt)) + }, + expected: strconv.FormatInt(math.MaxInt64-1, 10), + }, + "bigInteger > int64": { + setter: func(value Value) { + v := new(big.Int).SetInt64(math.MaxInt64) + value.BigInteger(v.Add(v, oneInt)) + }, + expected: "9223372036854775808", + }, + "bigInteger < int64": { + setter: func(value Value) { + v := new(big.Int).SetInt64(math.MinInt64) + value.BigInteger(v.Sub(v, oneInt)) + }, + expected: "-9223372036854775809", + }, + "bigFloat": { + setter: func(value Value) { + v := new(big.Float).SetFloat64(math.MaxFloat64) + value.BigDecimal(v.Sub(v, oneFloat)) + }, + expected: strconv.FormatFloat(math.MaxFloat64-1, 'e', -1, 64), + }, + "bigFloat fits in int64": { + setter: func(value Value) { + v := new(big.Float).SetInt64(math.MaxInt64) + value.BigDecimal(v) + }, + expected: "9223372036854775807", + }, + } + scratch := make([]byte, 64) + + for name, tt := range cases { + t.Run(name, func(t *testing.T) { + b := bytes.NewBuffer(nil) + root := StartElement{Name: Name{Local: "root"}} + value := newValue(b, &scratch, root) + tt.setter(value) + + if e, a := []byte("<root>"+tt.expected+"</root>"), b.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } + }) + } +} + +func TestWrappedValue(t *testing.T) { + buffer := bytes.NewBuffer(nil) + scratch := make([]byte, 64) + + func() { + root := StartElement{Name: Name{Local: "root"}} + object := newValue(buffer, &scratch, root) + defer object.Close() + + foo := StartElement{Name: Name{Local: "foo"}} + faz := StartElement{Name: Name{Local: "faz"}} + + object.MemberElement(foo).String("bar") + object.MemberElement(faz).String("baz") + }() + + e := []byte(`<root><foo>bar</foo><faz>baz</faz></root>`) + if a := buffer.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } +} + +func TestWrappedValueWithNameSpaceAndAttributes(t *testing.T) { + buffer := bytes.NewBuffer(nil) + scratch := make([]byte, 64) + + func() { + root := StartElement{Name: Name{Local: "root"}} + object := newValue(buffer, &scratch, root) + defer object.Close() + + foo := StartElement{Name: Name{Local: "foo"}, Attr: []Attr{ + NewNamespaceAttribute("newspace", "https://endpoint.com"), + NewAttribute("attrName", "attrValue"), + }} + faz := StartElement{Name: Name{Local: "faz"}} + + object.MemberElement(foo).String("bar") + object.MemberElement(faz).String("baz") + }() + + e := []byte(`<root><foo xmlns:newspace="https://endpoint.com" attrName="attrValue">bar</foo><faz>baz</faz></root>`) + if a := buffer.Bytes(); bytes.Compare(e, a) != 0 { + t.Errorf("expected %+q, but got %+q", e, a) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/xml_decoder.go b/vendor/github.com/aws/smithy-go/encoding/xml/xml_decoder.go new file mode 100644 index 0000000000..dc4eebdffa --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/xml_decoder.go @@ -0,0 +1,154 @@ +package xml + +import ( + "encoding/xml" + "fmt" + "strings" +) + +// NodeDecoder is a XML decoder wrapper that is responsible to decoding +// a single XML Node element and it's nested member elements. This wrapper decoder +// takes in the start element of the top level node being decoded. +type NodeDecoder struct { + Decoder *xml.Decoder + StartEl xml.StartElement +} + +// WrapNodeDecoder returns an initialized XMLNodeDecoder +func WrapNodeDecoder(decoder *xml.Decoder, startEl xml.StartElement) NodeDecoder { + return NodeDecoder{ + Decoder: decoder, + StartEl: startEl, + } +} + +// Token on a Node Decoder returns a xml StartElement. It returns a boolean that indicates the +// a token is the node decoder's end node token; and an error which indicates any error +// that occurred while retrieving the start element +func (d NodeDecoder) Token() (t xml.StartElement, done bool, err error) { + for { + token, e := d.Decoder.Token() + if e != nil { + return t, done, e + } + + // check if we reach end of the node being decoded + if el, ok := token.(xml.EndElement); ok { + return t, el == d.StartEl.End(), err + } + + if t, ok := token.(xml.StartElement); ok { + return restoreAttrNamespaces(t), false, err + } + + // skip token if it is a comment or preamble or empty space value due to indentation + // or if it's a value and is not expected + } +} + +// restoreAttrNamespaces update XML attributes to restore the short namespaces found within +// the raw XML document. +func restoreAttrNamespaces(node xml.StartElement) xml.StartElement { + if len(node.Attr) == 0 { + return node + } + + // Generate a mapping of XML namespace values to their short names. + ns := map[string]string{} + for _, a := range node.Attr { + if a.Name.Space == "xmlns" { + ns[a.Value] = a.Name.Local + break + } + } + + for i, a := range node.Attr { + if a.Name.Space == "xmlns" { + continue + } + // By default, xml.Decoder will fully resolve these namespaces. So if you had <foo xmlns:bar=baz bar:bin=hi/> + // then by default the second attribute would have the `Name.Space` resolved to `baz`. But we need it to + // continue to resolve as `bar` so we can easily identify it later on. + if v, ok := ns[node.Attr[i].Name.Space]; ok { + node.Attr[i].Name.Space = v + } + } + return node +} + +// GetElement looks for the given tag name at the current level, and returns the element if found, and +// skipping over non-matching elements. Returns an error if the node is not found, or if an error occurs while walking +// the document. +func (d NodeDecoder) GetElement(name string) (t xml.StartElement, err error) { + for { + token, done, err := d.Token() + if err != nil { + return t, err + } + if done { + return t, fmt.Errorf("%s node not found", name) + } + switch { + case strings.EqualFold(name, token.Name.Local): + return token, nil + default: + err = d.Decoder.Skip() + if err != nil { + return t, err + } + } + } +} + +// Value provides an abstraction to retrieve char data value within an xml element. +// The method will return an error if it encounters a nested xml element instead of char data. +// This method should only be used to retrieve simple type or blob shape values as []byte. +func (d NodeDecoder) Value() (c []byte, err error) { + t, e := d.Decoder.Token() + if e != nil { + return c, e + } + + endElement := d.StartEl.End() + + switch ev := t.(type) { + case xml.CharData: + c = ev.Copy() + case xml.EndElement: // end tag or self-closing + if ev == endElement { + return []byte{}, err + } + return c, fmt.Errorf("expected value for %v element, got %T type %v instead", d.StartEl.Name.Local, t, t) + default: + return c, fmt.Errorf("expected value for %v element, got %T type %v instead", d.StartEl.Name.Local, t, t) + } + + t, e = d.Decoder.Token() + if e != nil { + return c, e + } + + if ev, ok := t.(xml.EndElement); ok { + if ev == endElement { + return c, err + } + } + + return c, fmt.Errorf("expected end element %v, got %T type %v instead", endElement, t, t) +} + +// FetchRootElement takes in a decoder and returns the first start element within the xml body. +// This function is useful in fetching the start element of an XML response and ignore the +// comments and preamble +func FetchRootElement(decoder *xml.Decoder) (startElement xml.StartElement, err error) { + for { + t, e := decoder.Token() + if e != nil { + return startElement, e + } + + if startElement, ok := t.(xml.StartElement); ok { + return startElement, err + } + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/xml_decoder_test.go b/vendor/github.com/aws/smithy-go/encoding/xml/xml_decoder_test.go new file mode 100644 index 0000000000..4c48fd218f --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/xml_decoder_test.go @@ -0,0 +1,329 @@ +package xml + +import ( + "bytes" + "encoding/xml" + "io" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestXMLNodeDecoder_Token(t *testing.T) { + cases := map[string]struct { + responseBody io.Reader + expectedStartElement xml.StartElement + expectedDone bool + expectedError string + }{ + "simple success case": { + responseBody: bytes.NewReader([]byte(`<Response>abc</Response>`)), + expectedStartElement: xml.StartElement{ + Name: xml.Name{ + Local: "", + }, + }, + expectedDone: true, + }, + "no value": { + responseBody: bytes.NewReader([]byte(`<Response></Response>`)), + expectedDone: true, + }, + "empty body": { + responseBody: bytes.NewReader([]byte(``)), + expectedError: "EOF", + }, + "with indentation": { + responseBody: bytes.NewReader([]byte(` <Response><Struct>abc</Struct></Response>`)), + expectedStartElement: xml.StartElement{ + Name: xml.Name{ + Local: "Struct", + }, + Attr: []xml.Attr{}, + }, + }, + "with comment and indentation": { + responseBody: bytes.NewReader([]byte(`<!--comment--> <Response><Struct>abc</Struct></Response>`)), + expectedStartElement: xml.StartElement{ + Name: xml.Name{ + Local: "Struct", + }, + Attr: []xml.Attr{}, + }, + }, + "attr with namespace": { + responseBody: bytes.NewReader([]byte(`<Response><Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser"></Grantee></Response>`)), + expectedStartElement: xml.StartElement{ + Name: xml.Name{ + Local: "Grantee", + }, + Attr: []xml.Attr{ + { + Name: xml.Name{ + Space: "xmlns", + Local: "xsi", + }, + Value: "http://www.w3.org/2001/XMLSchema-instance", + }, + { + Name: xml.Name{ + Space: "xsi", + Local: "type", + }, + Value: "CanonicalUser", + }, + }, + }, + }, + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + xmlDecoder := xml.NewDecoder(c.responseBody) + st, err := FetchRootElement(xmlDecoder) + if err != nil { + if len(c.expectedError) == 0 { + t.Fatalf("Expected no error, got %v", err) + } + + if e, a := c.expectedError, err; !strings.Contains(err.Error(), c.expectedError) { + t.Fatalf("expected error to contain %v, found %v", e, a.Error()) + } + } + nodeDecoder := WrapNodeDecoder(xmlDecoder, st) + token, done, err := nodeDecoder.Token() + if err != nil { + if len(c.expectedError) == 0 { + t.Fatalf("Expected no error, got %v", err) + } + + if e, a := c.expectedError, err; !strings.Contains(err.Error(), c.expectedError) { + t.Fatalf("expected error to contain %v, found %v", e, a.Error()) + } + } + + if e, a := c.expectedDone, done; e != a { + t.Fatalf("expected a valid end element token for the xml document, got none") + } + + if diff := cmp.Diff(c.expectedStartElement, token); len(diff) != 0 { + t.Fatalf("Found diff : (-expected,+actual), \n %v", diff) + } + }) + } +} + +func TestXMLNodeDecoder_TokenExample(t *testing.T) { + responseBody := bytes.NewReader([]byte(`<Struct><Response>abc</Response></Struct>`)) + + xmlDecoder := xml.NewDecoder(responseBody) + // Fetches <Struct> tag as start element. + st, err := FetchRootElement(xmlDecoder) + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + + // nodeDecoder will track <Struct> tag as root node of the document + nodeDecoder := WrapNodeDecoder(xmlDecoder, st) + + // Retrieves <Response> tag + token, done, err := nodeDecoder.Token() + if err != nil { + t.Fatalf("Expected no error, got %v", err) + + } + if diff := cmp.Diff(token, xml.StartElement{Name: xml.Name{Local: "Response"}, Attr: []xml.Attr{}}); len(diff) != 0 { + t.Fatalf("Found diff : (-expected,+actual), \n %v", diff) + } + if done { + t.Fatalf("expected decoding to not be done yet") + } + + // Skips the value and gets </Response> that is the end token of previously retrieved <Response> tag. + // The way node decoder works it only keeps track of the root start tag using which it was initialized. + // Here <Struct> is used to initialize, while</Response> is end element corresponding to already read + // <Response> tag. We won't be done until we receive </Struct> + token, done, err = nodeDecoder.Token() + if err != nil { + t.Fatalf("Expected no error, got %v", err) + + } + if diff := cmp.Diff(token, xml.StartElement{Name: xml.Name{Local: ""}, Attr: nil}); len(diff) != 0 { + t.Fatalf("Found diff : (-expected,+actual), \n %v", diff) + } + if done { + t.Fatalf("expected decoding to not be done yet") + } + + // Retrieves </Struct> end element tag corresponding to <Struct> tag. + // Since we got the end element that corresponds to the start element being track, we are done decoding. + token, done, err = nodeDecoder.Token() + if err != nil { + t.Fatalf("Expected no error, got %v", err) + + } + if diff := cmp.Diff(token, xml.StartElement{Name: xml.Name{Local: ""}, Attr: nil}); len(diff) != 0 { + t.Fatalf("Found diff : (-expected,+actual), \n %v", diff) + } + if !done { + t.Fatalf("expected decoding to be done as we fetched the end element </Struct>") + } +} + +func TestXMLNodeDecoder_Value(t *testing.T) { + cases := map[string]struct { + responseBody io.Reader + expectedValue []byte + expectedDone bool + expectedError string + }{ + "simple success case": { + responseBody: bytes.NewReader([]byte(`<Response>abc</Response>`)), + expectedValue: []byte(`abc`), + }, + "no value": { + responseBody: bytes.NewReader([]byte(`<Response></Response>`)), + expectedValue: []byte{}, + }, + "self-closing": { + responseBody: bytes.NewReader([]byte(`<Response />`)), + expectedValue: []byte{}, + }, + "empty body": { + responseBody: bytes.NewReader([]byte(``)), + expectedError: "EOF", + }, + "start element retrieved": { + responseBody: bytes.NewReader([]byte(`<Response><Struct>abc</Struct></Response>`)), + expectedError: "expected value for Response element, got xml.StartElement type", + }, + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + xmlDecoder := xml.NewDecoder(c.responseBody) + st, err := FetchRootElement(xmlDecoder) + if err != nil { + if len(c.expectedError) == 0 { + t.Fatalf("Expected no error, got %v", err) + } + + if e, a := c.expectedError, err; !strings.Contains(err.Error(), c.expectedError) { + t.Fatalf("expected error to contain %v, found %v", e, a.Error()) + } + } + nodeDecoder := WrapNodeDecoder(xmlDecoder, st) + token, err := nodeDecoder.Value() + if err != nil { + if len(c.expectedError) == 0 { + t.Fatalf("Expected no error, got %v", err) + } + + if e, a := c.expectedError, err; !strings.Contains(err.Error(), c.expectedError) { + t.Fatalf("expected error to contain %v, found %v", e, a.Error()) + } + } + + if diff := cmp.Diff(c.expectedValue, token); len(diff) != 0 { + t.Fatalf("Found diff : (-expected,+actual), \n %v", diff) + } + }) + } +} + +func Test_FetchXMLRootElement(t *testing.T) { + cases := map[string]struct { + responseBody io.Reader + expectedStartElement xml.StartElement + expectedError string + }{ + "simple success case": { + responseBody: bytes.NewReader([]byte(`<Response><Struct>abc</Struct></Response>`)), + expectedStartElement: xml.StartElement{ + Name: xml.Name{ + Local: "Response", + }, + Attr: []xml.Attr{}, + }, + }, + "empty body": { + responseBody: bytes.NewReader([]byte(``)), + expectedError: "EOF", + }, + "with indentation": { + responseBody: bytes.NewReader([]byte(` <ErrorResponse> + <Error> + <Type>Sender</Type> + <Code>InvalidGreeting</Code> + <Message>Hi</Message> + <AnotherSetting>setting</AnotherSetting> + </Error> + <RequestId>foo-id</RequestId> +</ErrorResponse>`)), + expectedStartElement: xml.StartElement{ + Name: xml.Name{ + Local: "ErrorResponse", + }, + Attr: []xml.Attr{}, + }, + }, + "with preamble": { + responseBody: bytes.NewReader([]byte(`<?xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> +<ErrorResponse> + <Error> + <Type>Sender</Type> + <Code>InvalidGreeting</Code> + <Message>Hi</Message> + <AnotherSetting>setting</AnotherSetting> + </Error> + <RequestId>foo-id</RequestId> +</ErrorResponse>`)), + expectedStartElement: xml.StartElement{ + Name: xml.Name{ + Local: "ErrorResponse", + }, + Attr: []xml.Attr{}, + }, + }, + "with comments": { + responseBody: bytes.NewReader([]byte(`<!--Sample comment for testing--> +<?xml version = "1.0" encoding = "UTF-8" standalone = "no" ?> +<ErrorResponse> + <Error> + <Type>Sender</Type> + <Code>InvalidGreeting</Code> + <Message>Hi</Message> + <AnotherSetting>setting</AnotherSetting> + </Error> + <RequestId>foo-id</RequestId> +</ErrorResponse>`)), + expectedStartElement: xml.StartElement{ + Name: xml.Name{ + Local: "ErrorResponse", + }, + Attr: []xml.Attr{}, + }, + }, + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + decoder := xml.NewDecoder(c.responseBody) + st, err := FetchRootElement(decoder) + if err != nil { + if len(c.expectedError) == 0 { + t.Fatalf("Expected no error, got %v", err) + } + + if e, a := c.expectedError, err; !strings.Contains(err.Error(), c.expectedError) { + t.Fatalf("expected error to contain %v, found %v", e, a.Error()) + } + } + + if diff := cmp.Diff(c.expectedStartElement, st); len(diff) != 0 { + t.Fatalf("Found diff : (-expected,+actual), \n %v", diff) + } + }) + } +} diff --git a/vendor/github.com/aws/smithy-go/encoding/xml/ya.make b/vendor/github.com/aws/smithy-go/encoding/xml/ya.make new file mode 100644 index 0000000000..8345e974fe --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/xml/ya.make @@ -0,0 +1,32 @@ +GO_LIBRARY() + +LICENSE(Apache-2.0) + +SRCS( + array.go + constants.go + doc.go + element.go + encoder.go + error_utils.go + escape.go + map.go + value.go + xml_decoder.go +) + +GO_TEST_SRCS( + array_test.go + error_utils_test.go + map_test.go + value_test.go + xml_decoder_test.go +) + +GO_XTEST_SRCS(encoder_test.go) + +END() + +RECURSE( + gotest +) diff --git a/vendor/github.com/aws/smithy-go/encoding/ya.make b/vendor/github.com/aws/smithy-go/encoding/ya.make new file mode 100644 index 0000000000..6212adb3bd --- /dev/null +++ b/vendor/github.com/aws/smithy-go/encoding/ya.make @@ -0,0 +1,16 @@ +GO_LIBRARY() + +LICENSE(Apache-2.0) + +SRCS( + doc.go + encoding.go +) + +END() + +RECURSE( + httpbinding + json + xml +) |