Merged in feature/s3integration (pull request #47)
Set Up S3 integration * cfginterfaceandfirsts3funcs * addlocalstack * generallypassesfullsuite * addedmultipleattemptedpings * cleanup * stabiliseplusskip
This commit is contained in:
+5
-13
@@ -11,21 +11,13 @@ func (oscState oscStringState) Handle(b byte) (s state, e error) {
|
||||
return nextState, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case isOscStringTerminator(b):
|
||||
// There are several control characters and sequences which can
|
||||
// terminate an OSC string. Most of them are handled by the baseState
|
||||
// handler. The ANSI_BEL character is a special case which behaves as a
|
||||
// terminator only for an OSC string.
|
||||
if b == ANSI_BEL {
|
||||
return oscState.parser.ground, nil
|
||||
}
|
||||
|
||||
return oscState, nil
|
||||
}
|
||||
|
||||
// See below for OSC string terminators for linux
|
||||
// http://man7.org/linux/man-pages/man4/console_codes.4.html
|
||||
func isOscStringTerminator(b byte) bool {
|
||||
|
||||
if b == ANSI_BEL || b == 0x5C {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
*.sublime-*
|
||||
.DS_Store
|
||||
*.swp
|
||||
*.swo
|
||||
tags
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- "1.10.x"
|
||||
- "1.11.x"
|
||||
- tip
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
Copyright (c) 2012, Martin Angers
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-188
@@ -1,188 +0,0 @@
|
||||
# Purell
|
||||
|
||||
Purell is a tiny Go library to normalize URLs. It returns a pure URL. Pure-ell. Sanitizer and all. Yeah, I know...
|
||||
|
||||
Based on the [wikipedia paper][wiki] and the [RFC 3986 document][rfc].
|
||||
|
||||
[](http://travis-ci.org/PuerkitoBio/purell)
|
||||
|
||||
## Install
|
||||
|
||||
`go get github.com/PuerkitoBio/purell`
|
||||
|
||||
## Changelog
|
||||
|
||||
* **v1.1.1** : Fix failing test due to Go1.12 changes (thanks to @ianlancetaylor).
|
||||
* **2016-11-14 (v1.1.0)** : IDN: Conform to RFC 5895: Fold character width (thanks to @beeker1121).
|
||||
* **2016-07-27 (v1.0.0)** : Normalize IDN to ASCII (thanks to @zenovich).
|
||||
* **2015-02-08** : Add fix for relative paths issue ([PR #5][pr5]) and add fix for unnecessary encoding of reserved characters ([see issue #7][iss7]).
|
||||
* **v0.2.0** : Add benchmarks, Attempt IDN support.
|
||||
* **v0.1.0** : Initial release.
|
||||
|
||||
## Examples
|
||||
|
||||
From `example_test.go` (note that in your code, you would import "github.com/PuerkitoBio/purell", and would prefix references to its methods and constants with "purell."):
|
||||
|
||||
```go
|
||||
package purell
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func ExampleNormalizeURLString() {
|
||||
if normalized, err := NormalizeURLString("hTTp://someWEBsite.com:80/Amazing%3f/url/",
|
||||
FlagLowercaseScheme|FlagLowercaseHost|FlagUppercaseEscapes); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Print(normalized)
|
||||
}
|
||||
// Output: http://somewebsite.com:80/Amazing%3F/url/
|
||||
}
|
||||
|
||||
func ExampleMustNormalizeURLString() {
|
||||
normalized := MustNormalizeURLString("hTTpS://someWEBsite.com:443/Amazing%fa/url/",
|
||||
FlagsUnsafeGreedy)
|
||||
fmt.Print(normalized)
|
||||
|
||||
// Output: http://somewebsite.com/Amazing%FA/url
|
||||
}
|
||||
|
||||
func ExampleNormalizeURL() {
|
||||
if u, err := url.Parse("Http://SomeUrl.com:8080/a/b/.././c///g?c=3&a=1&b=9&c=0#target"); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
normalized := NormalizeURL(u, FlagsUsuallySafeGreedy|FlagRemoveDuplicateSlashes|FlagRemoveFragment)
|
||||
fmt.Print(normalized)
|
||||
}
|
||||
|
||||
// Output: http://someurl.com:8080/a/c/g?c=3&a=1&b=9&c=0
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
As seen in the examples above, purell offers three methods, `NormalizeURLString(string, NormalizationFlags) (string, error)`, `MustNormalizeURLString(string, NormalizationFlags) (string)` and `NormalizeURL(*url.URL, NormalizationFlags) (string)`. They all normalize the provided URL based on the specified flags. Here are the available flags:
|
||||
|
||||
```go
|
||||
const (
|
||||
// Safe normalizations
|
||||
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
|
||||
FlagLowercaseHost // http://HOST -> http://host
|
||||
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
|
||||
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
|
||||
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
|
||||
FlagRemoveDefaultPort // http://host:80 -> http://host
|
||||
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
|
||||
|
||||
// Usually safe normalizations
|
||||
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
|
||||
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
|
||||
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
|
||||
|
||||
// Unsafe normalizations
|
||||
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
|
||||
FlagRemoveFragment // http://host/path#fragment -> http://host/path
|
||||
FlagForceHTTP // https://host -> http://host
|
||||
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
|
||||
FlagRemoveWWW // http://www.host/ -> http://host/
|
||||
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
|
||||
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
|
||||
|
||||
// Normalizations not in the wikipedia article, required to cover tests cases
|
||||
// submitted by jehiah
|
||||
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
|
||||
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
|
||||
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
|
||||
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
|
||||
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
|
||||
|
||||
// Convenience set of safe normalizations
|
||||
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
|
||||
|
||||
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
|
||||
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
|
||||
|
||||
// Convenience set of usually safe normalizations (includes FlagsSafe)
|
||||
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
|
||||
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
|
||||
|
||||
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
|
||||
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
|
||||
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
|
||||
|
||||
// Convenience set of all available flags
|
||||
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
||||
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
||||
)
|
||||
```
|
||||
|
||||
For convenience, the set of flags `FlagsSafe`, `FlagsUsuallySafe[Greedy|NonGreedy]`, `FlagsUnsafe[Greedy|NonGreedy]` and `FlagsAll[Greedy|NonGreedy]` are provided for the similarly grouped normalizations on [wikipedia's URL normalization page][wiki]. You can add (using the bitwise OR `|` operator) or remove (using the bitwise AND NOT `&^` operator) individual flags from the sets if required, to build your own custom set.
|
||||
|
||||
The [full godoc reference is available on gopkgdoc][godoc].
|
||||
|
||||
Some things to note:
|
||||
|
||||
* `FlagDecodeUnnecessaryEscapes`, `FlagEncodeNecessaryEscapes`, `FlagUppercaseEscapes` and `FlagRemoveEmptyQuerySeparator` are always implicitly set, because internally, the URL string is parsed as an URL object, which automatically decodes unnecessary escapes, uppercases and encodes necessary ones, and removes empty query separators (an unnecessary `?` at the end of the url). So this operation cannot **not** be done. For this reason, `FlagRemoveEmptyQuerySeparator` (as well as the other three) has been included in the `FlagsSafe` convenience set, instead of `FlagsUnsafe`, where Wikipedia puts it.
|
||||
|
||||
* The `FlagDecodeUnnecessaryEscapes` decodes the following escapes (*from -> to*):
|
||||
- %24 -> $
|
||||
- %26 -> &
|
||||
- %2B-%3B -> +,-./0123456789:;
|
||||
- %3D -> =
|
||||
- %40-%5A -> @ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
- %5F -> _
|
||||
- %61-%7A -> abcdefghijklmnopqrstuvwxyz
|
||||
- %7E -> ~
|
||||
|
||||
|
||||
* When the `NormalizeURL` function is used (passing an URL object), this source URL object is modified (that is, after the call, the URL object will be modified to reflect the normalization).
|
||||
|
||||
* The *replace IP with domain name* normalization (`http://208.77.188.166/ → http://www.example.com/`) is obviously not possible for a library without making some network requests. This is not implemented in purell.
|
||||
|
||||
* The *remove unused query string parameters* and *remove default query parameters* are also not implemented, since this is a very case-specific normalization, and it is quite trivial to do with an URL object.
|
||||
|
||||
### Safe vs Usually Safe vs Unsafe
|
||||
|
||||
Purell allows you to control the level of risk you take while normalizing an URL. You can aggressively normalize, play it totally safe, or anything in between.
|
||||
|
||||
Consider the following URL:
|
||||
|
||||
`HTTPS://www.RooT.com/toto/t%45%1f///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
|
||||
|
||||
Normalizing with the `FlagsSafe` gives:
|
||||
|
||||
`https://www.root.com/toto/tE%1F///a/./b/../c/?z=3&w=2&a=4&w=1#invalid`
|
||||
|
||||
With the `FlagsUsuallySafeGreedy`:
|
||||
|
||||
`https://www.root.com/toto/tE%1F///a/c?z=3&w=2&a=4&w=1#invalid`
|
||||
|
||||
And with `FlagsUnsafeGreedy`:
|
||||
|
||||
`http://root.com/toto/tE%1F/a/c?a=4&w=1&w=2&z=3`
|
||||
|
||||
## TODOs
|
||||
|
||||
* Add a class/default instance to allow specifying custom directory index names? At the moment, removing directory index removes `(^|/)((?:default|index)\.\w{1,4})$`.
|
||||
|
||||
## Thanks / Contributions
|
||||
|
||||
@rogpeppe
|
||||
@jehiah
|
||||
@opennota
|
||||
@pchristopher1275
|
||||
@zenovich
|
||||
@beeker1121
|
||||
|
||||
## License
|
||||
|
||||
The [BSD 3-Clause license][bsd].
|
||||
|
||||
[bsd]: http://opensource.org/licenses/BSD-3-Clause
|
||||
[wiki]: http://en.wikipedia.org/wiki/URL_normalization
|
||||
[rfc]: http://tools.ietf.org/html/rfc3986#section-6
|
||||
[godoc]: http://go.pkgdoc.org/github.com/PuerkitoBio/purell
|
||||
[pr5]: https://github.com/PuerkitoBio/purell/pull/5
|
||||
[iss7]: https://github.com/PuerkitoBio/purell/issues/7
|
||||
-379
@@ -1,379 +0,0 @@
|
||||
/*
|
||||
Package purell offers URL normalization as described on the wikipedia page:
|
||||
http://en.wikipedia.org/wiki/URL_normalization
|
||||
*/
|
||||
package purell
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/urlesc"
|
||||
"golang.org/x/net/idna"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
"golang.org/x/text/width"
|
||||
)
|
||||
|
||||
// A set of normalization flags determines how a URL will
|
||||
// be normalized.
|
||||
type NormalizationFlags uint
|
||||
|
||||
const (
|
||||
// Safe normalizations
|
||||
FlagLowercaseScheme NormalizationFlags = 1 << iota // HTTP://host -> http://host, applied by default in Go1.1
|
||||
FlagLowercaseHost // http://HOST -> http://host
|
||||
FlagUppercaseEscapes // http://host/t%ef -> http://host/t%EF
|
||||
FlagDecodeUnnecessaryEscapes // http://host/t%41 -> http://host/tA
|
||||
FlagEncodeNecessaryEscapes // http://host/!"#$ -> http://host/%21%22#$
|
||||
FlagRemoveDefaultPort // http://host:80 -> http://host
|
||||
FlagRemoveEmptyQuerySeparator // http://host/path? -> http://host/path
|
||||
|
||||
// Usually safe normalizations
|
||||
FlagRemoveTrailingSlash // http://host/path/ -> http://host/path
|
||||
FlagAddTrailingSlash // http://host/path -> http://host/path/ (should choose only one of these add/remove trailing slash flags)
|
||||
FlagRemoveDotSegments // http://host/path/./a/b/../c -> http://host/path/a/c
|
||||
|
||||
// Unsafe normalizations
|
||||
FlagRemoveDirectoryIndex // http://host/path/index.html -> http://host/path/
|
||||
FlagRemoveFragment // http://host/path#fragment -> http://host/path
|
||||
FlagForceHTTP // https://host -> http://host
|
||||
FlagRemoveDuplicateSlashes // http://host/path//a///b -> http://host/path/a/b
|
||||
FlagRemoveWWW // http://www.host/ -> http://host/
|
||||
FlagAddWWW // http://host/ -> http://www.host/ (should choose only one of these add/remove WWW flags)
|
||||
FlagSortQuery // http://host/path?c=3&b=2&a=1&b=1 -> http://host/path?a=1&b=1&b=2&c=3
|
||||
|
||||
// Normalizations not in the wikipedia article, required to cover tests cases
|
||||
// submitted by jehiah
|
||||
FlagDecodeDWORDHost // http://1113982867 -> http://66.102.7.147
|
||||
FlagDecodeOctalHost // http://0102.0146.07.0223 -> http://66.102.7.147
|
||||
FlagDecodeHexHost // http://0x42660793 -> http://66.102.7.147
|
||||
FlagRemoveUnnecessaryHostDots // http://.host../path -> http://host/path
|
||||
FlagRemoveEmptyPortSeparator // http://host:/path -> http://host/path
|
||||
|
||||
// Convenience set of safe normalizations
|
||||
FlagsSafe NormalizationFlags = FlagLowercaseHost | FlagLowercaseScheme | FlagUppercaseEscapes | FlagDecodeUnnecessaryEscapes | FlagEncodeNecessaryEscapes | FlagRemoveDefaultPort | FlagRemoveEmptyQuerySeparator
|
||||
|
||||
// For convenience sets, "greedy" uses the "remove trailing slash" and "remove www. prefix" flags,
|
||||
// while "non-greedy" uses the "add (or keep) the trailing slash" and "add www. prefix".
|
||||
|
||||
// Convenience set of usually safe normalizations (includes FlagsSafe)
|
||||
FlagsUsuallySafeGreedy NormalizationFlags = FlagsSafe | FlagRemoveTrailingSlash | FlagRemoveDotSegments
|
||||
FlagsUsuallySafeNonGreedy NormalizationFlags = FlagsSafe | FlagAddTrailingSlash | FlagRemoveDotSegments
|
||||
|
||||
// Convenience set of unsafe normalizations (includes FlagsUsuallySafe)
|
||||
FlagsUnsafeGreedy NormalizationFlags = FlagsUsuallySafeGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagRemoveWWW | FlagSortQuery
|
||||
FlagsUnsafeNonGreedy NormalizationFlags = FlagsUsuallySafeNonGreedy | FlagRemoveDirectoryIndex | FlagRemoveFragment | FlagForceHTTP | FlagRemoveDuplicateSlashes | FlagAddWWW | FlagSortQuery
|
||||
|
||||
// Convenience set of all available flags
|
||||
FlagsAllGreedy = FlagsUnsafeGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
||||
FlagsAllNonGreedy = FlagsUnsafeNonGreedy | FlagDecodeDWORDHost | FlagDecodeOctalHost | FlagDecodeHexHost | FlagRemoveUnnecessaryHostDots | FlagRemoveEmptyPortSeparator
|
||||
)
|
||||
|
||||
const (
|
||||
defaultHttpPort = ":80"
|
||||
defaultHttpsPort = ":443"
|
||||
)
|
||||
|
||||
// Regular expressions used by the normalizations
|
||||
var rxPort = regexp.MustCompile(`(:\d+)/?$`)
|
||||
var rxDirIndex = regexp.MustCompile(`(^|/)((?:default|index)\.\w{1,4})$`)
|
||||
var rxDupSlashes = regexp.MustCompile(`/{2,}`)
|
||||
var rxDWORDHost = regexp.MustCompile(`^(\d+)((?:\.+)?(?:\:\d*)?)$`)
|
||||
var rxOctalHost = regexp.MustCompile(`^(0\d*)\.(0\d*)\.(0\d*)\.(0\d*)((?:\.+)?(?:\:\d*)?)$`)
|
||||
var rxHexHost = regexp.MustCompile(`^0x([0-9A-Fa-f]+)((?:\.+)?(?:\:\d*)?)$`)
|
||||
var rxHostDots = regexp.MustCompile(`^(.+?)(:\d+)?$`)
|
||||
var rxEmptyPort = regexp.MustCompile(`:+$`)
|
||||
|
||||
// Map of flags to implementation function.
|
||||
// FlagDecodeUnnecessaryEscapes has no action, since it is done automatically
|
||||
// by parsing the string as an URL. Same for FlagUppercaseEscapes and FlagRemoveEmptyQuerySeparator.
|
||||
|
||||
// Since maps have undefined traversing order, make a slice of ordered keys
|
||||
var flagsOrder = []NormalizationFlags{
|
||||
FlagLowercaseScheme,
|
||||
FlagLowercaseHost,
|
||||
FlagRemoveDefaultPort,
|
||||
FlagRemoveDirectoryIndex,
|
||||
FlagRemoveDotSegments,
|
||||
FlagRemoveFragment,
|
||||
FlagForceHTTP, // Must be after remove default port (because https=443/http=80)
|
||||
FlagRemoveDuplicateSlashes,
|
||||
FlagRemoveWWW,
|
||||
FlagAddWWW,
|
||||
FlagSortQuery,
|
||||
FlagDecodeDWORDHost,
|
||||
FlagDecodeOctalHost,
|
||||
FlagDecodeHexHost,
|
||||
FlagRemoveUnnecessaryHostDots,
|
||||
FlagRemoveEmptyPortSeparator,
|
||||
FlagRemoveTrailingSlash, // These two (add/remove trailing slash) must be last
|
||||
FlagAddTrailingSlash,
|
||||
}
|
||||
|
||||
// ... and then the map, where order is unimportant
|
||||
var flags = map[NormalizationFlags]func(*url.URL){
|
||||
FlagLowercaseScheme: lowercaseScheme,
|
||||
FlagLowercaseHost: lowercaseHost,
|
||||
FlagRemoveDefaultPort: removeDefaultPort,
|
||||
FlagRemoveDirectoryIndex: removeDirectoryIndex,
|
||||
FlagRemoveDotSegments: removeDotSegments,
|
||||
FlagRemoveFragment: removeFragment,
|
||||
FlagForceHTTP: forceHTTP,
|
||||
FlagRemoveDuplicateSlashes: removeDuplicateSlashes,
|
||||
FlagRemoveWWW: removeWWW,
|
||||
FlagAddWWW: addWWW,
|
||||
FlagSortQuery: sortQuery,
|
||||
FlagDecodeDWORDHost: decodeDWORDHost,
|
||||
FlagDecodeOctalHost: decodeOctalHost,
|
||||
FlagDecodeHexHost: decodeHexHost,
|
||||
FlagRemoveUnnecessaryHostDots: removeUnncessaryHostDots,
|
||||
FlagRemoveEmptyPortSeparator: removeEmptyPortSeparator,
|
||||
FlagRemoveTrailingSlash: removeTrailingSlash,
|
||||
FlagAddTrailingSlash: addTrailingSlash,
|
||||
}
|
||||
|
||||
// MustNormalizeURLString returns the normalized string, and panics if an error occurs.
|
||||
// It takes an URL string as input, as well as the normalization flags.
|
||||
func MustNormalizeURLString(u string, f NormalizationFlags) string {
|
||||
result, e := NormalizeURLString(u, f)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// NormalizeURLString returns the normalized string, or an error if it can't be parsed into an URL object.
|
||||
// It takes an URL string as input, as well as the normalization flags.
|
||||
func NormalizeURLString(u string, f NormalizationFlags) (string, error) {
|
||||
parsed, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if f&FlagLowercaseHost == FlagLowercaseHost {
|
||||
parsed.Host = strings.ToLower(parsed.Host)
|
||||
}
|
||||
|
||||
// The idna package doesn't fully conform to RFC 5895
|
||||
// (https://tools.ietf.org/html/rfc5895), so we do it here.
|
||||
// Taken from Go 1.8 cycle source, courtesy of bradfitz.
|
||||
// TODO: Remove when (if?) idna package conforms to RFC 5895.
|
||||
parsed.Host = width.Fold.String(parsed.Host)
|
||||
parsed.Host = norm.NFC.String(parsed.Host)
|
||||
if parsed.Host, err = idna.ToASCII(parsed.Host); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return NormalizeURL(parsed, f), nil
|
||||
}
|
||||
|
||||
// NormalizeURL returns the normalized string.
|
||||
// It takes a parsed URL object as input, as well as the normalization flags.
|
||||
func NormalizeURL(u *url.URL, f NormalizationFlags) string {
|
||||
for _, k := range flagsOrder {
|
||||
if f&k == k {
|
||||
flags[k](u)
|
||||
}
|
||||
}
|
||||
return urlesc.Escape(u)
|
||||
}
|
||||
|
||||
func lowercaseScheme(u *url.URL) {
|
||||
if len(u.Scheme) > 0 {
|
||||
u.Scheme = strings.ToLower(u.Scheme)
|
||||
}
|
||||
}
|
||||
|
||||
func lowercaseHost(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
u.Host = strings.ToLower(u.Host)
|
||||
}
|
||||
}
|
||||
|
||||
func removeDefaultPort(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
scheme := strings.ToLower(u.Scheme)
|
||||
u.Host = rxPort.ReplaceAllStringFunc(u.Host, func(val string) string {
|
||||
if (scheme == "http" && val == defaultHttpPort) || (scheme == "https" && val == defaultHttpsPort) {
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func removeTrailingSlash(u *url.URL) {
|
||||
if l := len(u.Path); l > 0 {
|
||||
if strings.HasSuffix(u.Path, "/") {
|
||||
u.Path = u.Path[:l-1]
|
||||
}
|
||||
} else if l = len(u.Host); l > 0 {
|
||||
if strings.HasSuffix(u.Host, "/") {
|
||||
u.Host = u.Host[:l-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addTrailingSlash(u *url.URL) {
|
||||
if l := len(u.Path); l > 0 {
|
||||
if !strings.HasSuffix(u.Path, "/") {
|
||||
u.Path += "/"
|
||||
}
|
||||
} else if l = len(u.Host); l > 0 {
|
||||
if !strings.HasSuffix(u.Host, "/") {
|
||||
u.Host += "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeDotSegments(u *url.URL) {
|
||||
if len(u.Path) > 0 {
|
||||
var dotFree []string
|
||||
var lastIsDot bool
|
||||
|
||||
sections := strings.Split(u.Path, "/")
|
||||
for _, s := range sections {
|
||||
if s == ".." {
|
||||
if len(dotFree) > 0 {
|
||||
dotFree = dotFree[:len(dotFree)-1]
|
||||
}
|
||||
} else if s != "." {
|
||||
dotFree = append(dotFree, s)
|
||||
}
|
||||
lastIsDot = (s == "." || s == "..")
|
||||
}
|
||||
// Special case if host does not end with / and new path does not begin with /
|
||||
u.Path = strings.Join(dotFree, "/")
|
||||
if u.Host != "" && !strings.HasSuffix(u.Host, "/") && !strings.HasPrefix(u.Path, "/") {
|
||||
u.Path = "/" + u.Path
|
||||
}
|
||||
// Special case if the last segment was a dot, make sure the path ends with a slash
|
||||
if lastIsDot && !strings.HasSuffix(u.Path, "/") {
|
||||
u.Path += "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeDirectoryIndex(u *url.URL) {
|
||||
if len(u.Path) > 0 {
|
||||
u.Path = rxDirIndex.ReplaceAllString(u.Path, "$1")
|
||||
}
|
||||
}
|
||||
|
||||
func removeFragment(u *url.URL) {
|
||||
u.Fragment = ""
|
||||
}
|
||||
|
||||
func forceHTTP(u *url.URL) {
|
||||
if strings.ToLower(u.Scheme) == "https" {
|
||||
u.Scheme = "http"
|
||||
}
|
||||
}
|
||||
|
||||
func removeDuplicateSlashes(u *url.URL) {
|
||||
if len(u.Path) > 0 {
|
||||
u.Path = rxDupSlashes.ReplaceAllString(u.Path, "/")
|
||||
}
|
||||
}
|
||||
|
||||
func removeWWW(u *url.URL) {
|
||||
if len(u.Host) > 0 && strings.HasPrefix(strings.ToLower(u.Host), "www.") {
|
||||
u.Host = u.Host[4:]
|
||||
}
|
||||
}
|
||||
|
||||
func addWWW(u *url.URL) {
|
||||
if len(u.Host) > 0 && !strings.HasPrefix(strings.ToLower(u.Host), "www.") {
|
||||
u.Host = "www." + u.Host
|
||||
}
|
||||
}
|
||||
|
||||
func sortQuery(u *url.URL) {
|
||||
q := u.Query()
|
||||
|
||||
if len(q) > 0 {
|
||||
arKeys := make([]string, len(q))
|
||||
i := 0
|
||||
for k := range q {
|
||||
arKeys[i] = k
|
||||
i++
|
||||
}
|
||||
sort.Strings(arKeys)
|
||||
buf := new(bytes.Buffer)
|
||||
for _, k := range arKeys {
|
||||
sort.Strings(q[k])
|
||||
for _, v := range q[k] {
|
||||
if buf.Len() > 0 {
|
||||
buf.WriteRune('&')
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s=%s", k, urlesc.QueryEscape(v)))
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild the raw query string
|
||||
u.RawQuery = buf.String()
|
||||
}
|
||||
}
|
||||
|
||||
func decodeDWORDHost(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
if matches := rxDWORDHost.FindStringSubmatch(u.Host); len(matches) > 2 {
|
||||
var parts [4]int64
|
||||
|
||||
dword, _ := strconv.ParseInt(matches[1], 10, 0)
|
||||
for i, shift := range []uint{24, 16, 8, 0} {
|
||||
parts[i] = dword >> shift & 0xFF
|
||||
}
|
||||
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeOctalHost(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
if matches := rxOctalHost.FindStringSubmatch(u.Host); len(matches) > 5 {
|
||||
var parts [4]int64
|
||||
|
||||
for i := 1; i <= 4; i++ {
|
||||
parts[i-1], _ = strconv.ParseInt(matches[i], 8, 0)
|
||||
}
|
||||
u.Host = fmt.Sprintf("%d.%d.%d.%d%s", parts[0], parts[1], parts[2], parts[3], matches[5])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeHexHost(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
if matches := rxHexHost.FindStringSubmatch(u.Host); len(matches) > 2 {
|
||||
// Conversion is safe because of regex validation
|
||||
parsed, _ := strconv.ParseInt(matches[1], 16, 0)
|
||||
// Set host as DWORD (base 10) encoded host
|
||||
u.Host = fmt.Sprintf("%d%s", parsed, matches[2])
|
||||
// The rest is the same as decoding a DWORD host
|
||||
decodeDWORDHost(u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeUnncessaryHostDots(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
if matches := rxHostDots.FindStringSubmatch(u.Host); len(matches) > 1 {
|
||||
// Trim the leading and trailing dots
|
||||
u.Host = strings.Trim(matches[1], ".")
|
||||
if len(matches) > 2 {
|
||||
u.Host += matches[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeEmptyPortSeparator(u *url.URL) {
|
||||
if len(u.Host) > 0 {
|
||||
u.Host = rxEmptyPort.ReplaceAllString(u.Host, "")
|
||||
}
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- tip
|
||||
|
||||
install:
|
||||
- go build .
|
||||
|
||||
script:
|
||||
- go test -v
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
urlesc [](https://travis-ci.org/PuerkitoBio/urlesc) [](http://godoc.org/github.com/PuerkitoBio/urlesc)
|
||||
======
|
||||
|
||||
Package urlesc implements query escaping as per RFC 3986.
|
||||
|
||||
It contains some parts of the net/url package, modified so as to allow
|
||||
some reserved characters incorrectly escaped by net/url (see [issue 5684](https://github.com/golang/go/issues/5684)).
|
||||
|
||||
## Install
|
||||
|
||||
go get github.com/PuerkitoBio/urlesc
|
||||
|
||||
## License
|
||||
|
||||
Go license (BSD-3-Clause)
|
||||
|
||||
-180
@@ -1,180 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// Package urlesc implements query escaping as per RFC 3986.
|
||||
// It contains some parts of the net/url package, modified so as to allow
|
||||
// some reserved characters incorrectly escaped by net/url.
|
||||
// See https://github.com/golang/go/issues/5684
|
||||
package urlesc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type encoding int
|
||||
|
||||
const (
|
||||
encodePath encoding = 1 + iota
|
||||
encodeUserPassword
|
||||
encodeQueryComponent
|
||||
encodeFragment
|
||||
)
|
||||
|
||||
// Return true if the specified character should be escaped when
|
||||
// appearing in a URL string, according to RFC 3986.
|
||||
func shouldEscape(c byte, mode encoding) bool {
|
||||
// §2.3 Unreserved characters (alphanum)
|
||||
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
|
||||
return false
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '-', '.', '_', '~': // §2.3 Unreserved characters (mark)
|
||||
return false
|
||||
|
||||
// §2.2 Reserved characters (reserved)
|
||||
case ':', '/', '?', '#', '[', ']', '@', // gen-delims
|
||||
'!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=': // sub-delims
|
||||
// Different sections of the URL allow a few of
|
||||
// the reserved characters to appear unescaped.
|
||||
switch mode {
|
||||
case encodePath: // §3.3
|
||||
// The RFC allows sub-delims and : @.
|
||||
// '/', '[' and ']' can be used to assign meaning to individual path
|
||||
// segments. This package only manipulates the path as a whole,
|
||||
// so we allow those as well. That leaves only ? and # to escape.
|
||||
return c == '?' || c == '#'
|
||||
|
||||
case encodeUserPassword: // §3.2.1
|
||||
// The RFC allows : and sub-delims in
|
||||
// userinfo. The parsing of userinfo treats ':' as special so we must escape
|
||||
// all the gen-delims.
|
||||
return c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@'
|
||||
|
||||
case encodeQueryComponent: // §3.4
|
||||
// The RFC allows / and ?.
|
||||
return c != '/' && c != '?'
|
||||
|
||||
case encodeFragment: // §4.1
|
||||
// The RFC text is silent but the grammar allows
|
||||
// everything, so escape nothing but #
|
||||
return c == '#'
|
||||
}
|
||||
}
|
||||
|
||||
// Everything else must be escaped.
|
||||
return true
|
||||
}
|
||||
|
||||
// QueryEscape escapes the string so it can be safely placed
|
||||
// inside a URL query.
|
||||
func QueryEscape(s string) string {
|
||||
return escape(s, encodeQueryComponent)
|
||||
}
|
||||
|
||||
func escape(s string, mode encoding) string {
|
||||
spaceCount, hexCount := 0, 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if shouldEscape(c, mode) {
|
||||
if c == ' ' && mode == encodeQueryComponent {
|
||||
spaceCount++
|
||||
} else {
|
||||
hexCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if spaceCount == 0 && hexCount == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
t := make([]byte, len(s)+2*hexCount)
|
||||
j := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch c := s[i]; {
|
||||
case c == ' ' && mode == encodeQueryComponent:
|
||||
t[j] = '+'
|
||||
j++
|
||||
case shouldEscape(c, mode):
|
||||
t[j] = '%'
|
||||
t[j+1] = "0123456789ABCDEF"[c>>4]
|
||||
t[j+2] = "0123456789ABCDEF"[c&15]
|
||||
j += 3
|
||||
default:
|
||||
t[j] = s[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
return string(t)
|
||||
}
|
||||
|
||||
var uiReplacer = strings.NewReplacer(
|
||||
"%21", "!",
|
||||
"%27", "'",
|
||||
"%28", "(",
|
||||
"%29", ")",
|
||||
"%2A", "*",
|
||||
)
|
||||
|
||||
// unescapeUserinfo unescapes some characters that need not to be escaped as per RFC3986.
|
||||
func unescapeUserinfo(s string) string {
|
||||
return uiReplacer.Replace(s)
|
||||
}
|
||||
|
||||
// Escape reassembles the URL into a valid URL string.
|
||||
// The general form of the result is one of:
|
||||
//
|
||||
// scheme:opaque
|
||||
// scheme://userinfo@host/path?query#fragment
|
||||
//
|
||||
// If u.Opaque is non-empty, String uses the first form;
|
||||
// otherwise it uses the second form.
|
||||
//
|
||||
// In the second form, the following rules apply:
|
||||
// - if u.Scheme is empty, scheme: is omitted.
|
||||
// - if u.User is nil, userinfo@ is omitted.
|
||||
// - if u.Host is empty, host/ is omitted.
|
||||
// - if u.Scheme and u.Host are empty and u.User is nil,
|
||||
// the entire scheme://userinfo@host/ is omitted.
|
||||
// - if u.Host is non-empty and u.Path begins with a /,
|
||||
// the form host/path does not add its own /.
|
||||
// - if u.RawQuery is empty, ?query is omitted.
|
||||
// - if u.Fragment is empty, #fragment is omitted.
|
||||
func Escape(u *url.URL) string {
|
||||
var buf bytes.Buffer
|
||||
if u.Scheme != "" {
|
||||
buf.WriteString(u.Scheme)
|
||||
buf.WriteByte(':')
|
||||
}
|
||||
if u.Opaque != "" {
|
||||
buf.WriteString(u.Opaque)
|
||||
} else {
|
||||
if u.Scheme != "" || u.Host != "" || u.User != nil {
|
||||
buf.WriteString("//")
|
||||
if ui := u.User; ui != nil {
|
||||
buf.WriteString(unescapeUserinfo(ui.String()))
|
||||
buf.WriteByte('@')
|
||||
}
|
||||
if h := u.Host; h != "" {
|
||||
buf.WriteString(h)
|
||||
}
|
||||
}
|
||||
if u.Path != "" && u.Path[0] != '/' && u.Host != "" {
|
||||
buf.WriteByte('/')
|
||||
}
|
||||
buf.WriteString(escape(u.Path, encodePath))
|
||||
}
|
||||
if u.RawQuery != "" {
|
||||
buf.WriteByte('?')
|
||||
buf.WriteString(u.RawQuery)
|
||||
}
|
||||
if u.Fragment != "" {
|
||||
buf.WriteByte('#')
|
||||
buf.WriteString(escape(u.Fragment, encodeFragment))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// Package arn provides a parser for interacting with Amazon Resource Names.
|
||||
package arn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
arnDelimiter = ":"
|
||||
arnSections = 6
|
||||
arnPrefix = "arn:"
|
||||
|
||||
// zero-indexed
|
||||
sectionPartition = 1
|
||||
sectionService = 2
|
||||
sectionRegion = 3
|
||||
sectionAccountID = 4
|
||||
sectionResource = 5
|
||||
|
||||
// errors
|
||||
invalidPrefix = "arn: invalid prefix"
|
||||
invalidSections = "arn: not enough sections"
|
||||
)
|
||||
|
||||
// ARN captures the individual fields of an Amazon Resource Name.
|
||||
// See http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html for more information.
|
||||
type ARN struct {
|
||||
// The partition that the resource is in. For standard AWS regions, the partition is "aws". If you have resources in
|
||||
// other partitions, the partition is "aws-partitionname". For example, the partition for resources in the China
|
||||
// (Beijing) region is "aws-cn".
|
||||
Partition string
|
||||
|
||||
// The service namespace that identifies the AWS product (for example, Amazon S3, IAM, or Amazon RDS). For a list of
|
||||
// namespaces, see
|
||||
// http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#genref-aws-service-namespaces.
|
||||
Service string
|
||||
|
||||
// The region the resource resides in. Note that the ARNs for some resources do not require a region, so this
|
||||
// component might be omitted.
|
||||
Region string
|
||||
|
||||
// The ID of the AWS account that owns the resource, without the hyphens. For example, 123456789012. Note that the
|
||||
// ARNs for some resources don't require an account number, so this component might be omitted.
|
||||
AccountID string
|
||||
|
||||
// The content of this part of the ARN varies by service. It often includes an indicator of the type of resource —
|
||||
// for example, an IAM user or Amazon RDS database - followed by a slash (/) or a colon (:), followed by the
|
||||
// resource name itself. Some services allows paths for resource names, as described in
|
||||
// http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#arns-paths.
|
||||
Resource string
|
||||
}
|
||||
|
||||
// Parse parses an ARN into its constituent parts.
|
||||
//
|
||||
// Some example ARNs:
|
||||
// arn:aws:elasticbeanstalk:us-east-1:123456789012:environment/My App/MyEnvironment
|
||||
// arn:aws:iam::123456789012:user/David
|
||||
// arn:aws:rds:eu-west-1:123456789012:db:mysql-db
|
||||
// arn:aws:s3:::my_corporate_bucket/exampleobject.png
|
||||
func Parse(arn string) (ARN, error) {
|
||||
if !strings.HasPrefix(arn, arnPrefix) {
|
||||
return ARN{}, errors.New(invalidPrefix)
|
||||
}
|
||||
sections := strings.SplitN(arn, arnDelimiter, arnSections)
|
||||
if len(sections) != arnSections {
|
||||
return ARN{}, errors.New(invalidSections)
|
||||
}
|
||||
return ARN{
|
||||
Partition: sections[sectionPartition],
|
||||
Service: sections[sectionService],
|
||||
Region: sections[sectionRegion],
|
||||
AccountID: sections[sectionAccountID],
|
||||
Resource: sections[sectionResource],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsARN returns whether the given string is an arn
|
||||
// by looking for whether the string starts with arn:
|
||||
func IsARN(arn string) bool {
|
||||
return strings.HasPrefix(arn, arnPrefix) && strings.Count(arn, ":") >= arnSections-1
|
||||
}
|
||||
|
||||
// String returns the canonical representation of the ARN
|
||||
func (arn ARN) String() string {
|
||||
return arnPrefix +
|
||||
arn.Partition + arnDelimiter +
|
||||
arn.Service + arnDelimiter +
|
||||
arn.Region + arnDelimiter +
|
||||
arn.AccountID + arnDelimiter +
|
||||
arn.Resource
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
package aws
|
||||
|
||||
// RequestChecksumCalculation controls request checksum calculation workflow
|
||||
type RequestChecksumCalculation int
|
||||
|
||||
const (
|
||||
// RequestChecksumCalculationUnset is the unset value for RequestChecksumCalculation
|
||||
RequestChecksumCalculationUnset RequestChecksumCalculation = iota
|
||||
|
||||
// RequestChecksumCalculationWhenSupported indicates request checksum will be calculated
|
||||
// if the operation supports input checksums
|
||||
RequestChecksumCalculationWhenSupported
|
||||
|
||||
// RequestChecksumCalculationWhenRequired indicates request checksum will be calculated
|
||||
// if required by the operation or if user elects to set a checksum algorithm in request
|
||||
RequestChecksumCalculationWhenRequired
|
||||
)
|
||||
|
||||
// ResponseChecksumValidation controls response checksum validation workflow
|
||||
type ResponseChecksumValidation int
|
||||
|
||||
const (
|
||||
// ResponseChecksumValidationUnset is the unset value for ResponseChecksumValidation
|
||||
ResponseChecksumValidationUnset ResponseChecksumValidation = iota
|
||||
|
||||
// ResponseChecksumValidationWhenSupported indicates response checksum will be validated
|
||||
// if the operation supports output checksums
|
||||
ResponseChecksumValidationWhenSupported
|
||||
|
||||
// ResponseChecksumValidationWhenRequired indicates response checksum will only
|
||||
// be validated if the operation requires output checksum validation
|
||||
ResponseChecksumValidationWhenRequired
|
||||
)
|
||||
+27
@@ -165,6 +165,33 @@ type Config struct {
|
||||
|
||||
// Controls how a resolved AWS account ID is handled for endpoint routing.
|
||||
AccountIDEndpointMode AccountIDEndpointMode
|
||||
|
||||
// RequestChecksumCalculation determines when request checksum calculation is performed.
|
||||
//
|
||||
// There are two possible values for this setting:
|
||||
//
|
||||
// 1. RequestChecksumCalculationWhenSupported (default): The checksum is always calculated
|
||||
// if the operation supports it, regardless of whether the user sets an algorithm in the request.
|
||||
//
|
||||
// 2. RequestChecksumCalculationWhenRequired: The checksum is only calculated if the user
|
||||
// explicitly sets a checksum algorithm in the request.
|
||||
//
|
||||
// This setting is sourced from the environment variable AWS_REQUEST_CHECKSUM_CALCULATION
|
||||
// or the shared config profile attribute "request_checksum_calculation".
|
||||
RequestChecksumCalculation RequestChecksumCalculation
|
||||
|
||||
// ResponseChecksumValidation determines when response checksum validation is performed
|
||||
//
|
||||
// There are two possible values for this setting:
|
||||
//
|
||||
// 1. ResponseChecksumValidationWhenSupported (default): The checksum is always validated
|
||||
// if the operation supports it, regardless of whether the user sets the validation mode to ENABLED in request.
|
||||
//
|
||||
// 2. ResponseChecksumValidationWhenRequired: The checksum is only validated if the user
|
||||
// explicitly sets the validation mode to ENABLED in the request
|
||||
// This variable is sourced from environment variable AWS_RESPONSE_CHECKSUM_VALIDATION or
|
||||
// the shared config profile attribute "response_checksum_validation".
|
||||
ResponseChecksumValidation ResponseChecksumValidation
|
||||
}
|
||||
|
||||
// NewConfig returns a new Config pointer that can be chained with builder
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
package aws
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.32.5"
|
||||
const goModuleVersion = "1.36.0"
|
||||
|
||||
+31
-14
@@ -34,6 +34,9 @@ const (
|
||||
FeatureMetadata2
|
||||
)
|
||||
|
||||
// Hardcoded value to specify which version of the user agent we're using
|
||||
const uaMetadata = "ua/2.1"
|
||||
|
||||
func (k SDKAgentKeyType) string() string {
|
||||
switch k {
|
||||
case APIMetadata:
|
||||
@@ -73,19 +76,28 @@ type UserAgentFeature string
|
||||
|
||||
// Enumerates UserAgentFeature.
|
||||
const (
|
||||
UserAgentFeatureResourceModel UserAgentFeature = "A" // n/a (we don't generate separate resource types)
|
||||
UserAgentFeatureWaiter = "B"
|
||||
UserAgentFeaturePaginator = "C"
|
||||
UserAgentFeatureRetryModeLegacy = "D" // n/a (equivalent to standard)
|
||||
UserAgentFeatureRetryModeStandard = "E"
|
||||
UserAgentFeatureRetryModeAdaptive = "F"
|
||||
UserAgentFeatureS3Transfer = "G"
|
||||
UserAgentFeatureS3CryptoV1N = "H" // n/a (crypto client is external)
|
||||
UserAgentFeatureS3CryptoV2 = "I" // n/a
|
||||
UserAgentFeatureS3ExpressBucket = "J"
|
||||
UserAgentFeatureS3AccessGrants = "K" // not yet implemented
|
||||
UserAgentFeatureGZIPRequestCompression = "L"
|
||||
UserAgentFeatureProtocolRPCV2CBOR = "M"
|
||||
UserAgentFeatureResourceModel UserAgentFeature = "A" // n/a (we don't generate separate resource types)
|
||||
UserAgentFeatureWaiter = "B"
|
||||
UserAgentFeaturePaginator = "C"
|
||||
UserAgentFeatureRetryModeLegacy = "D" // n/a (equivalent to standard)
|
||||
UserAgentFeatureRetryModeStandard = "E"
|
||||
UserAgentFeatureRetryModeAdaptive = "F"
|
||||
UserAgentFeatureS3Transfer = "G"
|
||||
UserAgentFeatureS3CryptoV1N = "H" // n/a (crypto client is external)
|
||||
UserAgentFeatureS3CryptoV2 = "I" // n/a
|
||||
UserAgentFeatureS3ExpressBucket = "J"
|
||||
UserAgentFeatureS3AccessGrants = "K" // not yet implemented
|
||||
UserAgentFeatureGZIPRequestCompression = "L"
|
||||
UserAgentFeatureProtocolRPCV2CBOR = "M"
|
||||
UserAgentFeatureRequestChecksumCRC32 = "U"
|
||||
UserAgentFeatureRequestChecksumCRC32C = "V"
|
||||
UserAgentFeatureRequestChecksumCRC64 = "W"
|
||||
UserAgentFeatureRequestChecksumSHA1 = "X"
|
||||
UserAgentFeatureRequestChecksumSHA256 = "Y"
|
||||
UserAgentFeatureRequestChecksumWhenSupported = "Z"
|
||||
UserAgentFeatureRequestChecksumWhenRequired = "a"
|
||||
UserAgentFeatureResponseChecksumWhenSupported = "b"
|
||||
UserAgentFeatureResponseChecksumWhenRequired = "c"
|
||||
)
|
||||
|
||||
// RequestUserAgent is a build middleware that set the User-Agent for the request.
|
||||
@@ -107,6 +119,7 @@ type RequestUserAgent struct {
|
||||
func NewRequestUserAgent() *RequestUserAgent {
|
||||
userAgent, sdkAgent := smithyhttp.NewUserAgentBuilder(), smithyhttp.NewUserAgentBuilder()
|
||||
addProductName(userAgent)
|
||||
addUserAgentMetadata(userAgent)
|
||||
addProductName(sdkAgent)
|
||||
|
||||
r := &RequestUserAgent{
|
||||
@@ -134,6 +147,10 @@ func addProductName(builder *smithyhttp.UserAgentBuilder) {
|
||||
builder.AddKeyValue(aws.SDKName, aws.SDKVersion)
|
||||
}
|
||||
|
||||
func addUserAgentMetadata(builder *smithyhttp.UserAgentBuilder) {
|
||||
builder.AddKey(uaMetadata)
|
||||
}
|
||||
|
||||
// AddUserAgentKey retrieves a requestUserAgent from the provided stack, or initializes one.
|
||||
func AddUserAgentKey(key string) func(*middleware.Stack) error {
|
||||
return func(stack *middleware.Stack) error {
|
||||
@@ -258,10 +275,10 @@ func (u *RequestUserAgent) HandleBuild(ctx context.Context, in middleware.BuildI
|
||||
|
||||
func (u *RequestUserAgent) addHTTPUserAgent(request *smithyhttp.Request) {
|
||||
const userAgent = "User-Agent"
|
||||
updateHTTPHeader(request, userAgent, u.userAgent.Build())
|
||||
if len(u.features) > 0 {
|
||||
updateHTTPHeader(request, userAgent, buildFeatureMetrics(u.features))
|
||||
}
|
||||
updateHTTPHeader(request, userAgent, u.userAgent.Build())
|
||||
}
|
||||
|
||||
func (u *RequestUserAgent) addHTTPSDKAgent(request *smithyhttp.Request) {
|
||||
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
# v1.6.8 (2025-01-24)
|
||||
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v1.6.7 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
|
||||
# v1.6.6 (2024-10-04)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.6.5 (2024-09-20)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.6.4 (2024-08-15)
|
||||
|
||||
* **Dependency Update**: Bump minimum Go version to 1.21.
|
||||
|
||||
# v1.6.3 (2024-06-28)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.6.2 (2024-03-29)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.6.1 (2024-02-21)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.6.0 (2024-02-13)
|
||||
|
||||
* **Feature**: Bump minimum Go version to 1.20 per our language support policy.
|
||||
|
||||
# v1.5.4 (2023-12-07)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.5.3 (2023-11-30)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.5.2 (2023-11-29)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.5.1 (2023-11-15)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.5.0 (2023-10-31)
|
||||
|
||||
* **Feature**: **BREAKING CHANGE**: Bump minimum go version to 1.19 per the revised [go version support policy](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-aligns-with-go-release-policy-on-supported-runtimes/).
|
||||
|
||||
# v1.4.14 (2023-10-06)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.13 (2023-08-18)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.12 (2023-08-07)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.11 (2023-07-31)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.10 (2022-12-02)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.9 (2022-10-24)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.8 (2022-09-14)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.7 (2022-09-02)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.6 (2022-08-31)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.5 (2022-08-29)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.4 (2022-08-09)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.3 (2022-06-29)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.2 (2022-06-07)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.1 (2022-03-24)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.4.0 (2022-03-08)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
|
||||
# v1.3.0 (2022-02-24)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
|
||||
# v1.2.0 (2022-01-14)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
|
||||
# v1.1.0 (2022-01-07)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
|
||||
# v1.0.0 (2021-11-06)
|
||||
|
||||
* **Announcement**: Support has been added for AWS EventStream APIs for Kinesis, S3, and Transcribe Streaming. Support for the Lex Runtime V2 EventStream API will be added in a future release.
|
||||
* **Release**: Protocol support has been added for AWS event stream.
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
package eventstream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type decodedMessage struct {
|
||||
rawMessage
|
||||
Headers decodedHeaders `json:"headers"`
|
||||
}
|
||||
type jsonMessage struct {
|
||||
Length json.Number `json:"total_length"`
|
||||
HeadersLen json.Number `json:"headers_length"`
|
||||
PreludeCRC json.Number `json:"prelude_crc"`
|
||||
Headers decodedHeaders `json:"headers"`
|
||||
Payload []byte `json:"payload"`
|
||||
CRC json.Number `json:"message_crc"`
|
||||
}
|
||||
|
||||
func (d *decodedMessage) UnmarshalJSON(b []byte) (err error) {
|
||||
var jsonMsg jsonMessage
|
||||
if err = json.Unmarshal(b, &jsonMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Length, err = numAsUint32(jsonMsg.Length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.HeadersLen, err = numAsUint32(jsonMsg.HeadersLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.PreludeCRC, err = numAsUint32(jsonMsg.PreludeCRC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Headers = jsonMsg.Headers
|
||||
d.Payload = jsonMsg.Payload
|
||||
d.CRC, err = numAsUint32(jsonMsg.CRC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *decodedMessage) MarshalJSON() ([]byte, error) {
|
||||
jsonMsg := jsonMessage{
|
||||
Length: json.Number(strconv.Itoa(int(d.Length))),
|
||||
HeadersLen: json.Number(strconv.Itoa(int(d.HeadersLen))),
|
||||
PreludeCRC: json.Number(strconv.Itoa(int(d.PreludeCRC))),
|
||||
Headers: d.Headers,
|
||||
Payload: d.Payload,
|
||||
CRC: json.Number(strconv.Itoa(int(d.CRC))),
|
||||
}
|
||||
|
||||
return json.Marshal(jsonMsg)
|
||||
}
|
||||
|
||||
func numAsUint32(n json.Number) (uint32, error) {
|
||||
v, err := n.Int64()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get int64 json number, %v", err)
|
||||
}
|
||||
|
||||
return uint32(v), nil
|
||||
}
|
||||
|
||||
func (d decodedMessage) Message() Message {
|
||||
return Message{
|
||||
Headers: Headers(d.Headers),
|
||||
Payload: d.Payload,
|
||||
}
|
||||
}
|
||||
|
||||
type decodedHeaders Headers
|
||||
|
||||
func (hs *decodedHeaders) UnmarshalJSON(b []byte) error {
|
||||
var jsonHeaders []struct {
|
||||
Name string `json:"name"`
|
||||
Type valueType `json:"type"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewReader(b))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&jsonHeaders); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var headers Headers
|
||||
for _, h := range jsonHeaders {
|
||||
value, err := valueFromType(h.Type, h.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
headers.Set(h.Name, value)
|
||||
}
|
||||
*hs = decodedHeaders(headers)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func valueFromType(typ valueType, val interface{}) (Value, error) {
|
||||
switch typ {
|
||||
case trueValueType:
|
||||
return BoolValue(true), nil
|
||||
case falseValueType:
|
||||
return BoolValue(false), nil
|
||||
case int8ValueType:
|
||||
v, err := val.(json.Number).Int64()
|
||||
return Int8Value(int8(v)), err
|
||||
case int16ValueType:
|
||||
v, err := val.(json.Number).Int64()
|
||||
return Int16Value(int16(v)), err
|
||||
case int32ValueType:
|
||||
v, err := val.(json.Number).Int64()
|
||||
return Int32Value(int32(v)), err
|
||||
case int64ValueType:
|
||||
v, err := val.(json.Number).Int64()
|
||||
return Int64Value(v), err
|
||||
case bytesValueType:
|
||||
v, err := base64.StdEncoding.DecodeString(val.(string))
|
||||
return BytesValue(v), err
|
||||
case stringValueType:
|
||||
v, err := base64.StdEncoding.DecodeString(val.(string))
|
||||
return StringValue(string(v)), err
|
||||
case timestampValueType:
|
||||
v, err := val.(json.Number).Int64()
|
||||
return TimestampValue(timeFromEpochMilli(v)), err
|
||||
case uuidValueType:
|
||||
v, err := base64.StdEncoding.DecodeString(val.(string))
|
||||
var tv UUIDValue
|
||||
copy(tv[:], v)
|
||||
return tv, err
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown type, %s, %T", typ.String(), val))
|
||||
}
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
package eventstream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
)
|
||||
|
||||
// DecoderOptions is the Decoder configuration options.
|
||||
type DecoderOptions struct {
|
||||
Logger logging.Logger
|
||||
LogMessages bool
|
||||
}
|
||||
|
||||
// Decoder provides decoding of an Event Stream messages.
|
||||
type Decoder struct {
|
||||
options DecoderOptions
|
||||
}
|
||||
|
||||
// NewDecoder initializes and returns a Decoder for decoding event
|
||||
// stream messages from the reader provided.
|
||||
func NewDecoder(optFns ...func(*DecoderOptions)) *Decoder {
|
||||
options := DecoderOptions{}
|
||||
|
||||
for _, fn := range optFns {
|
||||
fn(&options)
|
||||
}
|
||||
|
||||
return &Decoder{
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// Decode attempts to decode a single message from the event stream reader.
|
||||
// Will return the event stream message, or error if decodeMessage fails to read
|
||||
// the message from the stream.
|
||||
//
|
||||
// payloadBuf is a byte slice that will be used in the returned Message.Payload. Callers
|
||||
// must ensure that the Message.Payload from a previous decode has been consumed before passing in the same underlying
|
||||
// payloadBuf byte slice.
|
||||
func (d *Decoder) Decode(reader io.Reader, payloadBuf []byte) (m Message, err error) {
|
||||
if d.options.Logger != nil && d.options.LogMessages {
|
||||
debugMsgBuf := bytes.NewBuffer(nil)
|
||||
reader = io.TeeReader(reader, debugMsgBuf)
|
||||
defer func() {
|
||||
logMessageDecode(d.options.Logger, debugMsgBuf, m, err)
|
||||
}()
|
||||
}
|
||||
|
||||
m, err = decodeMessage(reader, payloadBuf)
|
||||
|
||||
return m, err
|
||||
}
|
||||
|
||||
// decodeMessage attempts to decode a single message from the event stream reader.
|
||||
// Will return the event stream message, or error if decodeMessage fails to read
|
||||
// the message from the reader.
|
||||
func decodeMessage(reader io.Reader, payloadBuf []byte) (m Message, err error) {
|
||||
crc := crc32.New(crc32IEEETable)
|
||||
hashReader := io.TeeReader(reader, crc)
|
||||
|
||||
prelude, err := decodePrelude(hashReader, crc)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
if prelude.HeadersLen > 0 {
|
||||
lr := io.LimitReader(hashReader, int64(prelude.HeadersLen))
|
||||
m.Headers, err = decodeHeaders(lr)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if payloadLen := prelude.PayloadLen(); payloadLen > 0 {
|
||||
buf, err := decodePayload(payloadBuf, io.LimitReader(hashReader, int64(payloadLen)))
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
m.Payload = buf
|
||||
}
|
||||
|
||||
msgCRC := crc.Sum32()
|
||||
if err := validateCRC(reader, msgCRC); err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func logMessageDecode(logger logging.Logger, msgBuf *bytes.Buffer, msg Message, decodeErr error) {
|
||||
w := bytes.NewBuffer(nil)
|
||||
defer func() { logger.Logf(logging.Debug, w.String()) }()
|
||||
|
||||
fmt.Fprintf(w, "Raw message:\n%s\n",
|
||||
hex.Dump(msgBuf.Bytes()))
|
||||
|
||||
if decodeErr != nil {
|
||||
fmt.Fprintf(w, "decodeMessage error: %v\n", decodeErr)
|
||||
return
|
||||
}
|
||||
|
||||
rawMsg, err := msg.rawMessage()
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "failed to create raw message, %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
decodedMsg := decodedMessage{
|
||||
rawMessage: rawMsg,
|
||||
Headers: decodedHeaders(msg.Headers),
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "Decoded message:\n")
|
||||
encoder := json.NewEncoder(w)
|
||||
if err := encoder.Encode(decodedMsg); err != nil {
|
||||
fmt.Fprintf(w, "failed to generate decoded message, %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func decodePrelude(r io.Reader, crc hash.Hash32) (messagePrelude, error) {
|
||||
var p messagePrelude
|
||||
|
||||
var err error
|
||||
p.Length, err = decodeUint32(r)
|
||||
if err != nil {
|
||||
return messagePrelude{}, err
|
||||
}
|
||||
|
||||
p.HeadersLen, err = decodeUint32(r)
|
||||
if err != nil {
|
||||
return messagePrelude{}, err
|
||||
}
|
||||
|
||||
if err := p.ValidateLens(); err != nil {
|
||||
return messagePrelude{}, err
|
||||
}
|
||||
|
||||
preludeCRC := crc.Sum32()
|
||||
if err := validateCRC(r, preludeCRC); err != nil {
|
||||
return messagePrelude{}, err
|
||||
}
|
||||
|
||||
p.PreludeCRC = preludeCRC
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func decodePayload(buf []byte, r io.Reader) ([]byte, error) {
|
||||
w := bytes.NewBuffer(buf[0:0])
|
||||
|
||||
_, err := io.Copy(w, r)
|
||||
return w.Bytes(), err
|
||||
}
|
||||
|
||||
func decodeUint8(r io.Reader) (uint8, error) {
|
||||
type byteReader interface {
|
||||
ReadByte() (byte, error)
|
||||
}
|
||||
|
||||
if br, ok := r.(byteReader); ok {
|
||||
v, err := br.ReadByte()
|
||||
return v, err
|
||||
}
|
||||
|
||||
var b [1]byte
|
||||
_, err := io.ReadFull(r, b[:])
|
||||
return b[0], err
|
||||
}
|
||||
|
||||
func decodeUint16(r io.Reader) (uint16, error) {
|
||||
var b [2]byte
|
||||
bs := b[:]
|
||||
_, err := io.ReadFull(r, bs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint16(bs), nil
|
||||
}
|
||||
|
||||
func decodeUint32(r io.Reader) (uint32, error) {
|
||||
var b [4]byte
|
||||
bs := b[:]
|
||||
_, err := io.ReadFull(r, bs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint32(bs), nil
|
||||
}
|
||||
|
||||
func decodeUint64(r io.Reader) (uint64, error) {
|
||||
var b [8]byte
|
||||
bs := b[:]
|
||||
_, err := io.ReadFull(r, bs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint64(bs), nil
|
||||
}
|
||||
|
||||
func validateCRC(r io.Reader, expect uint32) error {
|
||||
msgCRC, err := decodeUint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msgCRC != expect {
|
||||
return ChecksumError{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
package eventstream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
)
|
||||
|
||||
// EncoderOptions is the configuration options for Encoder.
|
||||
type EncoderOptions struct {
|
||||
Logger logging.Logger
|
||||
LogMessages bool
|
||||
}
|
||||
|
||||
// Encoder provides EventStream message encoding.
|
||||
type Encoder struct {
|
||||
options EncoderOptions
|
||||
|
||||
headersBuf *bytes.Buffer
|
||||
messageBuf *bytes.Buffer
|
||||
}
|
||||
|
||||
// NewEncoder initializes and returns an Encoder to encode Event Stream
|
||||
// messages.
|
||||
func NewEncoder(optFns ...func(*EncoderOptions)) *Encoder {
|
||||
o := EncoderOptions{}
|
||||
|
||||
for _, fn := range optFns {
|
||||
fn(&o)
|
||||
}
|
||||
|
||||
return &Encoder{
|
||||
options: o,
|
||||
headersBuf: bytes.NewBuffer(nil),
|
||||
messageBuf: bytes.NewBuffer(nil),
|
||||
}
|
||||
}
|
||||
|
||||
// Encode encodes a single EventStream message to the io.Writer the Encoder
|
||||
// was created with. An error is returned if writing the message fails.
|
||||
func (e *Encoder) Encode(w io.Writer, msg Message) (err error) {
|
||||
e.headersBuf.Reset()
|
||||
e.messageBuf.Reset()
|
||||
|
||||
var writer io.Writer = e.messageBuf
|
||||
if e.options.Logger != nil && e.options.LogMessages {
|
||||
encodeMsgBuf := bytes.NewBuffer(nil)
|
||||
writer = io.MultiWriter(writer, encodeMsgBuf)
|
||||
defer func() {
|
||||
logMessageEncode(e.options.Logger, encodeMsgBuf, msg, err)
|
||||
}()
|
||||
}
|
||||
|
||||
if err = EncodeHeaders(e.headersBuf, msg.Headers); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
crc := crc32.New(crc32IEEETable)
|
||||
hashWriter := io.MultiWriter(writer, crc)
|
||||
|
||||
headersLen := uint32(e.headersBuf.Len())
|
||||
payloadLen := uint32(len(msg.Payload))
|
||||
|
||||
if err = encodePrelude(hashWriter, crc, headersLen, payloadLen); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if headersLen > 0 {
|
||||
if _, err = io.Copy(hashWriter, e.headersBuf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if payloadLen > 0 {
|
||||
if _, err = hashWriter.Write(msg.Payload); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
msgCRC := crc.Sum32()
|
||||
if err := binary.Write(writer, binary.BigEndian, msgCRC); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(w, e.messageBuf)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func logMessageEncode(logger logging.Logger, msgBuf *bytes.Buffer, msg Message, encodeErr error) {
|
||||
w := bytes.NewBuffer(nil)
|
||||
defer func() { logger.Logf(logging.Debug, w.String()) }()
|
||||
|
||||
fmt.Fprintf(w, "Message to encode:\n")
|
||||
encoder := json.NewEncoder(w)
|
||||
if err := encoder.Encode(msg); err != nil {
|
||||
fmt.Fprintf(w, "Failed to get encoded message, %v\n", err)
|
||||
}
|
||||
|
||||
if encodeErr != nil {
|
||||
fmt.Fprintf(w, "Encode error: %v\n", encodeErr)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "Raw message:\n%s\n", hex.Dump(msgBuf.Bytes()))
|
||||
}
|
||||
|
||||
func encodePrelude(w io.Writer, crc hash.Hash32, headersLen, payloadLen uint32) error {
|
||||
p := messagePrelude{
|
||||
Length: minMsgLen + headersLen + payloadLen,
|
||||
HeadersLen: headersLen,
|
||||
}
|
||||
if err := p.ValidateLens(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := binaryWriteFields(w, binary.BigEndian,
|
||||
p.Length,
|
||||
p.HeadersLen,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.PreludeCRC = crc.Sum32()
|
||||
err = binary.Write(w, binary.BigEndian, p.PreludeCRC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeHeaders writes the header values to the writer encoded in the event
|
||||
// stream format. Returns an error if a header fails to encode.
|
||||
func EncodeHeaders(w io.Writer, headers Headers) error {
|
||||
for _, h := range headers {
|
||||
hn := headerName{
|
||||
Len: uint8(len(h.Name)),
|
||||
}
|
||||
copy(hn.Name[:hn.Len], h.Name)
|
||||
if err := hn.encode(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := h.Value.encode(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func binaryWriteFields(w io.Writer, order binary.ByteOrder, vs ...interface{}) error {
|
||||
for _, v := range vs {
|
||||
if err := binary.Write(w, order, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package eventstream
|
||||
|
||||
import "fmt"
|
||||
|
||||
// LengthError provides the error for items being larger than a maximum length.
|
||||
type LengthError struct {
|
||||
Part string
|
||||
Want int
|
||||
Have int
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (e LengthError) Error() string {
|
||||
return fmt.Sprintf("%s length invalid, %d/%d, %v",
|
||||
e.Part, e.Want, e.Have, e.Value)
|
||||
}
|
||||
|
||||
// ChecksumError provides the error for message checksum invalidation errors.
|
||||
type ChecksumError struct{}
|
||||
|
||||
func (e ChecksumError) Error() string {
|
||||
return "message checksum mismatch"
|
||||
}
|
||||
Generated
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
package eventstreamapi
|
||||
|
||||
// EventStream headers with specific meaning to async API functionality.
|
||||
const (
|
||||
ChunkSignatureHeader = `:chunk-signature` // chunk signature for message
|
||||
DateHeader = `:date` // Date header for signature
|
||||
ContentTypeHeader = ":content-type" // message payload content-type
|
||||
|
||||
// Message header and values
|
||||
MessageTypeHeader = `:message-type` // Identifies type of message.
|
||||
EventMessageType = `event`
|
||||
ErrorMessageType = `error`
|
||||
ExceptionMessageType = `exception`
|
||||
|
||||
// Message Events
|
||||
EventTypeHeader = `:event-type` // Identifies message event type e.g. "Stats".
|
||||
|
||||
// Message Error
|
||||
ErrorCodeHeader = `:error-code`
|
||||
ErrorMessageHeader = `:error-message`
|
||||
|
||||
// Message Exception
|
||||
ExceptionTypeHeader = `:exception-type`
|
||||
)
|
||||
Generated
Vendored
+71
@@ -0,0 +1,71 @@
|
||||
package eventstreamapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
"io"
|
||||
)
|
||||
|
||||
type eventStreamWriterKey struct{}
|
||||
|
||||
// GetInputStreamWriter returns EventTypeHeader io.PipeWriter used for the operation's input event stream.
|
||||
func GetInputStreamWriter(ctx context.Context) io.WriteCloser {
|
||||
writeCloser, _ := middleware.GetStackValue(ctx, eventStreamWriterKey{}).(io.WriteCloser)
|
||||
return writeCloser
|
||||
}
|
||||
|
||||
func setInputStreamWriter(ctx context.Context, writeCloser io.WriteCloser) context.Context {
|
||||
return middleware.WithStackValue(ctx, eventStreamWriterKey{}, writeCloser)
|
||||
}
|
||||
|
||||
// InitializeStreamWriter is a Finalize middleware initializes an in-memory pipe for sending event stream messages
|
||||
// via the HTTP request body.
|
||||
type InitializeStreamWriter struct{}
|
||||
|
||||
// AddInitializeStreamWriter adds the InitializeStreamWriter middleware to the provided stack.
|
||||
func AddInitializeStreamWriter(stack *middleware.Stack) error {
|
||||
return stack.Finalize.Add(&InitializeStreamWriter{}, middleware.After)
|
||||
}
|
||||
|
||||
// ID returns the identifier for the middleware.
|
||||
func (i *InitializeStreamWriter) ID() string {
|
||||
return "InitializeStreamWriter"
|
||||
}
|
||||
|
||||
// HandleFinalize is the middleware implementation.
|
||||
func (i *InitializeStreamWriter) HandleFinalize(
|
||||
ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler,
|
||||
) (
|
||||
out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
request, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, fmt.Errorf("unknown transport type: %T", in.Request)
|
||||
}
|
||||
|
||||
inputReader, inputWriter := io.Pipe()
|
||||
defer func() {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
_ = inputReader.Close()
|
||||
_ = inputWriter.Close()
|
||||
}()
|
||||
|
||||
request, err = request.SetStream(inputReader)
|
||||
if err != nil {
|
||||
return out, metadata, err
|
||||
}
|
||||
in.Request = request
|
||||
|
||||
ctx = setInputStreamWriter(ctx, inputWriter)
|
||||
|
||||
out, metadata, err = next.HandleFinalize(ctx, in)
|
||||
if err != nil {
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
return out, metadata, err
|
||||
}
|
||||
Generated
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
//go:build go1.18
|
||||
// +build go1.18
|
||||
|
||||
package eventstreamapi
|
||||
|
||||
import smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
|
||||
// ApplyHTTPTransportFixes applies fixes to the HTTP request for proper event stream functionality.
|
||||
//
|
||||
// This operation is a no-op for Go 1.18 and above.
|
||||
func ApplyHTTPTransportFixes(r *smithyhttp.Request) error {
|
||||
return nil
|
||||
}
|
||||
Generated
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
//go:build !go1.18
|
||||
// +build !go1.18
|
||||
|
||||
package eventstreamapi
|
||||
|
||||
import smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
|
||||
// ApplyHTTPTransportFixes applies fixes to the HTTP request for proper event stream functionality.
|
||||
func ApplyHTTPTransportFixes(r *smithyhttp.Request) error {
|
||||
r.Header.Set("Expect", "100-continue")
|
||||
return nil
|
||||
}
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT.
|
||||
|
||||
package eventstream
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.6.8"
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
package eventstream
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Headers are a collection of EventStream header values.
|
||||
type Headers []Header
|
||||
|
||||
// Header is a single EventStream Key Value header pair.
|
||||
type Header struct {
|
||||
Name string
|
||||
Value Value
|
||||
}
|
||||
|
||||
// Set associates the name with a value. If the header name already exists in
|
||||
// the Headers the value will be replaced with the new one.
|
||||
func (hs *Headers) Set(name string, value Value) {
|
||||
var i int
|
||||
for ; i < len(*hs); i++ {
|
||||
if (*hs)[i].Name == name {
|
||||
(*hs)[i].Value = value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
*hs = append(*hs, Header{
|
||||
Name: name, Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
// Get returns the Value associated with the header. Nil is returned if the
|
||||
// value does not exist.
|
||||
func (hs Headers) Get(name string) Value {
|
||||
for i := 0; i < len(hs); i++ {
|
||||
if h := hs[i]; h.Name == name {
|
||||
return h.Value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Del deletes the value in the Headers if it exists.
|
||||
func (hs *Headers) Del(name string) {
|
||||
for i := 0; i < len(*hs); i++ {
|
||||
if (*hs)[i].Name == name {
|
||||
copy((*hs)[i:], (*hs)[i+1:])
|
||||
(*hs) = (*hs)[:len(*hs)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of the headers
|
||||
func (hs Headers) Clone() Headers {
|
||||
o := make(Headers, 0, len(hs))
|
||||
for _, h := range hs {
|
||||
o.Set(h.Name, h.Value)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func decodeHeaders(r io.Reader) (Headers, error) {
|
||||
hs := Headers{}
|
||||
|
||||
for {
|
||||
name, err := decodeHeaderName(r)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
// EOF while getting header name means no more headers
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, err := decodeHeaderValue(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hs.Set(name, value)
|
||||
}
|
||||
|
||||
return hs, nil
|
||||
}
|
||||
|
||||
func decodeHeaderName(r io.Reader) (string, error) {
|
||||
var n headerName
|
||||
|
||||
var err error
|
||||
n.Len, err = decodeUint8(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
name := n.Name[:n.Len]
|
||||
if _, err := io.ReadFull(r, name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(name), nil
|
||||
}
|
||||
|
||||
func decodeHeaderValue(r io.Reader) (Value, error) {
|
||||
var raw rawValue
|
||||
|
||||
typ, err := decodeUint8(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw.Type = valueType(typ)
|
||||
|
||||
var v Value
|
||||
|
||||
switch raw.Type {
|
||||
case trueValueType:
|
||||
v = BoolValue(true)
|
||||
case falseValueType:
|
||||
v = BoolValue(false)
|
||||
case int8ValueType:
|
||||
var tv Int8Value
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case int16ValueType:
|
||||
var tv Int16Value
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case int32ValueType:
|
||||
var tv Int32Value
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case int64ValueType:
|
||||
var tv Int64Value
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case bytesValueType:
|
||||
var tv BytesValue
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case stringValueType:
|
||||
var tv StringValue
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case timestampValueType:
|
||||
var tv TimestampValue
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case uuidValueType:
|
||||
var tv UUIDValue
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown value type %d", raw.Type))
|
||||
}
|
||||
|
||||
// Error could be EOF, let caller deal with it
|
||||
return v, err
|
||||
}
|
||||
|
||||
const maxHeaderNameLen = 255
|
||||
|
||||
type headerName struct {
|
||||
Len uint8
|
||||
Name [maxHeaderNameLen]byte
|
||||
}
|
||||
|
||||
func (v headerName) encode(w io.Writer) error {
|
||||
if err := binary.Write(w, binary.BigEndian, v.Len); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := w.Write(v.Name[:v.Len])
|
||||
return err
|
||||
}
|
||||
+521
@@ -0,0 +1,521 @@
|
||||
package eventstream
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const maxHeaderValueLen = 1<<15 - 1 // 2^15-1 or 32KB - 1
|
||||
|
||||
// valueType is the EventStream header value type.
|
||||
type valueType uint8
|
||||
|
||||
// Header value types
|
||||
const (
|
||||
trueValueType valueType = iota
|
||||
falseValueType
|
||||
int8ValueType // Byte
|
||||
int16ValueType // Short
|
||||
int32ValueType // Integer
|
||||
int64ValueType // Long
|
||||
bytesValueType
|
||||
stringValueType
|
||||
timestampValueType
|
||||
uuidValueType
|
||||
)
|
||||
|
||||
func (t valueType) String() string {
|
||||
switch t {
|
||||
case trueValueType:
|
||||
return "bool"
|
||||
case falseValueType:
|
||||
return "bool"
|
||||
case int8ValueType:
|
||||
return "int8"
|
||||
case int16ValueType:
|
||||
return "int16"
|
||||
case int32ValueType:
|
||||
return "int32"
|
||||
case int64ValueType:
|
||||
return "int64"
|
||||
case bytesValueType:
|
||||
return "byte_array"
|
||||
case stringValueType:
|
||||
return "string"
|
||||
case timestampValueType:
|
||||
return "timestamp"
|
||||
case uuidValueType:
|
||||
return "uuid"
|
||||
default:
|
||||
return fmt.Sprintf("unknown value type %d", uint8(t))
|
||||
}
|
||||
}
|
||||
|
||||
type rawValue struct {
|
||||
Type valueType
|
||||
Len uint16 // Only set for variable length slices
|
||||
Value []byte // byte representation of value, BigEndian encoding.
|
||||
}
|
||||
|
||||
func (r rawValue) encodeScalar(w io.Writer, v interface{}) error {
|
||||
return binaryWriteFields(w, binary.BigEndian,
|
||||
r.Type,
|
||||
v,
|
||||
)
|
||||
}
|
||||
|
||||
func (r rawValue) encodeFixedSlice(w io.Writer, v []byte) error {
|
||||
binary.Write(w, binary.BigEndian, r.Type)
|
||||
|
||||
_, err := w.Write(v)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r rawValue) encodeBytes(w io.Writer, v []byte) error {
|
||||
if len(v) > maxHeaderValueLen {
|
||||
return LengthError{
|
||||
Part: "header value",
|
||||
Want: maxHeaderValueLen, Have: len(v),
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
r.Len = uint16(len(v))
|
||||
|
||||
err := binaryWriteFields(w, binary.BigEndian,
|
||||
r.Type,
|
||||
r.Len,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(v)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r rawValue) encodeString(w io.Writer, v string) error {
|
||||
if len(v) > maxHeaderValueLen {
|
||||
return LengthError{
|
||||
Part: "header value",
|
||||
Want: maxHeaderValueLen, Have: len(v),
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
r.Len = uint16(len(v))
|
||||
|
||||
type stringWriter interface {
|
||||
WriteString(string) (int, error)
|
||||
}
|
||||
|
||||
err := binaryWriteFields(w, binary.BigEndian,
|
||||
r.Type,
|
||||
r.Len,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sw, ok := w.(stringWriter); ok {
|
||||
_, err = sw.WriteString(v)
|
||||
} else {
|
||||
_, err = w.Write([]byte(v))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func decodeFixedBytesValue(r io.Reader, buf []byte) error {
|
||||
_, err := io.ReadFull(r, buf)
|
||||
return err
|
||||
}
|
||||
|
||||
func decodeBytesValue(r io.Reader) ([]byte, error) {
|
||||
var raw rawValue
|
||||
var err error
|
||||
raw.Len, err = decodeUint16(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := make([]byte, raw.Len)
|
||||
_, err = io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func decodeStringValue(r io.Reader) (string, error) {
|
||||
v, err := decodeBytesValue(r)
|
||||
return string(v), err
|
||||
}
|
||||
|
||||
// Value represents the abstract header value.
|
||||
type Value interface {
|
||||
Get() interface{}
|
||||
String() string
|
||||
valueType() valueType
|
||||
encode(io.Writer) error
|
||||
}
|
||||
|
||||
// An BoolValue provides eventstream encoding, and representation
|
||||
// of a Go bool value.
|
||||
type BoolValue bool
|
||||
|
||||
// Get returns the underlying type
|
||||
func (v BoolValue) Get() interface{} {
|
||||
return bool(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (v BoolValue) valueType() valueType {
|
||||
if v {
|
||||
return trueValueType
|
||||
}
|
||||
return falseValueType
|
||||
}
|
||||
|
||||
func (v BoolValue) String() string {
|
||||
return strconv.FormatBool(bool(v))
|
||||
}
|
||||
|
||||
// encode encodes the BoolValue into an eventstream binary value
|
||||
// representation.
|
||||
func (v BoolValue) encode(w io.Writer) error {
|
||||
return binary.Write(w, binary.BigEndian, v.valueType())
|
||||
}
|
||||
|
||||
// An Int8Value provides eventstream encoding, and representation of a Go
|
||||
// int8 value.
|
||||
type Int8Value int8
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v Int8Value) Get() interface{} {
|
||||
return int8(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (Int8Value) valueType() valueType {
|
||||
return int8ValueType
|
||||
}
|
||||
|
||||
func (v Int8Value) String() string {
|
||||
return fmt.Sprintf("0x%02x", int8(v))
|
||||
}
|
||||
|
||||
// encode encodes the Int8Value into an eventstream binary value
|
||||
// representation.
|
||||
func (v Int8Value) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
|
||||
return raw.encodeScalar(w, v)
|
||||
}
|
||||
|
||||
func (v *Int8Value) decode(r io.Reader) error {
|
||||
n, err := decodeUint8(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = Int8Value(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An Int16Value provides eventstream encoding, and representation of a Go
|
||||
// int16 value.
|
||||
type Int16Value int16
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v Int16Value) Get() interface{} {
|
||||
return int16(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (Int16Value) valueType() valueType {
|
||||
return int16ValueType
|
||||
}
|
||||
|
||||
func (v Int16Value) String() string {
|
||||
return fmt.Sprintf("0x%04x", int16(v))
|
||||
}
|
||||
|
||||
// encode encodes the Int16Value into an eventstream binary value
|
||||
// representation.
|
||||
func (v Int16Value) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
return raw.encodeScalar(w, v)
|
||||
}
|
||||
|
||||
func (v *Int16Value) decode(r io.Reader) error {
|
||||
n, err := decodeUint16(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = Int16Value(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An Int32Value provides eventstream encoding, and representation of a Go
|
||||
// int32 value.
|
||||
type Int32Value int32
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v Int32Value) Get() interface{} {
|
||||
return int32(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (Int32Value) valueType() valueType {
|
||||
return int32ValueType
|
||||
}
|
||||
|
||||
func (v Int32Value) String() string {
|
||||
return fmt.Sprintf("0x%08x", int32(v))
|
||||
}
|
||||
|
||||
// encode encodes the Int32Value into an eventstream binary value
|
||||
// representation.
|
||||
func (v Int32Value) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
return raw.encodeScalar(w, v)
|
||||
}
|
||||
|
||||
func (v *Int32Value) decode(r io.Reader) error {
|
||||
n, err := decodeUint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = Int32Value(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An Int64Value provides eventstream encoding, and representation of a Go
|
||||
// int64 value.
|
||||
type Int64Value int64
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v Int64Value) Get() interface{} {
|
||||
return int64(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (Int64Value) valueType() valueType {
|
||||
return int64ValueType
|
||||
}
|
||||
|
||||
func (v Int64Value) String() string {
|
||||
return fmt.Sprintf("0x%016x", int64(v))
|
||||
}
|
||||
|
||||
// encode encodes the Int64Value into an eventstream binary value
|
||||
// representation.
|
||||
func (v Int64Value) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
return raw.encodeScalar(w, v)
|
||||
}
|
||||
|
||||
func (v *Int64Value) decode(r io.Reader) error {
|
||||
n, err := decodeUint64(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = Int64Value(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An BytesValue provides eventstream encoding, and representation of a Go
|
||||
// byte slice.
|
||||
type BytesValue []byte
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v BytesValue) Get() interface{} {
|
||||
return []byte(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (BytesValue) valueType() valueType {
|
||||
return bytesValueType
|
||||
}
|
||||
|
||||
func (v BytesValue) String() string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(v))
|
||||
}
|
||||
|
||||
// encode encodes the BytesValue into an eventstream binary value
|
||||
// representation.
|
||||
func (v BytesValue) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
|
||||
return raw.encodeBytes(w, []byte(v))
|
||||
}
|
||||
|
||||
func (v *BytesValue) decode(r io.Reader) error {
|
||||
buf, err := decodeBytesValue(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = BytesValue(buf)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An StringValue provides eventstream encoding, and representation of a Go
|
||||
// string.
|
||||
type StringValue string
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v StringValue) Get() interface{} {
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (StringValue) valueType() valueType {
|
||||
return stringValueType
|
||||
}
|
||||
|
||||
func (v StringValue) String() string {
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// encode encodes the StringValue into an eventstream binary value
|
||||
// representation.
|
||||
func (v StringValue) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
|
||||
return raw.encodeString(w, string(v))
|
||||
}
|
||||
|
||||
func (v *StringValue) decode(r io.Reader) error {
|
||||
s, err := decodeStringValue(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = StringValue(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An TimestampValue provides eventstream encoding, and representation of a Go
|
||||
// timestamp.
|
||||
type TimestampValue time.Time
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v TimestampValue) Get() interface{} {
|
||||
return time.Time(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (TimestampValue) valueType() valueType {
|
||||
return timestampValueType
|
||||
}
|
||||
|
||||
func (v TimestampValue) epochMilli() int64 {
|
||||
nano := time.Time(v).UnixNano()
|
||||
msec := nano / int64(time.Millisecond)
|
||||
return msec
|
||||
}
|
||||
|
||||
func (v TimestampValue) String() string {
|
||||
msec := v.epochMilli()
|
||||
return strconv.FormatInt(msec, 10)
|
||||
}
|
||||
|
||||
// encode encodes the TimestampValue into an eventstream binary value
|
||||
// representation.
|
||||
func (v TimestampValue) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
|
||||
msec := v.epochMilli()
|
||||
return raw.encodeScalar(w, msec)
|
||||
}
|
||||
|
||||
func (v *TimestampValue) decode(r io.Reader) error {
|
||||
n, err := decodeUint64(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = TimestampValue(timeFromEpochMilli(int64(n)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface
|
||||
func (v TimestampValue) MarshalJSON() ([]byte, error) {
|
||||
return []byte(v.String()), nil
|
||||
}
|
||||
|
||||
func timeFromEpochMilli(t int64) time.Time {
|
||||
secs := t / 1e3
|
||||
msec := t % 1e3
|
||||
return time.Unix(secs, msec*int64(time.Millisecond)).UTC()
|
||||
}
|
||||
|
||||
// An UUIDValue provides eventstream encoding, and representation of a UUID
|
||||
// value.
|
||||
type UUIDValue [16]byte
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v UUIDValue) Get() interface{} {
|
||||
return v[:]
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (UUIDValue) valueType() valueType {
|
||||
return uuidValueType
|
||||
}
|
||||
|
||||
func (v UUIDValue) String() string {
|
||||
var scratch [36]byte
|
||||
|
||||
const dash = '-'
|
||||
|
||||
hex.Encode(scratch[:8], v[0:4])
|
||||
scratch[8] = dash
|
||||
hex.Encode(scratch[9:13], v[4:6])
|
||||
scratch[13] = dash
|
||||
hex.Encode(scratch[14:18], v[6:8])
|
||||
scratch[18] = dash
|
||||
hex.Encode(scratch[19:23], v[8:10])
|
||||
scratch[23] = dash
|
||||
hex.Encode(scratch[24:], v[10:])
|
||||
|
||||
return string(scratch[:])
|
||||
}
|
||||
|
||||
// encode encodes the UUIDValue into an eventstream binary value
|
||||
// representation.
|
||||
func (v UUIDValue) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
|
||||
return raw.encodeFixedSlice(w, v[:])
|
||||
}
|
||||
|
||||
func (v *UUIDValue) decode(r io.Reader) error {
|
||||
tv := (*v)[:]
|
||||
return decodeFixedBytesValue(r, tv)
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package eventstream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
const preludeLen = 8
|
||||
const preludeCRCLen = 4
|
||||
const msgCRCLen = 4
|
||||
const minMsgLen = preludeLen + preludeCRCLen + msgCRCLen
|
||||
const maxPayloadLen = 1024 * 1024 * 16 // 16MB
|
||||
const maxHeadersLen = 1024 * 128 // 128KB
|
||||
const maxMsgLen = minMsgLen + maxHeadersLen + maxPayloadLen
|
||||
|
||||
var crc32IEEETable = crc32.MakeTable(crc32.IEEE)
|
||||
|
||||
// A Message provides the eventstream message representation.
|
||||
type Message struct {
|
||||
Headers Headers
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (m *Message) rawMessage() (rawMessage, error) {
|
||||
var raw rawMessage
|
||||
|
||||
if len(m.Headers) > 0 {
|
||||
var headers bytes.Buffer
|
||||
if err := EncodeHeaders(&headers, m.Headers); err != nil {
|
||||
return rawMessage{}, err
|
||||
}
|
||||
raw.Headers = headers.Bytes()
|
||||
raw.HeadersLen = uint32(len(raw.Headers))
|
||||
}
|
||||
|
||||
raw.Length = raw.HeadersLen + uint32(len(m.Payload)) + minMsgLen
|
||||
|
||||
hash := crc32.New(crc32IEEETable)
|
||||
binaryWriteFields(hash, binary.BigEndian, raw.Length, raw.HeadersLen)
|
||||
raw.PreludeCRC = hash.Sum32()
|
||||
|
||||
binaryWriteFields(hash, binary.BigEndian, raw.PreludeCRC)
|
||||
|
||||
if raw.HeadersLen > 0 {
|
||||
hash.Write(raw.Headers)
|
||||
}
|
||||
|
||||
// Read payload bytes and update hash for it as well.
|
||||
if len(m.Payload) > 0 {
|
||||
raw.Payload = m.Payload
|
||||
hash.Write(raw.Payload)
|
||||
}
|
||||
|
||||
raw.CRC = hash.Sum32()
|
||||
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
// Clone returns a deep copy of the message.
|
||||
func (m Message) Clone() Message {
|
||||
var payload []byte
|
||||
if m.Payload != nil {
|
||||
payload = make([]byte, len(m.Payload))
|
||||
copy(payload, m.Payload)
|
||||
}
|
||||
|
||||
return Message{
|
||||
Headers: m.Headers.Clone(),
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
type messagePrelude struct {
|
||||
Length uint32
|
||||
HeadersLen uint32
|
||||
PreludeCRC uint32
|
||||
}
|
||||
|
||||
func (p messagePrelude) PayloadLen() uint32 {
|
||||
return p.Length - p.HeadersLen - minMsgLen
|
||||
}
|
||||
|
||||
func (p messagePrelude) ValidateLens() error {
|
||||
if p.Length == 0 || p.Length > maxMsgLen {
|
||||
return LengthError{
|
||||
Part: "message prelude",
|
||||
Want: maxMsgLen,
|
||||
Have: int(p.Length),
|
||||
}
|
||||
}
|
||||
if p.HeadersLen > maxHeadersLen {
|
||||
return LengthError{
|
||||
Part: "message headers",
|
||||
Want: maxHeadersLen,
|
||||
Have: int(p.HeadersLen),
|
||||
}
|
||||
}
|
||||
if payloadLen := p.PayloadLen(); payloadLen > maxPayloadLen {
|
||||
return LengthError{
|
||||
Part: "message payload",
|
||||
Want: maxPayloadLen,
|
||||
Have: int(payloadLen),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type rawMessage struct {
|
||||
messagePrelude
|
||||
|
||||
Headers []byte
|
||||
Payload []byte
|
||||
|
||||
CRC uint32
|
||||
}
|
||||
+9
-20
@@ -1,8 +1,8 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Array represents the encoding of Query lists and sets. A Query array is a
|
||||
@@ -21,19 +21,8 @@ type Array struct {
|
||||
// keys for each element in the list. For example, an entry might have the
|
||||
// key "ParentStructure.ListName.member.MemberName.1".
|
||||
//
|
||||
// While this is currently represented as a string that gets added to, it
|
||||
// could also be represented as a stack that only gets condensed into a
|
||||
// string when a finalized key is created. This could potentially reduce
|
||||
// allocations.
|
||||
// When the array is not flat the prefix will contain the memberName otherwise the memberName is ignored
|
||||
prefix string
|
||||
// Whether the list is flat or not. A list that is not flat will produce the
|
||||
// following entry to the url.Values for a given entry:
|
||||
// ListName.MemberName.1=value
|
||||
// A list that is flat will produce the following:
|
||||
// ListName.1=value
|
||||
flat bool
|
||||
// The location name of the member. In most cases this should be "member".
|
||||
memberName string
|
||||
// Elements are stored in values, so we keep track of the list size here.
|
||||
size int32
|
||||
// Empty lists are encoded as "<prefix>=", if we add a value later we will
|
||||
@@ -45,11 +34,14 @@ func newArray(values url.Values, prefix string, flat bool, memberName string) *A
|
||||
emptyValue := newValue(values, prefix, flat)
|
||||
emptyValue.String("")
|
||||
|
||||
if !flat {
|
||||
// This uses string concatenation in place of fmt.Sprintf as fmt.Sprintf has a much higher resource overhead
|
||||
prefix = prefix + keySeparator + memberName
|
||||
}
|
||||
|
||||
return &Array{
|
||||
values: values,
|
||||
prefix: prefix,
|
||||
flat: flat,
|
||||
memberName: memberName,
|
||||
emptyValue: emptyValue,
|
||||
}
|
||||
}
|
||||
@@ -63,10 +55,7 @@ func (a *Array) Value() Value {
|
||||
|
||||
// Query lists start a 1, so adjust the size first
|
||||
a.size++
|
||||
prefix := a.prefix
|
||||
if !a.flat {
|
||||
prefix = fmt.Sprintf("%s.%s", prefix, a.memberName)
|
||||
}
|
||||
// Lists can't have flat members
|
||||
return newValue(a.values, fmt.Sprintf("%s.%d", prefix, a.size), false)
|
||||
// This uses string concatenation in place of fmt.Sprintf as fmt.Sprintf has a much higher resource overhead
|
||||
return newValue(a.values, a.prefix+keySeparator+strconv.FormatInt(int64(a.size), 10), false)
|
||||
}
|
||||
|
||||
+5
-6
@@ -1,9 +1,6 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
import "net/url"
|
||||
|
||||
// Object represents the encoding of Query structures and unions. A Query
|
||||
// object is a representation of a mapping of string keys to arbitrary
|
||||
@@ -56,14 +53,16 @@ func (o *Object) FlatKey(name string) Value {
|
||||
|
||||
func (o *Object) key(name string, flatValue bool) Value {
|
||||
if o.prefix != "" {
|
||||
return newValue(o.values, fmt.Sprintf("%s.%s", o.prefix, name), flatValue)
|
||||
// This uses string concatenation in place of fmt.Sprintf as fmt.Sprintf has a much higher resource overhead
|
||||
return newValue(o.values, o.prefix+keySeparator+name, flatValue)
|
||||
}
|
||||
return newValue(o.values, name, flatValue)
|
||||
}
|
||||
|
||||
func (o *Object) keyWithValues(name string, flatValue bool) Value {
|
||||
if o.prefix != "" {
|
||||
return newAppendValue(o.values, fmt.Sprintf("%s.%s", o.prefix, name), flatValue)
|
||||
// This uses string concatenation in place of fmt.Sprintf as fmt.Sprintf has a much higher resource overhead
|
||||
return newAppendValue(o.values, o.prefix+keySeparator+name, flatValue)
|
||||
}
|
||||
return newAppendValue(o.values, name, flatValue)
|
||||
}
|
||||
|
||||
+2
@@ -7,6 +7,8 @@ import (
|
||||
"github.com/aws/smithy-go/encoding/httpbinding"
|
||||
)
|
||||
|
||||
const keySeparator = "."
|
||||
|
||||
// Value represents a Query Value type.
|
||||
type Value struct {
|
||||
// The query values to add the value to.
|
||||
|
||||
+6
@@ -116,7 +116,13 @@ func (r RetryableConnectionError) IsErrorRetryable(err error) aws.Ternary {
|
||||
case errors.As(err, &conErr) && conErr.ConnectionError():
|
||||
retryable = true
|
||||
|
||||
case strings.Contains(err.Error(), "use of closed network connection"):
|
||||
fallthrough
|
||||
case strings.Contains(err.Error(), "connection reset"):
|
||||
// The errors "connection reset" and "use of closed network connection"
|
||||
// are effectively the same. It appears to be the difference between
|
||||
// sync and async read of TCP RST in the stdlib's net.Conn read loop.
|
||||
// see #2737
|
||||
retryable = true
|
||||
|
||||
case errors.As(err, &urlErr):
|
||||
|
||||
+5
-4
@@ -4,10 +4,11 @@ package v4
|
||||
var IgnoredHeaders = Rules{
|
||||
ExcludeList{
|
||||
MapRule{
|
||||
"Authorization": struct{}{},
|
||||
"User-Agent": struct{}{},
|
||||
"X-Amzn-Trace-Id": struct{}{},
|
||||
"Expect": struct{}{},
|
||||
"Authorization": struct{}{},
|
||||
"User-Agent": struct{}{},
|
||||
"X-Amzn-Trace-Id": struct{}{},
|
||||
"Expect": struct{}{},
|
||||
"Transfer-Encoding": struct{}{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
+48
@@ -1,3 +1,51 @@
|
||||
# v1.29.4 (2025-01-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.29.3 (2025-01-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.29.2 (2025-01-24)
|
||||
|
||||
* **Bug Fix**: Fix env config naming and usage of deprecated ioutil
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v1.29.1 (2025-01-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.29.0 (2025-01-15)
|
||||
|
||||
* **Feature**: S3 client behavior is updated to always calculate a checksum by default for operations that support it (such as PutObject or UploadPart), or require it (such as DeleteObjects). The checksum algorithm used by default now becomes CRC32. Checksum behavior can be configured using `when_supported` and `when_required` options - in code using RequestChecksumCalculation, in shared config using request_checksum_calculation, or as env variable using AWS_REQUEST_CHECKSUM_CALCULATION. The S3 client attempts to validate response checksums for all S3 API operations that support checksums. However, if the SDK has not implemented the specified checksum algorithm then this validation is skipped. Checksum validation behavior can be configured using `when_supported` and `when_required` options - in code using ResponseChecksumValidation, in shared config using response_checksum_validation, or as env variable using AWS_RESPONSE_CHECKSUM_VALIDATION.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.28.11 (2025-01-14)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.28.10 (2025-01-10)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.28.9 (2025-01-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.28.8 (2025-01-08)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.28.7 (2024-12-19)
|
||||
|
||||
* **Bug Fix**: Fix improper use of printf-style functions.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.28.6 (2024-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.28.5 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
|
||||
+7
-1
@@ -83,6 +83,12 @@ var defaultAWSConfigResolvers = []awsConfigResolver{
|
||||
|
||||
// Sets the AccountIDEndpointMode if present in env var or shared config profile
|
||||
resolveAccountIDEndpointMode,
|
||||
|
||||
// Sets the RequestChecksumCalculation if present in env var or shared config profile
|
||||
resolveRequestChecksumCalculation,
|
||||
|
||||
// Sets the ResponseChecksumValidation if present in env var or shared config profile
|
||||
resolveResponseChecksumValidation,
|
||||
}
|
||||
|
||||
// A Config represents a generic configuration value or set of values. This type
|
||||
@@ -212,7 +218,7 @@ func resolveConfigLoaders(options *LoadOptions) []loader {
|
||||
loaders[0] = loadEnvConfig
|
||||
|
||||
// specification of a profile should cause a load failure if it doesn't exist
|
||||
if os.Getenv(awsProfileEnvVar) != "" || options.SharedConfigProfile != "" {
|
||||
if os.Getenv(awsProfileEnv) != "" || options.SharedConfigProfile != "" {
|
||||
loaders[1] = loadSharedConfig
|
||||
} else {
|
||||
loaders[1] = loadSharedConfigIgnoreNotExist
|
||||
|
||||
+135
-73
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -21,86 +20,89 @@ const CredentialsSourceName = "EnvConfigCredentials"
|
||||
|
||||
// Environment variables that will be read for configuration values.
|
||||
const (
|
||||
awsAccessKeyIDEnvVar = "AWS_ACCESS_KEY_ID"
|
||||
awsAccessKeyEnvVar = "AWS_ACCESS_KEY"
|
||||
awsAccessKeyIDEnv = "AWS_ACCESS_KEY_ID"
|
||||
awsAccessKeyEnv = "AWS_ACCESS_KEY"
|
||||
|
||||
awsSecretAccessKeyEnvVar = "AWS_SECRET_ACCESS_KEY"
|
||||
awsSecretKeyEnvVar = "AWS_SECRET_KEY"
|
||||
awsSecretAccessKeyEnv = "AWS_SECRET_ACCESS_KEY"
|
||||
awsSecretKeyEnv = "AWS_SECRET_KEY"
|
||||
|
||||
awsSessionTokenEnvVar = "AWS_SESSION_TOKEN"
|
||||
awsSessionTokenEnv = "AWS_SESSION_TOKEN"
|
||||
|
||||
awsContainerCredentialsEndpointEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
|
||||
awsContainerCredentialsRelativePathEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
|
||||
awsContainerPProviderAuthorizationEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
|
||||
awsContainerCredentialsFullURIEnv = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
|
||||
awsContainerCredentialsRelativeURIEnv = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
|
||||
awsContainerAuthorizationTokenEnv = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
|
||||
|
||||
awsRegionEnvVar = "AWS_REGION"
|
||||
awsDefaultRegionEnvVar = "AWS_DEFAULT_REGION"
|
||||
awsRegionEnv = "AWS_REGION"
|
||||
awsDefaultRegionEnv = "AWS_DEFAULT_REGION"
|
||||
|
||||
awsProfileEnvVar = "AWS_PROFILE"
|
||||
awsDefaultProfileEnvVar = "AWS_DEFAULT_PROFILE"
|
||||
awsProfileEnv = "AWS_PROFILE"
|
||||
awsDefaultProfileEnv = "AWS_DEFAULT_PROFILE"
|
||||
|
||||
awsSharedCredentialsFileEnvVar = "AWS_SHARED_CREDENTIALS_FILE"
|
||||
awsSharedCredentialsFileEnv = "AWS_SHARED_CREDENTIALS_FILE"
|
||||
|
||||
awsConfigFileEnvVar = "AWS_CONFIG_FILE"
|
||||
awsConfigFileEnv = "AWS_CONFIG_FILE"
|
||||
|
||||
awsCustomCABundleEnvVar = "AWS_CA_BUNDLE"
|
||||
awsCABundleEnv = "AWS_CA_BUNDLE"
|
||||
|
||||
awsWebIdentityTokenFilePathEnvVar = "AWS_WEB_IDENTITY_TOKEN_FILE"
|
||||
awsWebIdentityTokenFileEnv = "AWS_WEB_IDENTITY_TOKEN_FILE"
|
||||
|
||||
awsRoleARNEnvVar = "AWS_ROLE_ARN"
|
||||
awsRoleSessionNameEnvVar = "AWS_ROLE_SESSION_NAME"
|
||||
awsRoleARNEnv = "AWS_ROLE_ARN"
|
||||
awsRoleSessionNameEnv = "AWS_ROLE_SESSION_NAME"
|
||||
|
||||
awsEnableEndpointDiscoveryEnvVar = "AWS_ENABLE_ENDPOINT_DISCOVERY"
|
||||
awsEnableEndpointDiscoveryEnv = "AWS_ENABLE_ENDPOINT_DISCOVERY"
|
||||
|
||||
awsS3UseARNRegionEnvVar = "AWS_S3_USE_ARN_REGION"
|
||||
awsS3UseARNRegionEnv = "AWS_S3_USE_ARN_REGION"
|
||||
|
||||
awsEc2MetadataServiceEndpointModeEnvVar = "AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE"
|
||||
awsEc2MetadataServiceEndpointModeEnv = "AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE"
|
||||
|
||||
awsEc2MetadataServiceEndpointEnvVar = "AWS_EC2_METADATA_SERVICE_ENDPOINT"
|
||||
awsEc2MetadataServiceEndpointEnv = "AWS_EC2_METADATA_SERVICE_ENDPOINT"
|
||||
|
||||
awsEc2MetadataDisabled = "AWS_EC2_METADATA_DISABLED"
|
||||
awsEc2MetadataV1DisabledEnvVar = "AWS_EC2_METADATA_V1_DISABLED"
|
||||
awsEc2MetadataDisabledEnv = "AWS_EC2_METADATA_DISABLED"
|
||||
awsEc2MetadataV1DisabledEnv = "AWS_EC2_METADATA_V1_DISABLED"
|
||||
|
||||
awsS3DisableMultiRegionAccessPointEnvVar = "AWS_S3_DISABLE_MULTIREGION_ACCESS_POINTS"
|
||||
awsS3DisableMultiRegionAccessPointsEnv = "AWS_S3_DISABLE_MULTIREGION_ACCESS_POINTS"
|
||||
|
||||
awsUseDualStackEndpoint = "AWS_USE_DUALSTACK_ENDPOINT"
|
||||
awsUseDualStackEndpointEnv = "AWS_USE_DUALSTACK_ENDPOINT"
|
||||
|
||||
awsUseFIPSEndpoint = "AWS_USE_FIPS_ENDPOINT"
|
||||
awsUseFIPSEndpointEnv = "AWS_USE_FIPS_ENDPOINT"
|
||||
|
||||
awsDefaultMode = "AWS_DEFAULTS_MODE"
|
||||
awsDefaultsModeEnv = "AWS_DEFAULTS_MODE"
|
||||
|
||||
awsRetryMaxAttempts = "AWS_MAX_ATTEMPTS"
|
||||
awsRetryMode = "AWS_RETRY_MODE"
|
||||
awsSdkAppID = "AWS_SDK_UA_APP_ID"
|
||||
awsMaxAttemptsEnv = "AWS_MAX_ATTEMPTS"
|
||||
awsRetryModeEnv = "AWS_RETRY_MODE"
|
||||
awsSdkUaAppIDEnv = "AWS_SDK_UA_APP_ID"
|
||||
|
||||
awsIgnoreConfiguredEndpoints = "AWS_IGNORE_CONFIGURED_ENDPOINT_URLS"
|
||||
awsEndpointURL = "AWS_ENDPOINT_URL"
|
||||
awsIgnoreConfiguredEndpointURLEnv = "AWS_IGNORE_CONFIGURED_ENDPOINT_URLS"
|
||||
awsEndpointURLEnv = "AWS_ENDPOINT_URL"
|
||||
|
||||
awsDisableRequestCompression = "AWS_DISABLE_REQUEST_COMPRESSION"
|
||||
awsRequestMinCompressionSizeBytes = "AWS_REQUEST_MIN_COMPRESSION_SIZE_BYTES"
|
||||
awsDisableRequestCompressionEnv = "AWS_DISABLE_REQUEST_COMPRESSION"
|
||||
awsRequestMinCompressionSizeBytesEnv = "AWS_REQUEST_MIN_COMPRESSION_SIZE_BYTES"
|
||||
|
||||
awsS3DisableExpressSessionAuthEnv = "AWS_S3_DISABLE_EXPRESS_SESSION_AUTH"
|
||||
|
||||
awsAccountIDEnv = "AWS_ACCOUNT_ID"
|
||||
awsAccountIDEndpointModeEnv = "AWS_ACCOUNT_ID_ENDPOINT_MODE"
|
||||
|
||||
awsRequestChecksumCalculation = "AWS_REQUEST_CHECKSUM_CALCULATION"
|
||||
awsResponseChecksumValidation = "AWS_RESPONSE_CHECKSUM_VALIDATION"
|
||||
)
|
||||
|
||||
var (
|
||||
credAccessEnvKeys = []string{
|
||||
awsAccessKeyIDEnvVar,
|
||||
awsAccessKeyEnvVar,
|
||||
awsAccessKeyIDEnv,
|
||||
awsAccessKeyEnv,
|
||||
}
|
||||
credSecretEnvKeys = []string{
|
||||
awsSecretAccessKeyEnvVar,
|
||||
awsSecretKeyEnvVar,
|
||||
awsSecretAccessKeyEnv,
|
||||
awsSecretKeyEnv,
|
||||
}
|
||||
regionEnvKeys = []string{
|
||||
awsRegionEnvVar,
|
||||
awsDefaultRegionEnvVar,
|
||||
awsRegionEnv,
|
||||
awsDefaultRegionEnv,
|
||||
}
|
||||
profileEnvKeys = []string{
|
||||
awsProfileEnvVar,
|
||||
awsDefaultProfileEnvVar,
|
||||
awsProfileEnv,
|
||||
awsDefaultProfileEnv,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -296,6 +298,12 @@ type EnvConfig struct {
|
||||
|
||||
// Indicates whether account ID will be required/ignored in endpoint2.0 routing
|
||||
AccountIDEndpointMode aws.AccountIDEndpointMode
|
||||
|
||||
// Indicates whether request checksum should be calculated
|
||||
RequestChecksumCalculation aws.RequestChecksumCalculation
|
||||
|
||||
// Indicates whether response checksum should be validated
|
||||
ResponseChecksumValidation aws.ResponseChecksumValidation
|
||||
}
|
||||
|
||||
// loadEnvConfig reads configuration values from the OS's environment variables.
|
||||
@@ -316,79 +324,79 @@ func NewEnvConfig() (EnvConfig, error) {
|
||||
setStringFromEnvVal(&creds.SecretAccessKey, credSecretEnvKeys)
|
||||
if creds.HasKeys() {
|
||||
creds.AccountID = os.Getenv(awsAccountIDEnv)
|
||||
creds.SessionToken = os.Getenv(awsSessionTokenEnvVar)
|
||||
creds.SessionToken = os.Getenv(awsSessionTokenEnv)
|
||||
cfg.Credentials = creds
|
||||
}
|
||||
|
||||
cfg.ContainerCredentialsEndpoint = os.Getenv(awsContainerCredentialsEndpointEnvVar)
|
||||
cfg.ContainerCredentialsRelativePath = os.Getenv(awsContainerCredentialsRelativePathEnvVar)
|
||||
cfg.ContainerAuthorizationToken = os.Getenv(awsContainerPProviderAuthorizationEnvVar)
|
||||
cfg.ContainerCredentialsEndpoint = os.Getenv(awsContainerCredentialsFullURIEnv)
|
||||
cfg.ContainerCredentialsRelativePath = os.Getenv(awsContainerCredentialsRelativeURIEnv)
|
||||
cfg.ContainerAuthorizationToken = os.Getenv(awsContainerAuthorizationTokenEnv)
|
||||
|
||||
setStringFromEnvVal(&cfg.Region, regionEnvKeys)
|
||||
setStringFromEnvVal(&cfg.SharedConfigProfile, profileEnvKeys)
|
||||
|
||||
cfg.SharedCredentialsFile = os.Getenv(awsSharedCredentialsFileEnvVar)
|
||||
cfg.SharedConfigFile = os.Getenv(awsConfigFileEnvVar)
|
||||
cfg.SharedCredentialsFile = os.Getenv(awsSharedCredentialsFileEnv)
|
||||
cfg.SharedConfigFile = os.Getenv(awsConfigFileEnv)
|
||||
|
||||
cfg.CustomCABundle = os.Getenv(awsCustomCABundleEnvVar)
|
||||
cfg.CustomCABundle = os.Getenv(awsCABundleEnv)
|
||||
|
||||
cfg.WebIdentityTokenFilePath = os.Getenv(awsWebIdentityTokenFilePathEnvVar)
|
||||
cfg.WebIdentityTokenFilePath = os.Getenv(awsWebIdentityTokenFileEnv)
|
||||
|
||||
cfg.RoleARN = os.Getenv(awsRoleARNEnvVar)
|
||||
cfg.RoleSessionName = os.Getenv(awsRoleSessionNameEnvVar)
|
||||
cfg.RoleARN = os.Getenv(awsRoleARNEnv)
|
||||
cfg.RoleSessionName = os.Getenv(awsRoleSessionNameEnv)
|
||||
|
||||
cfg.AppID = os.Getenv(awsSdkAppID)
|
||||
cfg.AppID = os.Getenv(awsSdkUaAppIDEnv)
|
||||
|
||||
if err := setBoolPtrFromEnvVal(&cfg.DisableRequestCompression, []string{awsDisableRequestCompression}); err != nil {
|
||||
if err := setBoolPtrFromEnvVal(&cfg.DisableRequestCompression, []string{awsDisableRequestCompressionEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if err := setInt64PtrFromEnvVal(&cfg.RequestMinCompressSizeBytes, []string{awsRequestMinCompressionSizeBytes}, smithyrequestcompression.MaxRequestMinCompressSizeBytes); err != nil {
|
||||
if err := setInt64PtrFromEnvVal(&cfg.RequestMinCompressSizeBytes, []string{awsRequestMinCompressionSizeBytesEnv}, smithyrequestcompression.MaxRequestMinCompressSizeBytes); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setEndpointDiscoveryTypeFromEnvVal(&cfg.EnableEndpointDiscovery, []string{awsEnableEndpointDiscoveryEnvVar}); err != nil {
|
||||
if err := setEndpointDiscoveryTypeFromEnvVal(&cfg.EnableEndpointDiscovery, []string{awsEnableEndpointDiscoveryEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setBoolPtrFromEnvVal(&cfg.S3UseARNRegion, []string{awsS3UseARNRegionEnvVar}); err != nil {
|
||||
if err := setBoolPtrFromEnvVal(&cfg.S3UseARNRegion, []string{awsS3UseARNRegionEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
setEC2IMDSClientEnableState(&cfg.EC2IMDSClientEnableState, []string{awsEc2MetadataDisabled})
|
||||
if err := setEC2IMDSEndpointMode(&cfg.EC2IMDSEndpointMode, []string{awsEc2MetadataServiceEndpointModeEnvVar}); err != nil {
|
||||
setEC2IMDSClientEnableState(&cfg.EC2IMDSClientEnableState, []string{awsEc2MetadataDisabledEnv})
|
||||
if err := setEC2IMDSEndpointMode(&cfg.EC2IMDSEndpointMode, []string{awsEc2MetadataServiceEndpointModeEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
cfg.EC2IMDSEndpoint = os.Getenv(awsEc2MetadataServiceEndpointEnvVar)
|
||||
if err := setBoolPtrFromEnvVal(&cfg.EC2IMDSv1Disabled, []string{awsEc2MetadataV1DisabledEnvVar}); err != nil {
|
||||
cfg.EC2IMDSEndpoint = os.Getenv(awsEc2MetadataServiceEndpointEnv)
|
||||
if err := setBoolPtrFromEnvVal(&cfg.EC2IMDSv1Disabled, []string{awsEc2MetadataV1DisabledEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setBoolPtrFromEnvVal(&cfg.S3DisableMultiRegionAccessPoints, []string{awsS3DisableMultiRegionAccessPointEnvVar}); err != nil {
|
||||
if err := setBoolPtrFromEnvVal(&cfg.S3DisableMultiRegionAccessPoints, []string{awsS3DisableMultiRegionAccessPointsEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setUseDualStackEndpointFromEnvVal(&cfg.UseDualStackEndpoint, []string{awsUseDualStackEndpoint}); err != nil {
|
||||
if err := setUseDualStackEndpointFromEnvVal(&cfg.UseDualStackEndpoint, []string{awsUseDualStackEndpointEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setUseFIPSEndpointFromEnvVal(&cfg.UseFIPSEndpoint, []string{awsUseFIPSEndpoint}); err != nil {
|
||||
if err := setUseFIPSEndpointFromEnvVal(&cfg.UseFIPSEndpoint, []string{awsUseFIPSEndpointEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setDefaultsModeFromEnvVal(&cfg.DefaultsMode, []string{awsDefaultMode}); err != nil {
|
||||
if err := setDefaultsModeFromEnvVal(&cfg.DefaultsMode, []string{awsDefaultsModeEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setIntFromEnvVal(&cfg.RetryMaxAttempts, []string{awsRetryMaxAttempts}); err != nil {
|
||||
if err := setIntFromEnvVal(&cfg.RetryMaxAttempts, []string{awsMaxAttemptsEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if err := setRetryModeFromEnvVal(&cfg.RetryMode, []string{awsRetryMode}); err != nil {
|
||||
if err := setRetryModeFromEnvVal(&cfg.RetryMode, []string{awsRetryModeEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
setStringFromEnvVal(&cfg.BaseEndpoint, []string{awsEndpointURL})
|
||||
setStringFromEnvVal(&cfg.BaseEndpoint, []string{awsEndpointURLEnv})
|
||||
|
||||
if err := setBoolPtrFromEnvVal(&cfg.IgnoreConfiguredEndpoints, []string{awsIgnoreConfiguredEndpoints}); err != nil {
|
||||
if err := setBoolPtrFromEnvVal(&cfg.IgnoreConfiguredEndpoints, []string{awsIgnoreConfiguredEndpointURLEnv}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
@@ -400,6 +408,13 @@ func NewEnvConfig() (EnvConfig, error) {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setRequestChecksumCalculationFromEnvVal(&cfg.RequestChecksumCalculation, []string{awsRequestChecksumCalculation}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if err := setResponseChecksumValidationFromEnvVal(&cfg.ResponseChecksumValidation, []string{awsResponseChecksumValidation}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
@@ -432,6 +447,14 @@ func (c EnvConfig) getAccountIDEndpointMode(context.Context) (aws.AccountIDEndpo
|
||||
return c.AccountIDEndpointMode, len(c.AccountIDEndpointMode) > 0, nil
|
||||
}
|
||||
|
||||
func (c EnvConfig) getRequestChecksumCalculation(context.Context) (aws.RequestChecksumCalculation, bool, error) {
|
||||
return c.RequestChecksumCalculation, c.RequestChecksumCalculation > 0, nil
|
||||
}
|
||||
|
||||
func (c EnvConfig) getResponseChecksumValidation(context.Context) (aws.ResponseChecksumValidation, bool, error) {
|
||||
return c.ResponseChecksumValidation, c.ResponseChecksumValidation > 0, nil
|
||||
}
|
||||
|
||||
// GetRetryMaxAttempts returns the value of AWS_MAX_ATTEMPTS if was specified,
|
||||
// and not 0.
|
||||
func (c EnvConfig) GetRetryMaxAttempts(ctx context.Context) (int, bool, error) {
|
||||
@@ -528,6 +551,45 @@ func setAIDEndPointModeFromEnvVal(m *aws.AccountIDEndpointMode, keys []string) e
|
||||
return nil
|
||||
}
|
||||
|
||||
func setRequestChecksumCalculationFromEnvVal(m *aws.RequestChecksumCalculation, keys []string) error {
|
||||
for _, k := range keys {
|
||||
value := os.Getenv(k)
|
||||
if len(value) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
switch strings.ToLower(value) {
|
||||
case checksumWhenSupported:
|
||||
*m = aws.RequestChecksumCalculationWhenSupported
|
||||
case checksumWhenRequired:
|
||||
*m = aws.RequestChecksumCalculationWhenRequired
|
||||
default:
|
||||
return fmt.Errorf("invalid value for environment variable, %s=%s, must be when_supported/when_required", k, value)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setResponseChecksumValidationFromEnvVal(m *aws.ResponseChecksumValidation, keys []string) error {
|
||||
for _, k := range keys {
|
||||
value := os.Getenv(k)
|
||||
if len(value) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
switch strings.ToLower(value) {
|
||||
case checksumWhenSupported:
|
||||
*m = aws.ResponseChecksumValidationWhenSupported
|
||||
case checksumWhenRequired:
|
||||
*m = aws.ResponseChecksumValidationWhenRequired
|
||||
default:
|
||||
return fmt.Errorf("invalid value for environment variable, %s=%s, must be when_supported/when_required", k, value)
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRegion returns the AWS Region if set in the environment. Returns an empty
|
||||
// string if not set.
|
||||
func (c EnvConfig) getRegion(ctx context.Context) (string, bool, error) {
|
||||
@@ -584,7 +646,7 @@ func (c EnvConfig) getCustomCABundle(context.Context) (io.Reader, bool, error) {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(c.CustomCABundle)
|
||||
b, err := os.ReadFile(c.CustomCABundle)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
@@ -608,7 +670,7 @@ func (c EnvConfig) getBaseEndpoint(context.Context) (string, bool, error) {
|
||||
// GetServiceBaseEndpoint is used to retrieve a normalized SDK ID for use
|
||||
// with configured endpoints.
|
||||
func (c EnvConfig) GetServiceBaseEndpoint(ctx context.Context, sdkID string) (string, bool, error) {
|
||||
if endpt := os.Getenv(fmt.Sprintf("%s_%s", awsEndpointURL, normalizeEnv(sdkID))); endpt != "" {
|
||||
if endpt := os.Getenv(fmt.Sprintf("%s_%s", awsEndpointURLEnv, normalizeEnv(sdkID))); endpt != "" {
|
||||
return endpt, true, nil
|
||||
}
|
||||
return "", false, nil
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
package config
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.28.5"
|
||||
const goModuleVersion = "1.29.4"
|
||||
|
||||
+35
@@ -216,8 +216,15 @@ type LoadOptions struct {
|
||||
// Whether S3 Express auth is disabled.
|
||||
S3DisableExpressAuth *bool
|
||||
|
||||
// Whether account id should be built into endpoint resolution
|
||||
AccountIDEndpointMode aws.AccountIDEndpointMode
|
||||
|
||||
// Specify if request checksum should be calculated
|
||||
RequestChecksumCalculation aws.RequestChecksumCalculation
|
||||
|
||||
// Specifies if response checksum should be validated
|
||||
ResponseChecksumValidation aws.ResponseChecksumValidation
|
||||
|
||||
// Service endpoint override. This value is not necessarily final and is
|
||||
// passed to the service's EndpointResolverV2 for further delegation.
|
||||
BaseEndpoint string
|
||||
@@ -288,6 +295,14 @@ func (o LoadOptions) getAccountIDEndpointMode(ctx context.Context) (aws.AccountI
|
||||
return o.AccountIDEndpointMode, len(o.AccountIDEndpointMode) > 0, nil
|
||||
}
|
||||
|
||||
func (o LoadOptions) getRequestChecksumCalculation(ctx context.Context) (aws.RequestChecksumCalculation, bool, error) {
|
||||
return o.RequestChecksumCalculation, o.RequestChecksumCalculation > 0, nil
|
||||
}
|
||||
|
||||
func (o LoadOptions) getResponseChecksumValidation(ctx context.Context) (aws.ResponseChecksumValidation, bool, error) {
|
||||
return o.ResponseChecksumValidation, o.ResponseChecksumValidation > 0, nil
|
||||
}
|
||||
|
||||
func (o LoadOptions) getBaseEndpoint(context.Context) (string, bool, error) {
|
||||
return o.BaseEndpoint, o.BaseEndpoint != "", nil
|
||||
}
|
||||
@@ -357,6 +372,26 @@ func WithAccountIDEndpointMode(m aws.AccountIDEndpointMode) LoadOptionsFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// WithRequestChecksumCalculation is a helper function to construct functional options
|
||||
// that sets RequestChecksumCalculation on config's LoadOptions
|
||||
func WithRequestChecksumCalculation(c aws.RequestChecksumCalculation) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
if c > 0 {
|
||||
o.RequestChecksumCalculation = c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithResponseChecksumValidation is a helper function to construct functional options
|
||||
// that sets ResponseChecksumValidation on config's LoadOptions
|
||||
func WithResponseChecksumValidation(v aws.ResponseChecksumValidation) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.ResponseChecksumValidation = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getDefaultRegion returns DefaultRegion from config's LoadOptions
|
||||
func (o LoadOptions) getDefaultRegion(ctx context.Context) (string, bool, error) {
|
||||
if len(o.DefaultRegion) == 0 {
|
||||
|
||||
+34
@@ -242,6 +242,40 @@ func getAccountIDEndpointMode(ctx context.Context, configs configs) (value aws.A
|
||||
return
|
||||
}
|
||||
|
||||
// requestChecksumCalculationProvider provides access to the RequestChecksumCalculation
|
||||
type requestChecksumCalculationProvider interface {
|
||||
getRequestChecksumCalculation(context.Context) (aws.RequestChecksumCalculation, bool, error)
|
||||
}
|
||||
|
||||
func getRequestChecksumCalculation(ctx context.Context, configs configs) (value aws.RequestChecksumCalculation, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if p, ok := cfg.(requestChecksumCalculationProvider); ok {
|
||||
value, found, err = p.getRequestChecksumCalculation(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// responseChecksumValidationProvider provides access to the ResponseChecksumValidation
|
||||
type responseChecksumValidationProvider interface {
|
||||
getResponseChecksumValidation(context.Context) (aws.ResponseChecksumValidation, bool, error)
|
||||
}
|
||||
|
||||
func getResponseChecksumValidation(ctx context.Context, configs configs) (value aws.ResponseChecksumValidation, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if p, ok := cfg.(responseChecksumValidationProvider); ok {
|
||||
value, found, err = p.getResponseChecksumValidation(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ec2IMDSRegionProvider provides access to the ec2 imds region
|
||||
// configuration value
|
||||
type ec2IMDSRegionProvider interface {
|
||||
|
||||
+30
@@ -182,6 +182,36 @@ func resolveAccountIDEndpointMode(ctx context.Context, cfg *aws.Config, configs
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveRequestChecksumCalculation extracts the RequestChecksumCalculation from the configs slice's
|
||||
// SharedConfig or EnvConfig
|
||||
func resolveRequestChecksumCalculation(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
c, found, err := getRequestChecksumCalculation(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !found {
|
||||
c = aws.RequestChecksumCalculationWhenSupported
|
||||
}
|
||||
cfg.RequestChecksumCalculation = c
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveResponseValidation extracts the ResponseChecksumValidation from the configs slice's
|
||||
// SharedConfig or EnvConfig
|
||||
func resolveResponseChecksumValidation(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
c, found, err := getResponseChecksumValidation(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !found {
|
||||
c = aws.ResponseChecksumValidationWhenSupported
|
||||
}
|
||||
cfg.ResponseChecksumValidation = c
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveDefaultRegion extracts the first instance of a default region and sets `aws.Config.Region` to the default
|
||||
// region if region had not been resolved from other sources.
|
||||
func resolveDefaultRegion(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
|
||||
+62
@@ -118,6 +118,11 @@ const (
|
||||
|
||||
accountIDKey = "aws_account_id"
|
||||
accountIDEndpointMode = "account_id_endpoint_mode"
|
||||
|
||||
requestChecksumCalculationKey = "request_checksum_calculation"
|
||||
responseChecksumValidationKey = "response_checksum_validation"
|
||||
checksumWhenSupported = "when_supported"
|
||||
checksumWhenRequired = "when_required"
|
||||
)
|
||||
|
||||
// defaultSharedConfigProfile allows for swapping the default profile for testing
|
||||
@@ -346,6 +351,12 @@ type SharedConfig struct {
|
||||
S3DisableExpressAuth *bool
|
||||
|
||||
AccountIDEndpointMode aws.AccountIDEndpointMode
|
||||
|
||||
// RequestChecksumCalculation indicates if the request checksum should be calculated
|
||||
RequestChecksumCalculation aws.RequestChecksumCalculation
|
||||
|
||||
// ResponseChecksumValidation indicates if the response checksum should be validated
|
||||
ResponseChecksumValidation aws.ResponseChecksumValidation
|
||||
}
|
||||
|
||||
func (c SharedConfig) getDefaultsMode(ctx context.Context) (value aws.DefaultsMode, ok bool, err error) {
|
||||
@@ -1133,6 +1144,13 @@ func (c *SharedConfig) setFromIniSection(profile string, section ini.Section) er
|
||||
return fmt.Errorf("failed to load %s from shared config, %w", accountIDEndpointMode, err)
|
||||
}
|
||||
|
||||
if err := updateRequestChecksumCalculation(&c.RequestChecksumCalculation, section, requestChecksumCalculationKey); err != nil {
|
||||
return fmt.Errorf("failed to load %s from shared config, %w", requestChecksumCalculationKey, err)
|
||||
}
|
||||
if err := updateResponseChecksumValidation(&c.ResponseChecksumValidation, section, responseChecksumValidationKey); err != nil {
|
||||
return fmt.Errorf("failed to load %s from shared config, %w", responseChecksumValidationKey, err)
|
||||
}
|
||||
|
||||
// Shared Credentials
|
||||
creds := aws.Credentials{
|
||||
AccessKeyID: section.String(accessKeyIDKey),
|
||||
@@ -1207,6 +1225,42 @@ func updateAIDEndpointMode(m *aws.AccountIDEndpointMode, sec ini.Section, key st
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateRequestChecksumCalculation(m *aws.RequestChecksumCalculation, sec ini.Section, key string) error {
|
||||
if !sec.Has(key) {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := sec.String(key)
|
||||
switch strings.ToLower(v) {
|
||||
case checksumWhenSupported:
|
||||
*m = aws.RequestChecksumCalculationWhenSupported
|
||||
case checksumWhenRequired:
|
||||
*m = aws.RequestChecksumCalculationWhenRequired
|
||||
default:
|
||||
return fmt.Errorf("invalid value for shared config profile field, %s=%s, must be when_supported/when_required", key, v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateResponseChecksumValidation(m *aws.ResponseChecksumValidation, sec ini.Section, key string) error {
|
||||
if !sec.Has(key) {
|
||||
return nil
|
||||
}
|
||||
|
||||
v := sec.String(key)
|
||||
switch strings.ToLower(v) {
|
||||
case checksumWhenSupported:
|
||||
*m = aws.ResponseChecksumValidationWhenSupported
|
||||
case checksumWhenRequired:
|
||||
*m = aws.ResponseChecksumValidationWhenRequired
|
||||
default:
|
||||
return fmt.Errorf("invalid value for shared config profile field, %s=%s, must be when_supported/when_required", key, v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c SharedConfig) getRequestMinCompressSizeBytes(ctx context.Context) (int64, bool, error) {
|
||||
if c.RequestMinCompressSizeBytes == nil {
|
||||
return 0, false, nil
|
||||
@@ -1225,6 +1279,14 @@ func (c SharedConfig) getAccountIDEndpointMode(ctx context.Context) (aws.Account
|
||||
return c.AccountIDEndpointMode, len(c.AccountIDEndpointMode) > 0, nil
|
||||
}
|
||||
|
||||
func (c SharedConfig) getRequestChecksumCalculation(ctx context.Context) (aws.RequestChecksumCalculation, bool, error) {
|
||||
return c.RequestChecksumCalculation, c.RequestChecksumCalculation > 0, nil
|
||||
}
|
||||
|
||||
func (c SharedConfig) getResponseChecksumValidation(ctx context.Context) (aws.ResponseChecksumValidation, bool, error) {
|
||||
return c.ResponseChecksumValidation, c.ResponseChecksumValidation > 0, nil
|
||||
}
|
||||
|
||||
func updateDefaultsMode(mode *aws.DefaultsMode, section ini.Section, key string) error {
|
||||
if !section.Has(key) {
|
||||
return nil
|
||||
|
||||
+46
@@ -1,3 +1,49 @@
|
||||
# v1.17.57 (2025-01-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.56 (2025-01-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.55 (2025-01-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v1.17.54 (2025-01-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.53 (2025-01-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.52 (2025-01-14)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.51 (2025-01-10)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.50 (2025-01-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.49 (2025-01-08)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.48 (2024-12-19)
|
||||
|
||||
* **Bug Fix**: Fix improper use of printf-style functions.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.47 (2024-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.46 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
package credentials
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.17.46"
|
||||
const goModuleVersion = "1.17.57"
|
||||
|
||||
+29
@@ -1,3 +1,32 @@
|
||||
# v1.16.27 (2025-01-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.26 (2025-01-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.25 (2025-01-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v1.16.24 (2025-01-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.23 (2025-01-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.22 (2024-12-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.21 (2024-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.20 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
package imds
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.16.20"
|
||||
const goModuleVersion = "1.16.27"
|
||||
|
||||
+29
@@ -1,3 +1,32 @@
|
||||
# v1.3.31 (2025-01-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.30 (2025-01-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.29 (2025-01-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v1.3.28 (2025-01-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.27 (2025-01-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.26 (2024-12-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.25 (2024-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.24 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
package configsources
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.3.24"
|
||||
const goModuleVersion = "1.3.31"
|
||||
|
||||
Generated
Vendored
+6
@@ -47,6 +47,9 @@
|
||||
"ap-southeast-5" : {
|
||||
"description" : "Asia Pacific (Malaysia)"
|
||||
},
|
||||
"ap-southeast-7" : {
|
||||
"description" : "Asia Pacific (Thailand)"
|
||||
},
|
||||
"aws-global" : {
|
||||
"description" : "AWS Standard global region"
|
||||
},
|
||||
@@ -89,6 +92,9 @@
|
||||
"me-south-1" : {
|
||||
"description" : "Middle East (Bahrain)"
|
||||
},
|
||||
"mx-central-1" : {
|
||||
"description" : "Mexico (Central)"
|
||||
},
|
||||
"sa-east-1" : {
|
||||
"description" : "South America (Sao Paulo)"
|
||||
},
|
||||
|
||||
+30
@@ -1,3 +1,33 @@
|
||||
# v2.6.31 (2025-01-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v2.6.30 (2025-01-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v2.6.29 (2025-01-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v2.6.28 (2025-01-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v2.6.27 (2025-01-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v2.6.26 (2024-12-19)
|
||||
|
||||
* **Bug Fix**: Fix improper use of printf-style functions.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v2.6.25 (2024-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v2.6.24 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
package endpoints
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "2.6.24"
|
||||
const goModuleVersion = "2.6.31"
|
||||
|
||||
+4
@@ -1,3 +1,7 @@
|
||||
# v1.8.2 (2025-01-24)
|
||||
|
||||
* **Bug Fix**: Refactor filepath.Walk to filepath.WalkDir
|
||||
|
||||
# v1.8.1 (2024-08-15)
|
||||
|
||||
* **Dependency Update**: Bump minimum Go version to 1.21.
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
package ini
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.8.1"
|
||||
const goModuleVersion = "1.8.2"
|
||||
|
||||
+325
@@ -0,0 +1,325 @@
|
||||
# v1.3.31 (2025-01-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.30 (2025-01-30)
|
||||
|
||||
* **Bug Fix**: Do not sign Transfer-Encoding header in Sigv4[a]. Fixes a signer mismatch issue with S3 Accelerate.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.29 (2025-01-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v1.3.28 (2025-01-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.27 (2025-01-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.26 (2024-12-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.25 (2024-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.24 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.23 (2024-11-06)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.22 (2024-10-28)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.21 (2024-10-08)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.20 (2024-10-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.19 (2024-10-04)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.18 (2024-09-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.17 (2024-09-03)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.16 (2024-08-15)
|
||||
|
||||
* **Dependency Update**: Bump minimum Go version to 1.21.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.15 (2024-07-10.2)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.14 (2024-07-10)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.13 (2024-06-28)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.12 (2024-06-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.11 (2024-06-18)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.10 (2024-06-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.9 (2024-06-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.8 (2024-06-03)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.7 (2024-05-16)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.6 (2024-05-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.5 (2024-03-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.4 (2024-03-18)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.3 (2024-03-07)
|
||||
|
||||
* **Bug Fix**: Remove dependency on go-cmp.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.2 (2024-02-23)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.1 (2024-02-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.0 (2024-02-13)
|
||||
|
||||
* **Feature**: Bump minimum Go version to 1.20 per our language support policy.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.10 (2024-01-04)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.9 (2023-12-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.8 (2023-12-01)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.7 (2023-11-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.6 (2023-11-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.5 (2023-11-28.2)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.4 (2023-11-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.3 (2023-11-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.2 (2023-11-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.1 (2023-11-01)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.0 (2023-10-31)
|
||||
|
||||
* **Feature**: **BREAKING CHANGE**: Bump minimum go version to 1.19 per the revised [go version support policy](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-aligns-with-go-release-policy-on-supported-runtimes/).
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.6 (2023-10-12)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.5 (2023-10-06)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.4 (2023-08-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.3 (2023-08-18)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.2 (2023-08-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.1 (2023-08-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.0 (2023-07-31)
|
||||
|
||||
* **Feature**: Adds support for smithy-modeled endpoint resolution. A new rules-based endpoint resolution will be added to the SDK which will supercede and deprecate existing endpoint resolution. Specifically, EndpointResolver will be deprecated while BaseEndpoint and EndpointResolverV2 will take its place. For more information, please see the Endpoints section in our Developer Guide.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.28 (2023-07-28)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.27 (2023-07-13)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.26 (2023-06-13)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.25 (2023-04-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.24 (2023-04-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.23 (2023-03-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.22 (2023-03-10)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.21 (2023-02-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.20 (2023-02-14)
|
||||
|
||||
* No change notes available for this release.
|
||||
|
||||
# v1.0.19 (2023-02-03)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.18 (2022-12-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.17 (2022-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.16 (2022-10-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.15 (2022-10-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.14 (2022-09-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.13 (2022-09-14)
|
||||
|
||||
* **Bug Fix**: Fixes an issues where an error from an underlying SigV4 credential provider would not be surfaced from the SigV4a credential provider. Contribution by [sakthipriyan-aqfer](https://github.com/sakthipriyan-aqfer).
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.12 (2022-09-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.11 (2022-08-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.10 (2022-08-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.9 (2022-08-11)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.8 (2022-08-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.7 (2022-08-08)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.6 (2022-08-01)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.5 (2022-07-05)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.4 (2022-06-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.3 (2022-06-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.2 (2022-05-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.1 (2022-04-25)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.0 (2022-04-07)
|
||||
|
||||
* **Release**: New internal v4a signing module location.
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package v4a
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
||||
)
|
||||
|
||||
// Credentials is Context, ECDSA, and Optional Session Token that can be used
|
||||
// to sign requests using SigV4a
|
||||
type Credentials struct {
|
||||
Context string
|
||||
PrivateKey *ecdsa.PrivateKey
|
||||
SessionToken string
|
||||
|
||||
// Time the credentials will expire.
|
||||
CanExpire bool
|
||||
Expires time.Time
|
||||
}
|
||||
|
||||
// Expired returns if the credentials have expired.
|
||||
func (v Credentials) Expired() bool {
|
||||
if v.CanExpire {
|
||||
return !v.Expires.After(sdk.NowTime())
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasKeys returns if the credentials keys are set.
|
||||
func (v Credentials) HasKeys() bool {
|
||||
return len(v.Context) > 0 && v.PrivateKey != nil
|
||||
}
|
||||
|
||||
// SymmetricCredentialAdaptor wraps a SigV4 AccessKey/SecretKey provider and adapts the credentials
|
||||
// to a ECDSA PrivateKey for signing with SiV4a
|
||||
type SymmetricCredentialAdaptor struct {
|
||||
SymmetricProvider aws.CredentialsProvider
|
||||
|
||||
asymmetric atomic.Value
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
// Retrieve retrieves symmetric credentials from the underlying provider.
|
||||
func (s *SymmetricCredentialAdaptor) Retrieve(ctx context.Context) (aws.Credentials, error) {
|
||||
symCreds, err := s.retrieveFromSymmetricProvider(ctx)
|
||||
if err != nil {
|
||||
return aws.Credentials{}, err
|
||||
}
|
||||
|
||||
if asymCreds := s.getCreds(); asymCreds == nil {
|
||||
return symCreds, nil
|
||||
}
|
||||
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
|
||||
asymCreds := s.getCreds()
|
||||
if asymCreds == nil {
|
||||
return symCreds, nil
|
||||
}
|
||||
|
||||
// if the context does not match the access key id clear it
|
||||
if asymCreds.Context != symCreds.AccessKeyID {
|
||||
s.asymmetric.Store((*Credentials)(nil))
|
||||
}
|
||||
|
||||
return symCreds, nil
|
||||
}
|
||||
|
||||
// RetrievePrivateKey returns credentials suitable for SigV4a signing
|
||||
func (s *SymmetricCredentialAdaptor) RetrievePrivateKey(ctx context.Context) (Credentials, error) {
|
||||
if asymCreds := s.getCreds(); asymCreds != nil {
|
||||
return *asymCreds, nil
|
||||
}
|
||||
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
|
||||
if asymCreds := s.getCreds(); asymCreds != nil {
|
||||
return *asymCreds, nil
|
||||
}
|
||||
|
||||
symmetricCreds, err := s.retrieveFromSymmetricProvider(ctx)
|
||||
if err != nil {
|
||||
return Credentials{}, fmt.Errorf("failed to retrieve symmetric credentials: %v", err)
|
||||
}
|
||||
|
||||
privateKey, err := deriveKeyFromAccessKeyPair(symmetricCreds.AccessKeyID, symmetricCreds.SecretAccessKey)
|
||||
if err != nil {
|
||||
return Credentials{}, fmt.Errorf("failed to derive assymetric key from credentials")
|
||||
}
|
||||
|
||||
creds := Credentials{
|
||||
Context: symmetricCreds.AccessKeyID,
|
||||
PrivateKey: privateKey,
|
||||
SessionToken: symmetricCreds.SessionToken,
|
||||
CanExpire: symmetricCreds.CanExpire,
|
||||
Expires: symmetricCreds.Expires,
|
||||
}
|
||||
|
||||
s.asymmetric.Store(&creds)
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
func (s *SymmetricCredentialAdaptor) getCreds() *Credentials {
|
||||
v := s.asymmetric.Load()
|
||||
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := v.(*Credentials)
|
||||
if c != nil && c.HasKeys() && !c.Expired() {
|
||||
return c
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SymmetricCredentialAdaptor) retrieveFromSymmetricProvider(ctx context.Context) (aws.Credentials, error) {
|
||||
credentials, err := s.SymmetricProvider.Retrieve(ctx)
|
||||
if err != nil {
|
||||
return aws.Credentials{}, err
|
||||
}
|
||||
|
||||
return credentials, nil
|
||||
}
|
||||
|
||||
// CredentialsProvider is the interface for a provider to retrieve credentials
|
||||
// to sign requests with.
|
||||
type CredentialsProvider interface {
|
||||
RetrievePrivateKey(context.Context) (Credentials, error)
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package v4a
|
||||
|
||||
import "fmt"
|
||||
|
||||
// SigningError indicates an error condition occurred while performing SigV4a signing
|
||||
type SigningError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *SigningError) Error() string {
|
||||
return fmt.Sprintf("failed to sign request: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying error cause
|
||||
func (e *SigningError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT.
|
||||
|
||||
package v4a
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.3.31"
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package crypto
|
||||
|
||||
import "fmt"
|
||||
|
||||
// ConstantTimeByteCompare is a constant-time byte comparison of x and y. This function performs an absolute comparison
|
||||
// if the two byte slices assuming they represent a big-endian number.
|
||||
//
|
||||
// error if len(x) != len(y)
|
||||
// -1 if x < y
|
||||
// 0 if x == y
|
||||
// +1 if x > y
|
||||
func ConstantTimeByteCompare(x, y []byte) (int, error) {
|
||||
if len(x) != len(y) {
|
||||
return 0, fmt.Errorf("slice lengths do not match")
|
||||
}
|
||||
|
||||
xLarger, yLarger := 0, 0
|
||||
|
||||
for i := 0; i < len(x); i++ {
|
||||
xByte, yByte := int(x[i]), int(y[i])
|
||||
|
||||
x := ((yByte - xByte) >> 8) & 1
|
||||
y := ((xByte - yByte) >> 8) & 1
|
||||
|
||||
xLarger |= x &^ yLarger
|
||||
yLarger |= y &^ xLarger
|
||||
}
|
||||
|
||||
return xLarger - yLarger, nil
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/hmac"
|
||||
"encoding/asn1"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
"math"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type ecdsaSignature struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
|
||||
// ECDSAKey takes the given elliptic curve, and private key (d) byte slice
|
||||
// and returns the private ECDSA key.
|
||||
func ECDSAKey(curve elliptic.Curve, d []byte) *ecdsa.PrivateKey {
|
||||
return ECDSAKeyFromPoint(curve, (&big.Int{}).SetBytes(d))
|
||||
}
|
||||
|
||||
// ECDSAKeyFromPoint takes the given elliptic curve and point and returns the
|
||||
// private and public keypair
|
||||
func ECDSAKeyFromPoint(curve elliptic.Curve, d *big.Int) *ecdsa.PrivateKey {
|
||||
pX, pY := curve.ScalarBaseMult(d.Bytes())
|
||||
|
||||
privKey := &ecdsa.PrivateKey{
|
||||
PublicKey: ecdsa.PublicKey{
|
||||
Curve: curve,
|
||||
X: pX,
|
||||
Y: pY,
|
||||
},
|
||||
D: d,
|
||||
}
|
||||
|
||||
return privKey
|
||||
}
|
||||
|
||||
// ECDSAPublicKey takes the provide curve and (x, y) coordinates and returns
|
||||
// *ecdsa.PublicKey. Returns an error if the given points are not on the curve.
|
||||
func ECDSAPublicKey(curve elliptic.Curve, x, y []byte) (*ecdsa.PublicKey, error) {
|
||||
xPoint := (&big.Int{}).SetBytes(x)
|
||||
yPoint := (&big.Int{}).SetBytes(y)
|
||||
|
||||
if !curve.IsOnCurve(xPoint, yPoint) {
|
||||
return nil, fmt.Errorf("point(%v, %v) is not on the given curve", xPoint.String(), yPoint.String())
|
||||
}
|
||||
|
||||
return &ecdsa.PublicKey{
|
||||
Curve: curve,
|
||||
X: xPoint,
|
||||
Y: yPoint,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// VerifySignature takes the provided public key, hash, and asn1 encoded signature and returns
|
||||
// whether the given signature is valid.
|
||||
func VerifySignature(key *ecdsa.PublicKey, hash []byte, signature []byte) (bool, error) {
|
||||
var ecdsaSignature ecdsaSignature
|
||||
|
||||
_, err := asn1.Unmarshal(signature, &ecdsaSignature)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return ecdsa.Verify(key, hash, ecdsaSignature.R, ecdsaSignature.S), nil
|
||||
}
|
||||
|
||||
// HMACKeyDerivation provides an implementation of a NIST-800-108 of a KDF (Key Derivation Function) in Counter Mode.
|
||||
// For the purposes of this implantation HMAC is used as the PRF (Pseudorandom function), where the value of
|
||||
// `r` is defined as a 4 byte counter.
|
||||
func HMACKeyDerivation(hash func() hash.Hash, bitLen int, key []byte, label, context []byte) ([]byte, error) {
|
||||
// verify that we won't overflow the counter
|
||||
n := int64(math.Ceil((float64(bitLen) / 8) / float64(hash().Size())))
|
||||
if n > 0x7FFFFFFF {
|
||||
return nil, fmt.Errorf("unable to derive key of size %d using 32-bit counter", bitLen)
|
||||
}
|
||||
|
||||
// verify the requested bit length is not larger then the length encoding size
|
||||
if int64(bitLen) > 0x7FFFFFFF {
|
||||
return nil, fmt.Errorf("bitLen is greater than 32-bits")
|
||||
}
|
||||
|
||||
fixedInput := bytes.NewBuffer(nil)
|
||||
fixedInput.Write(label)
|
||||
fixedInput.WriteByte(0x00)
|
||||
fixedInput.Write(context)
|
||||
if err := binary.Write(fixedInput, binary.BigEndian, int32(bitLen)); err != nil {
|
||||
return nil, fmt.Errorf("failed to write bit length to fixed input string: %v", err)
|
||||
}
|
||||
|
||||
var output []byte
|
||||
|
||||
h := hmac.New(hash, key)
|
||||
|
||||
for i := int64(1); i <= n; i++ {
|
||||
h.Reset()
|
||||
if err := binary.Write(h, binary.BigEndian, int32(i)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err := h.Write(fixedInput.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output = append(output, h.Sum(nil)...)
|
||||
}
|
||||
|
||||
return output[:bitLen/8], nil
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package v4
|
||||
|
||||
const (
|
||||
// EmptyStringSHA256 is the hex encoded sha256 value of an empty string
|
||||
EmptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
|
||||
|
||||
// UnsignedPayload indicates that the request payload body is unsigned
|
||||
UnsignedPayload = "UNSIGNED-PAYLOAD"
|
||||
|
||||
// AmzAlgorithmKey indicates the signing algorithm
|
||||
AmzAlgorithmKey = "X-Amz-Algorithm"
|
||||
|
||||
// AmzSecurityTokenKey indicates the security token to be used with temporary credentials
|
||||
AmzSecurityTokenKey = "X-Amz-Security-Token"
|
||||
|
||||
// AmzDateKey is the UTC timestamp for the request in the format YYYYMMDD'T'HHMMSS'Z'
|
||||
AmzDateKey = "X-Amz-Date"
|
||||
|
||||
// AmzCredentialKey is the access key ID and credential scope
|
||||
AmzCredentialKey = "X-Amz-Credential"
|
||||
|
||||
// AmzSignedHeadersKey is the set of headers signed for the request
|
||||
AmzSignedHeadersKey = "X-Amz-SignedHeaders"
|
||||
|
||||
// AmzSignatureKey is the query parameter to store the SigV4 signature
|
||||
AmzSignatureKey = "X-Amz-Signature"
|
||||
|
||||
// TimeFormat is the time format to be used in the X-Amz-Date header or query parameter
|
||||
TimeFormat = "20060102T150405Z"
|
||||
|
||||
// ShortTimeFormat is the shorten time format used in the credential scope
|
||||
ShortTimeFormat = "20060102"
|
||||
|
||||
// ContentSHAKey is the SHA256 of request body
|
||||
ContentSHAKey = "X-Amz-Content-Sha256"
|
||||
)
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
package v4
|
||||
|
||||
import (
|
||||
sdkstrings "github.com/aws/aws-sdk-go-v2/internal/strings"
|
||||
)
|
||||
|
||||
// Rules houses a set of Rule needed for validation of a
|
||||
// string value
|
||||
type Rules []Rule
|
||||
|
||||
// Rule interface allows for more flexible rules and just simply
|
||||
// checks whether or not a value adheres to that Rule
|
||||
type Rule interface {
|
||||
IsValid(value string) bool
|
||||
}
|
||||
|
||||
// IsValid will iterate through all rules and see if any rules
|
||||
// apply to the value and supports nested rules
|
||||
func (r Rules) IsValid(value string) bool {
|
||||
for _, rule := range r {
|
||||
if rule.IsValid(value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MapRule generic Rule for maps
|
||||
type MapRule map[string]struct{}
|
||||
|
||||
// IsValid for the map Rule satisfies whether it exists in the map
|
||||
func (m MapRule) IsValid(value string) bool {
|
||||
_, ok := m[value]
|
||||
return ok
|
||||
}
|
||||
|
||||
// AllowList is a generic Rule for whitelisting
|
||||
type AllowList struct {
|
||||
Rule
|
||||
}
|
||||
|
||||
// IsValid for AllowList checks if the value is within the AllowList
|
||||
func (w AllowList) IsValid(value string) bool {
|
||||
return w.Rule.IsValid(value)
|
||||
}
|
||||
|
||||
// DenyList is a generic Rule for blacklisting
|
||||
type DenyList struct {
|
||||
Rule
|
||||
}
|
||||
|
||||
// IsValid for AllowList checks if the value is within the AllowList
|
||||
func (b DenyList) IsValid(value string) bool {
|
||||
return !b.Rule.IsValid(value)
|
||||
}
|
||||
|
||||
// Patterns is a list of strings to match against
|
||||
type Patterns []string
|
||||
|
||||
// IsValid for Patterns checks each pattern and returns if a match has
|
||||
// been found
|
||||
func (p Patterns) IsValid(value string) bool {
|
||||
for _, pattern := range p {
|
||||
if sdkstrings.HasPrefixFold(value, pattern) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InclusiveRules rules allow for rules to depend on one another
|
||||
type InclusiveRules []Rule
|
||||
|
||||
// IsValid will return true if all rules are true
|
||||
func (r InclusiveRules) IsValid(value string) bool {
|
||||
for _, rule := range r {
|
||||
if !rule.IsValid(value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package v4
|
||||
|
||||
// IgnoredHeaders is a list of headers that are ignored during signing
|
||||
var IgnoredHeaders = Rules{
|
||||
DenyList{
|
||||
MapRule{
|
||||
"Authorization": struct{}{},
|
||||
"User-Agent": struct{}{},
|
||||
"X-Amzn-Trace-Id": struct{}{},
|
||||
"Transfer-Encoding": struct{}{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// RequiredSignedHeaders is a whitelist for Build canonical headers.
|
||||
var RequiredSignedHeaders = Rules{
|
||||
AllowList{
|
||||
MapRule{
|
||||
"Cache-Control": struct{}{},
|
||||
"Content-Disposition": struct{}{},
|
||||
"Content-Encoding": struct{}{},
|
||||
"Content-Language": struct{}{},
|
||||
"Content-Md5": struct{}{},
|
||||
"Content-Type": struct{}{},
|
||||
"Expires": struct{}{},
|
||||
"If-Match": struct{}{},
|
||||
"If-Modified-Since": struct{}{},
|
||||
"If-None-Match": struct{}{},
|
||||
"If-Unmodified-Since": struct{}{},
|
||||
"Range": struct{}{},
|
||||
"X-Amz-Acl": struct{}{},
|
||||
"X-Amz-Copy-Source": struct{}{},
|
||||
"X-Amz-Copy-Source-If-Match": struct{}{},
|
||||
"X-Amz-Copy-Source-If-Modified-Since": struct{}{},
|
||||
"X-Amz-Copy-Source-If-None-Match": struct{}{},
|
||||
"X-Amz-Copy-Source-If-Unmodified-Since": struct{}{},
|
||||
"X-Amz-Copy-Source-Range": struct{}{},
|
||||
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{},
|
||||
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": struct{}{},
|
||||
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
|
||||
"X-Amz-Grant-Full-control": struct{}{},
|
||||
"X-Amz-Grant-Read": struct{}{},
|
||||
"X-Amz-Grant-Read-Acp": struct{}{},
|
||||
"X-Amz-Grant-Write": struct{}{},
|
||||
"X-Amz-Grant-Write-Acp": struct{}{},
|
||||
"X-Amz-Metadata-Directive": struct{}{},
|
||||
"X-Amz-Mfa": struct{}{},
|
||||
"X-Amz-Request-Payer": struct{}{},
|
||||
"X-Amz-Server-Side-Encryption": struct{}{},
|
||||
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": struct{}{},
|
||||
"X-Amz-Server-Side-Encryption-Customer-Algorithm": struct{}{},
|
||||
"X-Amz-Server-Side-Encryption-Customer-Key": struct{}{},
|
||||
"X-Amz-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
|
||||
"X-Amz-Storage-Class": struct{}{},
|
||||
"X-Amz-Website-Redirect-Location": struct{}{},
|
||||
"X-Amz-Content-Sha256": struct{}{},
|
||||
"X-Amz-Tagging": struct{}{},
|
||||
},
|
||||
},
|
||||
Patterns{"X-Amz-Meta-"},
|
||||
}
|
||||
|
||||
// AllowedQueryHoisting is a whitelist for Build query headers. The boolean value
|
||||
// represents whether or not it is a pattern.
|
||||
var AllowedQueryHoisting = InclusiveRules{
|
||||
DenyList{RequiredSignedHeaders},
|
||||
Patterns{"X-Amz-"},
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package v4
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
// HMACSHA256 computes a HMAC-SHA256 of data given the provided key.
|
||||
func HMACSHA256(key []byte, data []byte) []byte {
|
||||
hash := hmac.New(sha256.New, key)
|
||||
hash.Write(data)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
package v4
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SanitizeHostForHeader removes default port from host and updates request.Host
|
||||
func SanitizeHostForHeader(r *http.Request) {
|
||||
host := getHost(r)
|
||||
port := portOnly(host)
|
||||
if port != "" && isDefaultPort(r.URL.Scheme, port) {
|
||||
r.Host = stripPort(host)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns host from request
|
||||
func getHost(r *http.Request) string {
|
||||
if r.Host != "" {
|
||||
return r.Host
|
||||
}
|
||||
|
||||
return r.URL.Host
|
||||
}
|
||||
|
||||
// Hostname returns u.Host, without any port number.
|
||||
//
|
||||
// If Host is an IPv6 literal with a port number, Hostname returns the
|
||||
// IPv6 literal without the square brackets. IPv6 literals may include
|
||||
// a zone identifier.
|
||||
//
|
||||
// Copied from the Go 1.8 standard library (net/url)
|
||||
func stripPort(hostport string) string {
|
||||
colon := strings.IndexByte(hostport, ':')
|
||||
if colon == -1 {
|
||||
return hostport
|
||||
}
|
||||
if i := strings.IndexByte(hostport, ']'); i != -1 {
|
||||
return strings.TrimPrefix(hostport[:i], "[")
|
||||
}
|
||||
return hostport[:colon]
|
||||
}
|
||||
|
||||
// Port returns the port part of u.Host, without the leading colon.
|
||||
// If u.Host doesn't contain a port, Port returns an empty string.
|
||||
//
|
||||
// Copied from the Go 1.8 standard library (net/url)
|
||||
func portOnly(hostport string) string {
|
||||
colon := strings.IndexByte(hostport, ':')
|
||||
if colon == -1 {
|
||||
return ""
|
||||
}
|
||||
if i := strings.Index(hostport, "]:"); i != -1 {
|
||||
return hostport[i+len("]:"):]
|
||||
}
|
||||
if strings.Contains(hostport, "]") {
|
||||
return ""
|
||||
}
|
||||
return hostport[colon+len(":"):]
|
||||
}
|
||||
|
||||
// Returns true if the specified URI is using the standard port
|
||||
// (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs)
|
||||
func isDefaultPort(scheme, port string) bool {
|
||||
if port == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
lowerCaseScheme := strings.ToLower(scheme)
|
||||
if (lowerCaseScheme == "http" && port == "80") || (lowerCaseScheme == "https" && port == "443") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package v4
|
||||
|
||||
import "time"
|
||||
|
||||
// SigningTime provides a wrapper around a time.Time which provides cached values for SigV4 signing.
|
||||
type SigningTime struct {
|
||||
time.Time
|
||||
timeFormat string
|
||||
shortTimeFormat string
|
||||
}
|
||||
|
||||
// NewSigningTime creates a new SigningTime given a time.Time
|
||||
func NewSigningTime(t time.Time) SigningTime {
|
||||
return SigningTime{
|
||||
Time: t,
|
||||
}
|
||||
}
|
||||
|
||||
// TimeFormat provides a time formatted in the X-Amz-Date format.
|
||||
func (m *SigningTime) TimeFormat() string {
|
||||
return m.format(&m.timeFormat, TimeFormat)
|
||||
}
|
||||
|
||||
// ShortTimeFormat provides a time formatted of 20060102.
|
||||
func (m *SigningTime) ShortTimeFormat() string {
|
||||
return m.format(&m.shortTimeFormat, ShortTimeFormat)
|
||||
}
|
||||
|
||||
func (m *SigningTime) format(target *string, format string) string {
|
||||
if len(*target) > 0 {
|
||||
return *target
|
||||
}
|
||||
v := m.Time.Format(format)
|
||||
*target = v
|
||||
return v
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package v4
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const doubleSpace = " "
|
||||
|
||||
// StripExcessSpaces will rewrite the passed in slice's string values to not
|
||||
// contain muliple side-by-side spaces.
|
||||
func StripExcessSpaces(str string) string {
|
||||
var j, k, l, m, spaces int
|
||||
// Trim trailing spaces
|
||||
for j = len(str) - 1; j >= 0 && str[j] == ' '; j-- {
|
||||
}
|
||||
|
||||
// Trim leading spaces
|
||||
for k = 0; k < j && str[k] == ' '; k++ {
|
||||
}
|
||||
str = str[k : j+1]
|
||||
|
||||
// Strip multiple spaces.
|
||||
j = strings.Index(str, doubleSpace)
|
||||
if j < 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
buf := []byte(str)
|
||||
for k, m, l = j, j, len(buf); k < l; k++ {
|
||||
if buf[k] == ' ' {
|
||||
if spaces == 0 {
|
||||
// First space.
|
||||
buf[m] = buf[k]
|
||||
m++
|
||||
}
|
||||
spaces++
|
||||
} else {
|
||||
// End of multiple spaces.
|
||||
spaces = 0
|
||||
buf[m] = buf[k]
|
||||
m++
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf[:m])
|
||||
}
|
||||
|
||||
// GetURIPath returns the escaped URI component from the provided URL
|
||||
func GetURIPath(u *url.URL) string {
|
||||
var uri string
|
||||
|
||||
if len(u.Opaque) > 0 {
|
||||
uri = "/" + strings.Join(strings.Split(u.Opaque, "/")[3:], "/")
|
||||
} else {
|
||||
uri = u.EscapedPath()
|
||||
}
|
||||
|
||||
if len(uri) == 0 {
|
||||
uri = "/"
|
||||
}
|
||||
|
||||
return uri
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package v4a
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
||||
internalauth "github.com/aws/aws-sdk-go-v2/internal/auth"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPSigner is SigV4a HTTP signer implementation
|
||||
type HTTPSigner interface {
|
||||
SignHTTP(ctx context.Context, credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, optfns ...func(*SignerOptions)) error
|
||||
}
|
||||
|
||||
// SignHTTPRequestMiddlewareOptions is the middleware options for constructing a SignHTTPRequestMiddleware.
|
||||
type SignHTTPRequestMiddlewareOptions struct {
|
||||
Credentials CredentialsProvider
|
||||
Signer HTTPSigner
|
||||
LogSigning bool
|
||||
}
|
||||
|
||||
// SignHTTPRequestMiddleware is a middleware for signing an HTTP request using SigV4a.
|
||||
type SignHTTPRequestMiddleware struct {
|
||||
credentials CredentialsProvider
|
||||
signer HTTPSigner
|
||||
logSigning bool
|
||||
}
|
||||
|
||||
// NewSignHTTPRequestMiddleware constructs a SignHTTPRequestMiddleware using the given SignHTTPRequestMiddlewareOptions.
|
||||
func NewSignHTTPRequestMiddleware(options SignHTTPRequestMiddlewareOptions) *SignHTTPRequestMiddleware {
|
||||
return &SignHTTPRequestMiddleware{
|
||||
credentials: options.Credentials,
|
||||
signer: options.Signer,
|
||||
logSigning: options.LogSigning,
|
||||
}
|
||||
}
|
||||
|
||||
// ID the middleware identifier.
|
||||
func (s *SignHTTPRequestMiddleware) ID() string {
|
||||
return "Signing"
|
||||
}
|
||||
|
||||
// HandleFinalize signs an HTTP request using SigV4a.
|
||||
func (s *SignHTTPRequestMiddleware) HandleFinalize(
|
||||
ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler,
|
||||
) (
|
||||
out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
if !hasCredentialProvider(s.credentials) {
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, fmt.Errorf("unexpected request middleware type %T", in.Request)
|
||||
}
|
||||
|
||||
signingName, signingRegion := awsmiddleware.GetSigningName(ctx), awsmiddleware.GetSigningRegion(ctx)
|
||||
payloadHash := v4.GetPayloadHash(ctx)
|
||||
if len(payloadHash) == 0 {
|
||||
return out, metadata, &SigningError{Err: fmt.Errorf("computed payload hash missing from context")}
|
||||
}
|
||||
|
||||
credentials, err := s.credentials.RetrievePrivateKey(ctx)
|
||||
if err != nil {
|
||||
return out, metadata, &SigningError{Err: fmt.Errorf("failed to retrieve credentials: %w", err)}
|
||||
}
|
||||
|
||||
signerOptions := []func(o *SignerOptions){
|
||||
func(o *SignerOptions) {
|
||||
o.Logger = middleware.GetLogger(ctx)
|
||||
o.LogSigning = s.logSigning
|
||||
},
|
||||
}
|
||||
|
||||
// existing DisableURIPathEscaping is equivalent in purpose
|
||||
// to authentication scheme property DisableDoubleEncoding
|
||||
disableDoubleEncoding, overridden := internalauth.GetDisableDoubleEncoding(ctx)
|
||||
if overridden {
|
||||
signerOptions = append(signerOptions, func(o *SignerOptions) {
|
||||
o.DisableURIPathEscaping = disableDoubleEncoding
|
||||
})
|
||||
}
|
||||
|
||||
err = s.signer.SignHTTP(ctx, credentials, req.Request, payloadHash, signingName, []string{signingRegion}, time.Now().UTC(), signerOptions...)
|
||||
if err != nil {
|
||||
return out, metadata, &SigningError{Err: fmt.Errorf("failed to sign http request, %w", err)}
|
||||
}
|
||||
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
|
||||
func hasCredentialProvider(p CredentialsProvider) bool {
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// RegisterSigningMiddleware registers the SigV4a signing middleware to the stack. If a signing middleware is already
|
||||
// present, this provided middleware will be swapped. Otherwise the middleware will be added at the tail of the
|
||||
// finalize step.
|
||||
func RegisterSigningMiddleware(stack *middleware.Stack, signingMiddleware *SignHTTPRequestMiddleware) (err error) {
|
||||
const signedID = "Signing"
|
||||
_, present := stack.Finalize.Get(signedID)
|
||||
if present {
|
||||
_, err = stack.Finalize.Swap(signedID, signingMiddleware)
|
||||
} else {
|
||||
err = stack.Finalize.Add(signingMiddleware, middleware.After)
|
||||
}
|
||||
return err
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
package v4a
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyHTTP "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// HTTPPresigner is an interface to a SigV4a signer that can sign create a
|
||||
// presigned URL for a HTTP requests.
|
||||
type HTTPPresigner interface {
|
||||
PresignHTTP(
|
||||
ctx context.Context, credentials Credentials, r *http.Request,
|
||||
payloadHash string, service string, regionSet []string, signingTime time.Time,
|
||||
optFns ...func(*SignerOptions),
|
||||
) (url string, signedHeader http.Header, err error)
|
||||
}
|
||||
|
||||
// PresignHTTPRequestMiddlewareOptions is the options for the PresignHTTPRequestMiddleware middleware.
|
||||
type PresignHTTPRequestMiddlewareOptions struct {
|
||||
CredentialsProvider CredentialsProvider
|
||||
Presigner HTTPPresigner
|
||||
LogSigning bool
|
||||
}
|
||||
|
||||
// PresignHTTPRequestMiddleware provides the Finalize middleware for creating a
|
||||
// presigned URL for an HTTP request.
|
||||
//
|
||||
// Will short circuit the middleware stack and not forward onto the next
|
||||
// Finalize handler.
|
||||
type PresignHTTPRequestMiddleware struct {
|
||||
credentialsProvider CredentialsProvider
|
||||
presigner HTTPPresigner
|
||||
logSigning bool
|
||||
}
|
||||
|
||||
// NewPresignHTTPRequestMiddleware returns a new PresignHTTPRequestMiddleware
|
||||
// initialized with the presigner.
|
||||
func NewPresignHTTPRequestMiddleware(options PresignHTTPRequestMiddlewareOptions) *PresignHTTPRequestMiddleware {
|
||||
return &PresignHTTPRequestMiddleware{
|
||||
credentialsProvider: options.CredentialsProvider,
|
||||
presigner: options.Presigner,
|
||||
logSigning: options.LogSigning,
|
||||
}
|
||||
}
|
||||
|
||||
// ID provides the middleware ID.
|
||||
func (*PresignHTTPRequestMiddleware) ID() string { return "PresignHTTPRequest" }
|
||||
|
||||
// HandleFinalize will take the provided input and create a presigned url for
|
||||
// the http request using the SigV4 presign authentication scheme.
|
||||
func (s *PresignHTTPRequestMiddleware) HandleFinalize(
|
||||
ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler,
|
||||
) (
|
||||
out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
req, ok := in.Request.(*smithyHTTP.Request)
|
||||
if !ok {
|
||||
return out, metadata, &SigningError{
|
||||
Err: fmt.Errorf("unexpected request middleware type %T", in.Request),
|
||||
}
|
||||
}
|
||||
|
||||
httpReq := req.Build(ctx)
|
||||
if !hasCredentialProvider(s.credentialsProvider) {
|
||||
out.Result = &v4.PresignedHTTPRequest{
|
||||
URL: httpReq.URL.String(),
|
||||
Method: httpReq.Method,
|
||||
SignedHeader: http.Header{},
|
||||
}
|
||||
|
||||
return out, metadata, nil
|
||||
}
|
||||
|
||||
signingName := awsmiddleware.GetSigningName(ctx)
|
||||
signingRegion := awsmiddleware.GetSigningRegion(ctx)
|
||||
payloadHash := v4.GetPayloadHash(ctx)
|
||||
if len(payloadHash) == 0 {
|
||||
return out, metadata, &SigningError{
|
||||
Err: fmt.Errorf("computed payload hash missing from context"),
|
||||
}
|
||||
}
|
||||
|
||||
credentials, err := s.credentialsProvider.RetrievePrivateKey(ctx)
|
||||
if err != nil {
|
||||
return out, metadata, &SigningError{
|
||||
Err: fmt.Errorf("failed to retrieve credentials: %w", err),
|
||||
}
|
||||
}
|
||||
|
||||
u, h, err := s.presigner.PresignHTTP(ctx, credentials,
|
||||
httpReq, payloadHash, signingName, []string{signingRegion}, sdk.NowTime(),
|
||||
func(o *SignerOptions) {
|
||||
o.Logger = middleware.GetLogger(ctx)
|
||||
o.LogSigning = s.logSigning
|
||||
})
|
||||
if err != nil {
|
||||
return out, metadata, &SigningError{
|
||||
Err: fmt.Errorf("failed to sign http request, %w", err),
|
||||
}
|
||||
}
|
||||
|
||||
out.Result = &v4.PresignedHTTPRequest{
|
||||
URL: u,
|
||||
Method: httpReq.Method,
|
||||
SignedHeader: h,
|
||||
}
|
||||
|
||||
return out, metadata, nil
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package v4a
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
internalcontext "github.com/aws/aws-sdk-go-v2/internal/context"
|
||||
|
||||
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
||||
"github.com/aws/smithy-go"
|
||||
"github.com/aws/smithy-go/auth"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// CredentialsAdapter adapts v4a.Credentials to smithy auth.Identity.
|
||||
type CredentialsAdapter struct {
|
||||
Credentials Credentials
|
||||
}
|
||||
|
||||
var _ auth.Identity = (*CredentialsAdapter)(nil)
|
||||
|
||||
// Expiration returns the time of expiration for the credentials.
|
||||
func (v *CredentialsAdapter) Expiration() time.Time {
|
||||
return v.Credentials.Expires
|
||||
}
|
||||
|
||||
// CredentialsProviderAdapter adapts v4a.CredentialsProvider to
|
||||
// auth.IdentityResolver.
|
||||
type CredentialsProviderAdapter struct {
|
||||
Provider CredentialsProvider
|
||||
}
|
||||
|
||||
var _ (auth.IdentityResolver) = (*CredentialsProviderAdapter)(nil)
|
||||
|
||||
// GetIdentity retrieves v4a credentials using the underlying provider.
|
||||
func (v *CredentialsProviderAdapter) GetIdentity(ctx context.Context, _ smithy.Properties) (
|
||||
auth.Identity, error,
|
||||
) {
|
||||
creds, err := v.Provider.RetrievePrivateKey(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get credentials: %w", err)
|
||||
}
|
||||
|
||||
return &CredentialsAdapter{Credentials: creds}, nil
|
||||
}
|
||||
|
||||
// SignerAdapter adapts v4a.HTTPSigner to smithy http.Signer.
|
||||
type SignerAdapter struct {
|
||||
Signer HTTPSigner
|
||||
Logger logging.Logger
|
||||
LogSigning bool
|
||||
}
|
||||
|
||||
var _ (smithyhttp.Signer) = (*SignerAdapter)(nil)
|
||||
|
||||
// SignRequest signs the request with the provided identity.
|
||||
func (v *SignerAdapter) SignRequest(ctx context.Context, r *smithyhttp.Request, identity auth.Identity, props smithy.Properties) error {
|
||||
ca, ok := identity.(*CredentialsAdapter)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected identity type: %T", identity)
|
||||
}
|
||||
|
||||
name, ok := smithyhttp.GetSigV4SigningName(&props)
|
||||
if !ok {
|
||||
return fmt.Errorf("sigv4a signing name is required")
|
||||
}
|
||||
|
||||
regions, ok := smithyhttp.GetSigV4ASigningRegions(&props)
|
||||
if !ok {
|
||||
return fmt.Errorf("sigv4a signing region is required")
|
||||
}
|
||||
|
||||
hash := v4.GetPayloadHash(ctx)
|
||||
signingTime := sdk.NowTime()
|
||||
if skew := internalcontext.GetAttemptSkewContext(ctx); skew != 0 {
|
||||
signingTime.Add(skew)
|
||||
}
|
||||
err := v.Signer.SignHTTP(ctx, ca.Credentials, r.Request, hash, name, regions, signingTime, func(o *SignerOptions) {
|
||||
o.DisableURIPathEscaping, _ = smithyhttp.GetDisableDoubleEncoding(&props)
|
||||
|
||||
o.Logger = v.Logger
|
||||
o.LogSigning = v.LogSigning
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("sign http: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+520
@@ -0,0 +1,520 @@
|
||||
package v4a
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
signerCrypto "github.com/aws/aws-sdk-go-v2/internal/v4a/internal/crypto"
|
||||
v4Internal "github.com/aws/aws-sdk-go-v2/internal/v4a/internal/v4"
|
||||
"github.com/aws/smithy-go/encoding/httpbinding"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
// AmzRegionSetKey represents the region set header used for sigv4a
|
||||
AmzRegionSetKey = "X-Amz-Region-Set"
|
||||
amzAlgorithmKey = v4Internal.AmzAlgorithmKey
|
||||
amzSecurityTokenKey = v4Internal.AmzSecurityTokenKey
|
||||
amzDateKey = v4Internal.AmzDateKey
|
||||
amzCredentialKey = v4Internal.AmzCredentialKey
|
||||
amzSignedHeadersKey = v4Internal.AmzSignedHeadersKey
|
||||
authorizationHeader = "Authorization"
|
||||
|
||||
signingAlgorithm = "AWS4-ECDSA-P256-SHA256"
|
||||
|
||||
timeFormat = "20060102T150405Z"
|
||||
shortTimeFormat = "20060102"
|
||||
|
||||
// EmptyStringSHA256 is a hex encoded SHA-256 hash of an empty string
|
||||
EmptyStringSHA256 = v4Internal.EmptyStringSHA256
|
||||
|
||||
// Version of signing v4a
|
||||
Version = "SigV4A"
|
||||
)
|
||||
|
||||
var (
|
||||
p256 elliptic.Curve
|
||||
nMinusTwoP256 *big.Int
|
||||
|
||||
one = new(big.Int).SetInt64(1)
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Ensure the elliptic curve parameters are initialized on package import rather then on first usage
|
||||
p256 = elliptic.P256()
|
||||
|
||||
nMinusTwoP256 = new(big.Int).SetBytes(p256.Params().N.Bytes())
|
||||
nMinusTwoP256 = nMinusTwoP256.Sub(nMinusTwoP256, new(big.Int).SetInt64(2))
|
||||
}
|
||||
|
||||
// SignerOptions is the SigV4a signing options for constructing a Signer.
|
||||
type SignerOptions struct {
|
||||
Logger logging.Logger
|
||||
LogSigning bool
|
||||
|
||||
// Disables the Signer's moving HTTP header key/value pairs from the HTTP
|
||||
// request header to the request's query string. This is most commonly used
|
||||
// with pre-signed requests preventing headers from being added to the
|
||||
// request's query string.
|
||||
DisableHeaderHoisting bool
|
||||
|
||||
// Disables the automatic escaping of the URI path of the request for the
|
||||
// siganture's canonical string's path. For services that do not need additional
|
||||
// escaping then use this to disable the signer escaping the path.
|
||||
//
|
||||
// S3 is an example of a service that does not need additional escaping.
|
||||
//
|
||||
// http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
||||
DisableURIPathEscaping bool
|
||||
}
|
||||
|
||||
// Signer is a SigV4a HTTP signing implementation
|
||||
type Signer struct {
|
||||
options SignerOptions
|
||||
}
|
||||
|
||||
// NewSigner constructs a SigV4a Signer.
|
||||
func NewSigner(optFns ...func(*SignerOptions)) *Signer {
|
||||
options := SignerOptions{}
|
||||
|
||||
for _, fn := range optFns {
|
||||
fn(&options)
|
||||
}
|
||||
|
||||
return &Signer{options: options}
|
||||
}
|
||||
|
||||
// deriveKeyFromAccessKeyPair derives a NIST P-256 PrivateKey from the given
|
||||
// IAM AccessKey and SecretKey pair.
|
||||
//
|
||||
// Based on FIPS.186-4 Appendix B.4.2
|
||||
func deriveKeyFromAccessKeyPair(accessKey, secretKey string) (*ecdsa.PrivateKey, error) {
|
||||
params := p256.Params()
|
||||
bitLen := params.BitSize // Testing random candidates does not require an additional 64 bits
|
||||
counter := 0x01
|
||||
|
||||
buffer := make([]byte, 1+len(accessKey)) // 1 byte counter + len(accessKey)
|
||||
kdfContext := bytes.NewBuffer(buffer)
|
||||
|
||||
inputKey := append([]byte("AWS4A"), []byte(secretKey)...)
|
||||
|
||||
d := new(big.Int)
|
||||
for {
|
||||
kdfContext.Reset()
|
||||
kdfContext.WriteString(accessKey)
|
||||
kdfContext.WriteByte(byte(counter))
|
||||
|
||||
key, err := signerCrypto.HMACKeyDerivation(sha256.New, bitLen, inputKey, []byte(signingAlgorithm), kdfContext.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check key first before calling SetBytes if key key is in fact a valid candidate.
|
||||
// This ensures the byte slice is the correct length (32-bytes) to compare in constant-time
|
||||
cmp, err := signerCrypto.ConstantTimeByteCompare(key, nMinusTwoP256.Bytes())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cmp == -1 {
|
||||
d.SetBytes(key)
|
||||
break
|
||||
}
|
||||
|
||||
counter++
|
||||
if counter > 0xFF {
|
||||
return nil, fmt.Errorf("exhausted single byte external counter")
|
||||
}
|
||||
}
|
||||
d = d.Add(d, one)
|
||||
|
||||
priv := new(ecdsa.PrivateKey)
|
||||
priv.PublicKey.Curve = p256
|
||||
priv.D = d
|
||||
priv.PublicKey.X, priv.PublicKey.Y = p256.ScalarBaseMult(d.Bytes())
|
||||
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
type httpSigner struct {
|
||||
Request *http.Request
|
||||
ServiceName string
|
||||
RegionSet []string
|
||||
Time time.Time
|
||||
Credentials Credentials
|
||||
IsPreSign bool
|
||||
|
||||
Logger logging.Logger
|
||||
Debug bool
|
||||
|
||||
// PayloadHash is the hex encoded SHA-256 hash of the request payload
|
||||
// If len(PayloadHash) == 0 the signer will attempt to send the request
|
||||
// as an unsigned payload. Note: Unsigned payloads only work for a subset of services.
|
||||
PayloadHash string
|
||||
|
||||
DisableHeaderHoisting bool
|
||||
DisableURIPathEscaping bool
|
||||
}
|
||||
|
||||
// SignHTTP takes the provided http.Request, payload hash, service, regionSet, and time and signs using SigV4a.
|
||||
// The passed in request will be modified in place.
|
||||
func (s *Signer) SignHTTP(ctx context.Context, credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, optFns ...func(*SignerOptions)) error {
|
||||
options := s.options
|
||||
for _, fn := range optFns {
|
||||
fn(&options)
|
||||
}
|
||||
|
||||
signer := &httpSigner{
|
||||
Request: r,
|
||||
PayloadHash: payloadHash,
|
||||
ServiceName: service,
|
||||
RegionSet: regionSet,
|
||||
Credentials: credentials,
|
||||
Time: signingTime.UTC(),
|
||||
DisableHeaderHoisting: options.DisableHeaderHoisting,
|
||||
DisableURIPathEscaping: options.DisableURIPathEscaping,
|
||||
}
|
||||
|
||||
signedRequest, err := signer.Build()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logHTTPSigningInfo(ctx, options, signedRequest)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PresignHTTP takes the provided http.Request, payload hash, service, regionSet, and time and presigns using SigV4a
|
||||
// Returns the presigned URL along with the headers that were signed with the request.
|
||||
//
|
||||
// PresignHTTP will not set the expires time of the presigned request
|
||||
// automatically. To specify the expire duration for a request add the
|
||||
// "X-Amz-Expires" query parameter on the request with the value as the
|
||||
// duration in seconds the presigned URL should be considered valid for. This
|
||||
// parameter is not used by all AWS services, and is most notable used by
|
||||
// Amazon S3 APIs.
|
||||
func (s *Signer) PresignHTTP(ctx context.Context, credentials Credentials, r *http.Request, payloadHash string, service string, regionSet []string, signingTime time.Time, optFns ...func(*SignerOptions)) (signedURI string, signedHeaders http.Header, err error) {
|
||||
options := s.options
|
||||
for _, fn := range optFns {
|
||||
fn(&options)
|
||||
}
|
||||
|
||||
signer := &httpSigner{
|
||||
Request: r,
|
||||
PayloadHash: payloadHash,
|
||||
ServiceName: service,
|
||||
RegionSet: regionSet,
|
||||
Credentials: credentials,
|
||||
Time: signingTime.UTC(),
|
||||
IsPreSign: true,
|
||||
DisableHeaderHoisting: options.DisableHeaderHoisting,
|
||||
DisableURIPathEscaping: options.DisableURIPathEscaping,
|
||||
}
|
||||
|
||||
signedRequest, err := signer.Build()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
logHTTPSigningInfo(ctx, options, signedRequest)
|
||||
|
||||
signedHeaders = make(http.Header)
|
||||
|
||||
// For the signed headers we canonicalize the header keys in the returned map.
|
||||
// This avoids situations where can standard library double headers like host header. For example the standard
|
||||
// library will set the Host header, even if it is present in lower-case form.
|
||||
for k, v := range signedRequest.SignedHeaders {
|
||||
key := textproto.CanonicalMIMEHeaderKey(k)
|
||||
signedHeaders[key] = append(signedHeaders[key], v...)
|
||||
}
|
||||
|
||||
return signedRequest.Request.URL.String(), signedHeaders, nil
|
||||
}
|
||||
|
||||
func (s *httpSigner) setRequiredSigningFields(headers http.Header, query url.Values) {
|
||||
amzDate := s.Time.Format(timeFormat)
|
||||
|
||||
if s.IsPreSign {
|
||||
query.Set(AmzRegionSetKey, strings.Join(s.RegionSet, ","))
|
||||
query.Set(amzDateKey, amzDate)
|
||||
query.Set(amzAlgorithmKey, signingAlgorithm)
|
||||
if len(s.Credentials.SessionToken) > 0 {
|
||||
query.Set(amzSecurityTokenKey, s.Credentials.SessionToken)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
headers.Set(AmzRegionSetKey, strings.Join(s.RegionSet, ","))
|
||||
headers.Set(amzDateKey, amzDate)
|
||||
if len(s.Credentials.SessionToken) > 0 {
|
||||
headers.Set(amzSecurityTokenKey, s.Credentials.SessionToken)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *httpSigner) Build() (signedRequest, error) {
|
||||
req := s.Request
|
||||
|
||||
query := req.URL.Query()
|
||||
headers := req.Header
|
||||
|
||||
s.setRequiredSigningFields(headers, query)
|
||||
|
||||
// Sort Each Query Key's Values
|
||||
for key := range query {
|
||||
sort.Strings(query[key])
|
||||
}
|
||||
|
||||
v4Internal.SanitizeHostForHeader(req)
|
||||
|
||||
credentialScope := s.buildCredentialScope()
|
||||
credentialStr := s.Credentials.Context + "/" + credentialScope
|
||||
if s.IsPreSign {
|
||||
query.Set(amzCredentialKey, credentialStr)
|
||||
}
|
||||
|
||||
unsignedHeaders := headers
|
||||
if s.IsPreSign && !s.DisableHeaderHoisting {
|
||||
urlValues := url.Values{}
|
||||
urlValues, unsignedHeaders = buildQuery(v4Internal.AllowedQueryHoisting, unsignedHeaders)
|
||||
for k := range urlValues {
|
||||
query[k] = urlValues[k]
|
||||
}
|
||||
}
|
||||
|
||||
host := req.URL.Host
|
||||
if len(req.Host) > 0 {
|
||||
host = req.Host
|
||||
}
|
||||
|
||||
signedHeaders, signedHeadersStr, canonicalHeaderStr := s.buildCanonicalHeaders(host, v4Internal.IgnoredHeaders, unsignedHeaders, s.Request.ContentLength)
|
||||
|
||||
if s.IsPreSign {
|
||||
query.Set(amzSignedHeadersKey, signedHeadersStr)
|
||||
}
|
||||
|
||||
rawQuery := strings.Replace(query.Encode(), "+", "%20", -1)
|
||||
|
||||
canonicalURI := v4Internal.GetURIPath(req.URL)
|
||||
if !s.DisableURIPathEscaping {
|
||||
canonicalURI = httpbinding.EscapePath(canonicalURI, false)
|
||||
}
|
||||
|
||||
canonicalString := s.buildCanonicalString(
|
||||
req.Method,
|
||||
canonicalURI,
|
||||
rawQuery,
|
||||
signedHeadersStr,
|
||||
canonicalHeaderStr,
|
||||
)
|
||||
|
||||
strToSign := s.buildStringToSign(credentialScope, canonicalString)
|
||||
signingSignature, err := s.buildSignature(strToSign)
|
||||
if err != nil {
|
||||
return signedRequest{}, err
|
||||
}
|
||||
|
||||
if s.IsPreSign {
|
||||
rawQuery += "&X-Amz-Signature=" + signingSignature
|
||||
} else {
|
||||
headers[authorizationHeader] = append(headers[authorizationHeader][:0], buildAuthorizationHeader(credentialStr, signedHeadersStr, signingSignature))
|
||||
}
|
||||
|
||||
req.URL.RawQuery = rawQuery
|
||||
|
||||
return signedRequest{
|
||||
Request: req,
|
||||
SignedHeaders: signedHeaders,
|
||||
CanonicalString: canonicalString,
|
||||
StringToSign: strToSign,
|
||||
PreSigned: s.IsPreSign,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildAuthorizationHeader(credentialStr, signedHeadersStr, signingSignature string) string {
|
||||
const credential = "Credential="
|
||||
const signedHeaders = "SignedHeaders="
|
||||
const signature = "Signature="
|
||||
const commaSpace = ", "
|
||||
|
||||
var parts strings.Builder
|
||||
parts.Grow(len(signingAlgorithm) + 1 +
|
||||
len(credential) + len(credentialStr) + len(commaSpace) +
|
||||
len(signedHeaders) + len(signedHeadersStr) + len(commaSpace) +
|
||||
len(signature) + len(signingSignature),
|
||||
)
|
||||
parts.WriteString(signingAlgorithm)
|
||||
parts.WriteRune(' ')
|
||||
parts.WriteString(credential)
|
||||
parts.WriteString(credentialStr)
|
||||
parts.WriteString(commaSpace)
|
||||
parts.WriteString(signedHeaders)
|
||||
parts.WriteString(signedHeadersStr)
|
||||
parts.WriteString(commaSpace)
|
||||
parts.WriteString(signature)
|
||||
parts.WriteString(signingSignature)
|
||||
return parts.String()
|
||||
}
|
||||
|
||||
func (s *httpSigner) buildCredentialScope() string {
|
||||
return strings.Join([]string{
|
||||
s.Time.Format(shortTimeFormat),
|
||||
s.ServiceName,
|
||||
"aws4_request",
|
||||
}, "/")
|
||||
|
||||
}
|
||||
|
||||
func buildQuery(r v4Internal.Rule, header http.Header) (url.Values, http.Header) {
|
||||
query := url.Values{}
|
||||
unsignedHeaders := http.Header{}
|
||||
for k, h := range header {
|
||||
if r.IsValid(k) {
|
||||
query[k] = h
|
||||
} else {
|
||||
unsignedHeaders[k] = h
|
||||
}
|
||||
}
|
||||
|
||||
return query, unsignedHeaders
|
||||
}
|
||||
|
||||
func (s *httpSigner) buildCanonicalHeaders(host string, rule v4Internal.Rule, header http.Header, length int64) (signed http.Header, signedHeaders, canonicalHeadersStr string) {
|
||||
signed = make(http.Header)
|
||||
|
||||
var headers []string
|
||||
const hostHeader = "host"
|
||||
headers = append(headers, hostHeader)
|
||||
signed[hostHeader] = append(signed[hostHeader], host)
|
||||
|
||||
if length > 0 {
|
||||
const contentLengthHeader = "content-length"
|
||||
headers = append(headers, contentLengthHeader)
|
||||
signed[contentLengthHeader] = append(signed[contentLengthHeader], strconv.FormatInt(length, 10))
|
||||
}
|
||||
|
||||
for k, v := range header {
|
||||
if !rule.IsValid(k) {
|
||||
continue // ignored header
|
||||
}
|
||||
|
||||
lowerCaseKey := strings.ToLower(k)
|
||||
if _, ok := signed[lowerCaseKey]; ok {
|
||||
// include additional values
|
||||
signed[lowerCaseKey] = append(signed[lowerCaseKey], v...)
|
||||
continue
|
||||
}
|
||||
|
||||
headers = append(headers, lowerCaseKey)
|
||||
signed[lowerCaseKey] = v
|
||||
}
|
||||
sort.Strings(headers)
|
||||
|
||||
signedHeaders = strings.Join(headers, ";")
|
||||
|
||||
var canonicalHeaders strings.Builder
|
||||
n := len(headers)
|
||||
const colon = ':'
|
||||
for i := 0; i < n; i++ {
|
||||
if headers[i] == hostHeader {
|
||||
canonicalHeaders.WriteString(hostHeader)
|
||||
canonicalHeaders.WriteRune(colon)
|
||||
canonicalHeaders.WriteString(v4Internal.StripExcessSpaces(host))
|
||||
} else {
|
||||
canonicalHeaders.WriteString(headers[i])
|
||||
canonicalHeaders.WriteRune(colon)
|
||||
// Trim out leading, trailing, and dedup inner spaces from signed header values.
|
||||
values := signed[headers[i]]
|
||||
for j, v := range values {
|
||||
cleanedValue := strings.TrimSpace(v4Internal.StripExcessSpaces(v))
|
||||
canonicalHeaders.WriteString(cleanedValue)
|
||||
if j < len(values)-1 {
|
||||
canonicalHeaders.WriteRune(',')
|
||||
}
|
||||
}
|
||||
}
|
||||
canonicalHeaders.WriteRune('\n')
|
||||
}
|
||||
canonicalHeadersStr = canonicalHeaders.String()
|
||||
|
||||
return signed, signedHeaders, canonicalHeadersStr
|
||||
}
|
||||
|
||||
func (s *httpSigner) buildCanonicalString(method, uri, query, signedHeaders, canonicalHeaders string) string {
|
||||
return strings.Join([]string{
|
||||
method,
|
||||
uri,
|
||||
query,
|
||||
canonicalHeaders,
|
||||
signedHeaders,
|
||||
s.PayloadHash,
|
||||
}, "\n")
|
||||
}
|
||||
|
||||
func (s *httpSigner) buildStringToSign(credentialScope, canonicalRequestString string) string {
|
||||
return strings.Join([]string{
|
||||
signingAlgorithm,
|
||||
s.Time.Format(timeFormat),
|
||||
credentialScope,
|
||||
hex.EncodeToString(makeHash(sha256.New(), []byte(canonicalRequestString))),
|
||||
}, "\n")
|
||||
}
|
||||
|
||||
func makeHash(hash hash.Hash, b []byte) []byte {
|
||||
hash.Reset()
|
||||
hash.Write(b)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
func (s *httpSigner) buildSignature(strToSign string) (string, error) {
|
||||
sig, err := s.Credentials.PrivateKey.Sign(rand.Reader, makeHash(sha256.New(), []byte(strToSign)), crypto.SHA256)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(sig), nil
|
||||
}
|
||||
|
||||
const logSignInfoMsg = `Request Signature:
|
||||
---[ CANONICAL STRING ]-----------------------------
|
||||
%s
|
||||
---[ STRING TO SIGN ]--------------------------------
|
||||
%s%s
|
||||
-----------------------------------------------------`
|
||||
const logSignedURLMsg = `
|
||||
---[ SIGNED URL ]------------------------------------
|
||||
%s`
|
||||
|
||||
func logHTTPSigningInfo(ctx context.Context, options SignerOptions, r signedRequest) {
|
||||
if !options.LogSigning {
|
||||
return
|
||||
}
|
||||
signedURLMsg := ""
|
||||
if r.PreSigned {
|
||||
signedURLMsg = fmt.Sprintf(logSignedURLMsg, r.Request.URL.String())
|
||||
}
|
||||
logger := logging.WithContext(ctx, options.Logger)
|
||||
logger.Logf(logging.Debug, logSignInfoMsg, r.CanonicalString, r.StringToSign, signedURLMsg)
|
||||
}
|
||||
|
||||
type signedRequest struct {
|
||||
Request *http.Request
|
||||
SignedHeaders http.Header
|
||||
CanonicalString string
|
||||
StringToSign string
|
||||
PreSigned bool
|
||||
}
|
||||
Generated
Vendored
+4
@@ -1,3 +1,7 @@
|
||||
# v1.12.2 (2025-01-24)
|
||||
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v1.12.1 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -3,4 +3,4 @@
|
||||
package acceptencoding
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.12.1"
|
||||
const goModuleVersion = "1.12.2"
|
||||
|
||||
+361
@@ -0,0 +1,361 @@
|
||||
# v1.5.5 (2025-01-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.5.4 (2025-01-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.5.3 (2025-01-24)
|
||||
|
||||
* **Bug Fix**: Enable request checksum validation mode by default
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v1.5.2 (2025-01-17)
|
||||
|
||||
* **Bug Fix**: Fix bug where credentials weren't refreshed during retry loop.
|
||||
|
||||
# v1.5.1 (2025-01-16)
|
||||
|
||||
* **Bug Fix**: Fix nil dereference panic for operations that require checksums, but do not have an input setting for which algorithm to use.
|
||||
|
||||
# v1.5.0 (2025-01-15)
|
||||
|
||||
* **Feature**: S3 client behavior is updated to always calculate a checksum by default for operations that support it (such as PutObject or UploadPart), or require it (such as DeleteObjects). The checksum algorithm used by default now becomes CRC32. Checksum behavior can be configured using `when_supported` and `when_required` options - in code using RequestChecksumCalculation, in shared config using request_checksum_calculation, or as env variable using AWS_REQUEST_CHECKSUM_CALCULATION. The S3 client attempts to validate response checksums for all S3 API operations that support checksums. However, if the SDK has not implemented the specified checksum algorithm then this validation is skipped. Checksum validation behavior can be configured using `when_supported` and `when_required` options - in code using ResponseChecksumValidation, in shared config using response_checksum_validation, or as env variable using AWS_RESPONSE_CHECKSUM_VALIDATION.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.8 (2025-01-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.7 (2024-12-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.6 (2024-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.5 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.4 (2024-11-06)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.3 (2024-10-28)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.2 (2024-10-08)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.1 (2024-10-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.0 (2024-10-04)
|
||||
|
||||
* **Feature**: Add support for HTTP client metrics.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.20 (2024-09-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.19 (2024-09-03)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.18 (2024-08-15)
|
||||
|
||||
* **Dependency Update**: Bump minimum Go version to 1.21.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.17 (2024-07-10.2)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.16 (2024-07-10)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.15 (2024-06-28)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.14 (2024-06-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.13 (2024-06-18)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.12 (2024-06-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.11 (2024-06-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.10 (2024-06-03)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.9 (2024-05-16)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.8 (2024-05-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.7 (2024-03-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.6 (2024-03-18)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.5 (2024-03-07)
|
||||
|
||||
* **Bug Fix**: Remove dependency on go-cmp.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.4 (2024-03-05)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.3 (2024-03-04)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.2 (2024-02-23)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.1 (2024-02-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.0 (2024-02-13)
|
||||
|
||||
* **Feature**: Bump minimum Go version to 1.20 per our language support policy.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.10 (2024-01-04)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.9 (2023-12-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.8 (2023-12-01)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.7 (2023-11-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.6 (2023-11-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.5 (2023-11-28.2)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.4 (2023-11-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.3 (2023-11-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.2 (2023-11-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.1 (2023-11-01)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.0 (2023-10-31)
|
||||
|
||||
* **Feature**: **BREAKING CHANGE**: Bump minimum go version to 1.19 per the revised [go version support policy](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-aligns-with-go-release-policy-on-supported-runtimes/).
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.38 (2023-10-12)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.37 (2023-10-06)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.36 (2023-08-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.35 (2023-08-18)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.34 (2023-08-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.33 (2023-08-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.32 (2023-07-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.31 (2023-07-28)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.30 (2023-07-13)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.29 (2023-06-13)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.28 (2023-04-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.27 (2023-04-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.26 (2023-03-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.25 (2023-03-10)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.24 (2023-02-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.23 (2023-02-03)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.22 (2022-12-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.21 (2022-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.20 (2022-10-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.19 (2022-10-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.18 (2022-09-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.17 (2022-09-14)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.16 (2022-09-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.15 (2022-08-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.14 (2022-08-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.13 (2022-08-11)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.12 (2022-08-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.11 (2022-08-08)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.10 (2022-08-01)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.9 (2022-07-05)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.8 (2022-06-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.7 (2022-06-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.6 (2022-05-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.5 (2022-04-27)
|
||||
|
||||
* **Bug Fix**: Fixes a bug that could cause the SigV4 payload hash to be incorrectly encoded, leading to signing errors.
|
||||
|
||||
# v1.1.4 (2022-04-25)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.3 (2022-03-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.2 (2022-03-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.1 (2022-03-23)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.1.0 (2022-03-08)
|
||||
|
||||
* **Feature**: Updates the SDK's checksum validation logic to require opt-in to output response payload validation. The SDK was always preforming output response payload checksum validation, not respecting the output validation model option. Fixes [#1606](https://github.com/aws/aws-sdk-go-v2/issues/1606)
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.0.0 (2022-02-24)
|
||||
|
||||
* **Release**: New module for computing checksums
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
package checksum
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Algorithm represents the checksum algorithms supported
|
||||
type Algorithm string
|
||||
|
||||
// Enumeration values for supported checksum Algorithms.
|
||||
const (
|
||||
// AlgorithmCRC32C represents CRC32C hash algorithm
|
||||
AlgorithmCRC32C Algorithm = "CRC32C"
|
||||
|
||||
// AlgorithmCRC32 represents CRC32 hash algorithm
|
||||
AlgorithmCRC32 Algorithm = "CRC32"
|
||||
|
||||
// AlgorithmSHA1 represents SHA1 hash algorithm
|
||||
AlgorithmSHA1 Algorithm = "SHA1"
|
||||
|
||||
// AlgorithmSHA256 represents SHA256 hash algorithm
|
||||
AlgorithmSHA256 Algorithm = "SHA256"
|
||||
|
||||
// AlgorithmCRC64NVME represents CRC64NVME hash algorithm
|
||||
AlgorithmCRC64NVME Algorithm = "CRC64NVME"
|
||||
)
|
||||
|
||||
var supportedAlgorithms = []Algorithm{
|
||||
AlgorithmCRC32C,
|
||||
AlgorithmCRC32,
|
||||
AlgorithmSHA1,
|
||||
AlgorithmSHA256,
|
||||
}
|
||||
|
||||
func (a Algorithm) String() string { return string(a) }
|
||||
|
||||
// ParseAlgorithm attempts to parse the provided value into a checksum
|
||||
// algorithm, matching without case. Returns the algorithm matched, or an error
|
||||
// if the algorithm wasn't matched.
|
||||
func ParseAlgorithm(v string) (Algorithm, error) {
|
||||
for _, a := range supportedAlgorithms {
|
||||
if strings.EqualFold(string(a), v) {
|
||||
return a, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("unknown checksum algorithm, %v", v)
|
||||
}
|
||||
|
||||
// FilterSupportedAlgorithms filters the set of algorithms, returning a slice
|
||||
// of algorithms that are supported.
|
||||
func FilterSupportedAlgorithms(vs []string) []Algorithm {
|
||||
found := map[Algorithm]struct{}{}
|
||||
|
||||
supported := make([]Algorithm, 0, len(supportedAlgorithms))
|
||||
for _, v := range vs {
|
||||
for _, a := range supportedAlgorithms {
|
||||
// Only consider algorithms that are supported
|
||||
if !strings.EqualFold(v, string(a)) {
|
||||
continue
|
||||
}
|
||||
// Ignore duplicate algorithms in list.
|
||||
if _, ok := found[a]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
supported = append(supported, a)
|
||||
found[a] = struct{}{}
|
||||
}
|
||||
}
|
||||
return supported
|
||||
}
|
||||
|
||||
// NewAlgorithmHash returns a hash.Hash for the checksum algorithm. Error is
|
||||
// returned if the algorithm is unknown.
|
||||
func NewAlgorithmHash(v Algorithm) (hash.Hash, error) {
|
||||
switch v {
|
||||
case AlgorithmSHA1:
|
||||
return sha1.New(), nil
|
||||
case AlgorithmSHA256:
|
||||
return sha256.New(), nil
|
||||
case AlgorithmCRC32:
|
||||
return crc32.NewIEEE(), nil
|
||||
case AlgorithmCRC32C:
|
||||
return crc32.New(crc32.MakeTable(crc32.Castagnoli)), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown checksum algorithm, %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
// AlgorithmChecksumLength returns the length of the algorithm's checksum in
|
||||
// bytes. If the algorithm is not known, an error is returned.
|
||||
func AlgorithmChecksumLength(v Algorithm) (int, error) {
|
||||
switch v {
|
||||
case AlgorithmSHA1:
|
||||
return sha1.Size, nil
|
||||
case AlgorithmSHA256:
|
||||
return sha256.Size, nil
|
||||
case AlgorithmCRC32:
|
||||
return crc32.Size, nil
|
||||
case AlgorithmCRC32C:
|
||||
return crc32.Size, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown checksum algorithm, %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
const awsChecksumHeaderPrefix = "x-amz-checksum-"
|
||||
|
||||
// AlgorithmHTTPHeader returns the HTTP header for the algorithm's hash.
|
||||
func AlgorithmHTTPHeader(v Algorithm) string {
|
||||
return awsChecksumHeaderPrefix + strings.ToLower(string(v))
|
||||
}
|
||||
|
||||
// base64EncodeHashSum computes base64 encoded checksum of a given running
|
||||
// hash. The running hash must already have content written to it. Returns the
|
||||
// byte slice of checksum and an error
|
||||
func base64EncodeHashSum(h hash.Hash) []byte {
|
||||
sum := h.Sum(nil)
|
||||
sum64 := make([]byte, base64.StdEncoding.EncodedLen(len(sum)))
|
||||
base64.StdEncoding.Encode(sum64, sum)
|
||||
return sum64
|
||||
}
|
||||
|
||||
// hexEncodeHashSum computes hex encoded checksum of a given running hash. The
|
||||
// running hash must already have content written to it. Returns the byte slice
|
||||
// of checksum and an error
|
||||
func hexEncodeHashSum(h hash.Hash) []byte {
|
||||
sum := h.Sum(nil)
|
||||
sumHex := make([]byte, hex.EncodedLen(len(sum)))
|
||||
hex.Encode(sumHex, sum)
|
||||
return sumHex
|
||||
}
|
||||
|
||||
// computeMD5Checksum computes base64 MD5 checksum of an io.Reader's contents.
|
||||
// Returns the byte slice of MD5 checksum and an error.
|
||||
func computeMD5Checksum(r io.Reader) ([]byte, error) {
|
||||
h := md5.New()
|
||||
|
||||
// Copy errors may be assumed to be from the body.
|
||||
if _, err := io.Copy(h, r); err != nil {
|
||||
return nil, fmt.Errorf("failed compute MD5 hash of reader, %w", err)
|
||||
}
|
||||
|
||||
// Encode the MD5 checksum in base64.
|
||||
return base64EncodeHashSum(h), nil
|
||||
}
|
||||
|
||||
// computeChecksumReader provides a reader wrapping an underlying io.Reader to
|
||||
// compute the checksum of the stream's bytes.
|
||||
type computeChecksumReader struct {
|
||||
stream io.Reader
|
||||
algorithm Algorithm
|
||||
hasher hash.Hash
|
||||
base64ChecksumLen int
|
||||
|
||||
mux sync.RWMutex
|
||||
lockedChecksum string
|
||||
lockedErr error
|
||||
}
|
||||
|
||||
// newComputeChecksumReader returns a computeChecksumReader for the stream and
|
||||
// algorithm specified. Returns error if unable to create the reader, or
|
||||
// algorithm is unknown.
|
||||
func newComputeChecksumReader(stream io.Reader, algorithm Algorithm) (*computeChecksumReader, error) {
|
||||
hasher, err := NewAlgorithmHash(algorithm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
checksumLength, err := AlgorithmChecksumLength(algorithm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &computeChecksumReader{
|
||||
stream: io.TeeReader(stream, hasher),
|
||||
algorithm: algorithm,
|
||||
hasher: hasher,
|
||||
base64ChecksumLen: base64.StdEncoding.EncodedLen(checksumLength),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read wraps the underlying reader. When the underlying reader returns EOF,
|
||||
// the checksum of the reader will be computed, and can be retrieved with
|
||||
// ChecksumBase64String.
|
||||
func (r *computeChecksumReader) Read(p []byte) (int, error) {
|
||||
n, err := r.stream.Read(p)
|
||||
if err == nil {
|
||||
return n, nil
|
||||
} else if err != io.EOF {
|
||||
r.mux.Lock()
|
||||
defer r.mux.Unlock()
|
||||
|
||||
r.lockedErr = err
|
||||
return n, err
|
||||
}
|
||||
|
||||
b := base64EncodeHashSum(r.hasher)
|
||||
|
||||
r.mux.Lock()
|
||||
defer r.mux.Unlock()
|
||||
|
||||
r.lockedChecksum = string(b)
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *computeChecksumReader) Algorithm() Algorithm {
|
||||
return r.algorithm
|
||||
}
|
||||
|
||||
// Base64ChecksumLength returns the base64 encoded length of the checksum for
|
||||
// algorithm.
|
||||
func (r *computeChecksumReader) Base64ChecksumLength() int {
|
||||
return r.base64ChecksumLen
|
||||
}
|
||||
|
||||
// Base64Checksum returns the base64 checksum for the algorithm, or error if
|
||||
// the underlying reader returned a non-EOF error.
|
||||
//
|
||||
// Safe to be called concurrently, but will return an error until after the
|
||||
// underlying reader is returns EOF.
|
||||
func (r *computeChecksumReader) Base64Checksum() (string, error) {
|
||||
r.mux.RLock()
|
||||
defer r.mux.RUnlock()
|
||||
|
||||
if r.lockedErr != nil {
|
||||
return "", r.lockedErr
|
||||
}
|
||||
|
||||
if r.lockedChecksum == "" {
|
||||
return "", fmt.Errorf(
|
||||
"checksum not available yet, called before reader returns EOF",
|
||||
)
|
||||
}
|
||||
|
||||
return r.lockedChecksum, nil
|
||||
}
|
||||
|
||||
// validateChecksumReader implements io.ReadCloser interface. The wrapper
|
||||
// performs checksum validation when the underlying reader has been fully read.
|
||||
type validateChecksumReader struct {
|
||||
originalBody io.ReadCloser
|
||||
body io.Reader
|
||||
hasher hash.Hash
|
||||
algorithm Algorithm
|
||||
expectChecksum string
|
||||
}
|
||||
|
||||
// newValidateChecksumReader returns a configured io.ReadCloser that performs
|
||||
// checksum validation when the underlying reader has been fully read.
|
||||
func newValidateChecksumReader(
|
||||
body io.ReadCloser,
|
||||
algorithm Algorithm,
|
||||
expectChecksum string,
|
||||
) (*validateChecksumReader, error) {
|
||||
hasher, err := NewAlgorithmHash(algorithm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &validateChecksumReader{
|
||||
originalBody: body,
|
||||
body: io.TeeReader(body, hasher),
|
||||
hasher: hasher,
|
||||
algorithm: algorithm,
|
||||
expectChecksum: expectChecksum,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Read attempts to read from the underlying stream while also updating the
|
||||
// running hash. If the underlying stream returns with an EOF error, the
|
||||
// checksum of the stream will be collected, and compared against the expected
|
||||
// checksum. If the checksums do not match, an error will be returned.
|
||||
//
|
||||
// If a non-EOF error occurs when reading the underlying stream, that error
|
||||
// will be returned and the checksum for the stream will be discarded.
|
||||
func (c *validateChecksumReader) Read(p []byte) (n int, err error) {
|
||||
n, err = c.body.Read(p)
|
||||
if err == io.EOF {
|
||||
if checksumErr := c.validateChecksum(); checksumErr != nil {
|
||||
return n, checksumErr
|
||||
}
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Close closes the underlying reader, returning any error that occurred in the
|
||||
// underlying reader.
|
||||
func (c *validateChecksumReader) Close() (err error) {
|
||||
return c.originalBody.Close()
|
||||
}
|
||||
|
||||
func (c *validateChecksumReader) validateChecksum() error {
|
||||
// Compute base64 encoded checksum hash of the payload's read bytes.
|
||||
v := base64EncodeHashSum(c.hasher)
|
||||
if e, a := c.expectChecksum, string(v); !strings.EqualFold(e, a) {
|
||||
return validationError{
|
||||
Algorithm: c.algorithm, Expect: e, Actual: a,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type validationError struct {
|
||||
Algorithm Algorithm
|
||||
Expect string
|
||||
Actual string
|
||||
}
|
||||
|
||||
func (v validationError) Error() string {
|
||||
return fmt.Sprintf("checksum did not match: algorithm %v, expect %v, actual %v",
|
||||
v.Algorithm, v.Expect, v.Actual)
|
||||
}
|
||||
Generated
Vendored
+389
@@ -0,0 +1,389 @@
|
||||
package checksum
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
crlf = "\r\n"
|
||||
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html
|
||||
defaultChunkLength = 1024 * 64
|
||||
|
||||
awsTrailerHeaderName = "x-amz-trailer"
|
||||
decodedContentLengthHeaderName = "x-amz-decoded-content-length"
|
||||
|
||||
contentEncodingHeaderName = "content-encoding"
|
||||
awsChunkedContentEncodingHeaderValue = "aws-chunked"
|
||||
|
||||
trailerKeyValueSeparator = ":"
|
||||
)
|
||||
|
||||
var (
|
||||
crlfBytes = []byte(crlf)
|
||||
finalChunkBytes = []byte("0" + crlf)
|
||||
)
|
||||
|
||||
type awsChunkedEncodingOptions struct {
|
||||
// The total size of the stream. For unsigned encoding this implies that
|
||||
// there will only be a single chunk containing the underlying payload,
|
||||
// unless ChunkLength is also specified.
|
||||
StreamLength int64
|
||||
|
||||
// Set of trailer key:value pairs that will be appended to the end of the
|
||||
// payload after the end chunk has been written.
|
||||
Trailers map[string]awsChunkedTrailerValue
|
||||
|
||||
// The maximum size of each chunk to be sent. Default value of -1, signals
|
||||
// that optimal chunk length will be used automatically. ChunkSize must be
|
||||
// at least 8KB.
|
||||
//
|
||||
// If ChunkLength and StreamLength are both specified, the stream will be
|
||||
// broken up into ChunkLength chunks. The encoded length of the aws-chunked
|
||||
// encoding can still be determined as long as all trailers, if any, have a
|
||||
// fixed length.
|
||||
ChunkLength int
|
||||
}
|
||||
|
||||
type awsChunkedTrailerValue struct {
|
||||
// Function to retrieve the value of the trailer. Will only be called after
|
||||
// the underlying stream returns EOF error.
|
||||
Get func() (string, error)
|
||||
|
||||
// If the length of the value can be pre-determined, and is constant
|
||||
// specify the length. A value of -1 means the length is unknown, or
|
||||
// cannot be pre-determined.
|
||||
Length int
|
||||
}
|
||||
|
||||
// awsChunkedEncoding provides a reader that wraps the payload such that
|
||||
// payload is read as a single aws-chunk payload. This reader can only be used
|
||||
// if the content length of payload is known. Content-Length is used as size of
|
||||
// the single payload chunk. The final chunk and trailing checksum is appended
|
||||
// at the end.
|
||||
//
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html#sigv4-chunked-body-definition
|
||||
//
|
||||
// Here is the aws-chunked payload stream as read from the awsChunkedEncoding
|
||||
// if original request stream is "Hello world", and checksum hash used is SHA256
|
||||
//
|
||||
// <b>\r\n
|
||||
// Hello world\r\n
|
||||
// 0\r\n
|
||||
// x-amz-checksum-sha256:ZOyIygCyaOW6GjVnihtTFtIS9PNmskdyMlNKiuyjfzw=\r\n
|
||||
// \r\n
|
||||
type awsChunkedEncoding struct {
|
||||
options awsChunkedEncodingOptions
|
||||
|
||||
encodedStream io.Reader
|
||||
trailerEncodedLength int
|
||||
}
|
||||
|
||||
// newUnsignedAWSChunkedEncoding returns a new awsChunkedEncoding configured
|
||||
// for unsigned aws-chunked content encoding. Any additional trailers that need
|
||||
// to be appended after the end chunk must be included as via Trailer
|
||||
// callbacks.
|
||||
func newUnsignedAWSChunkedEncoding(
|
||||
stream io.Reader,
|
||||
optFns ...func(*awsChunkedEncodingOptions),
|
||||
) *awsChunkedEncoding {
|
||||
options := awsChunkedEncodingOptions{
|
||||
Trailers: map[string]awsChunkedTrailerValue{},
|
||||
StreamLength: -1,
|
||||
ChunkLength: -1,
|
||||
}
|
||||
for _, fn := range optFns {
|
||||
fn(&options)
|
||||
}
|
||||
|
||||
var chunkReader io.Reader
|
||||
if options.ChunkLength != -1 || options.StreamLength == -1 {
|
||||
if options.ChunkLength == -1 {
|
||||
options.ChunkLength = defaultChunkLength
|
||||
}
|
||||
chunkReader = newBufferedAWSChunkReader(stream, options.ChunkLength)
|
||||
} else {
|
||||
chunkReader = newUnsignedChunkReader(stream, options.StreamLength)
|
||||
}
|
||||
|
||||
trailerReader := newAWSChunkedTrailerReader(options.Trailers)
|
||||
|
||||
return &awsChunkedEncoding{
|
||||
options: options,
|
||||
encodedStream: io.MultiReader(chunkReader,
|
||||
trailerReader,
|
||||
bytes.NewBuffer(crlfBytes),
|
||||
),
|
||||
trailerEncodedLength: trailerReader.EncodedLength(),
|
||||
}
|
||||
}
|
||||
|
||||
// EncodedLength returns the final length of the aws-chunked content encoded
|
||||
// stream if it can be determined without reading the underlying stream or lazy
|
||||
// header values, otherwise -1 is returned.
|
||||
func (e *awsChunkedEncoding) EncodedLength() int64 {
|
||||
var length int64
|
||||
if e.options.StreamLength == -1 || e.trailerEncodedLength == -1 {
|
||||
return -1
|
||||
}
|
||||
|
||||
if e.options.StreamLength != 0 {
|
||||
// If the stream length is known, and there is no chunk length specified,
|
||||
// only a single chunk will be used. Otherwise the stream length needs to
|
||||
// include the multiple chunk padding content.
|
||||
if e.options.ChunkLength == -1 {
|
||||
length += getUnsignedChunkBytesLength(e.options.StreamLength)
|
||||
|
||||
} else {
|
||||
// Compute chunk header and payload length
|
||||
numChunks := e.options.StreamLength / int64(e.options.ChunkLength)
|
||||
length += numChunks * getUnsignedChunkBytesLength(int64(e.options.ChunkLength))
|
||||
if remainder := e.options.StreamLength % int64(e.options.ChunkLength); remainder != 0 {
|
||||
length += getUnsignedChunkBytesLength(remainder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// End chunk
|
||||
length += int64(len(finalChunkBytes))
|
||||
|
||||
// Trailers
|
||||
length += int64(e.trailerEncodedLength)
|
||||
|
||||
// Encoding terminator
|
||||
length += int64(len(crlf))
|
||||
|
||||
return length
|
||||
}
|
||||
|
||||
func getUnsignedChunkBytesLength(payloadLength int64) int64 {
|
||||
payloadLengthStr := strconv.FormatInt(payloadLength, 16)
|
||||
return int64(len(payloadLengthStr)) + int64(len(crlf)) + payloadLength + int64(len(crlf))
|
||||
}
|
||||
|
||||
// HTTPHeaders returns the set of headers that must be included the request for
|
||||
// aws-chunked to work. This includes the content-encoding: aws-chunked header.
|
||||
//
|
||||
// If there are multiple layered content encoding, the aws-chunked encoding
|
||||
// must be appended to the previous layers the stream's encoding. The best way
|
||||
// to do this is to append all header values returned to the HTTP request's set
|
||||
// of headers.
|
||||
func (e *awsChunkedEncoding) HTTPHeaders() map[string][]string {
|
||||
headers := map[string][]string{
|
||||
contentEncodingHeaderName: {
|
||||
awsChunkedContentEncodingHeaderValue,
|
||||
},
|
||||
}
|
||||
|
||||
if len(e.options.Trailers) != 0 {
|
||||
trailers := make([]string, 0, len(e.options.Trailers))
|
||||
for name := range e.options.Trailers {
|
||||
trailers = append(trailers, strings.ToLower(name))
|
||||
}
|
||||
headers[awsTrailerHeaderName] = trailers
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
func (e *awsChunkedEncoding) Read(b []byte) (n int, err error) {
|
||||
return e.encodedStream.Read(b)
|
||||
}
|
||||
|
||||
// awsChunkedTrailerReader provides a lazy reader for reading of aws-chunked
|
||||
// content encoded trailers. The trailer values will not be retrieved until the
|
||||
// reader is read from.
|
||||
type awsChunkedTrailerReader struct {
|
||||
reader *bytes.Buffer
|
||||
trailers map[string]awsChunkedTrailerValue
|
||||
trailerEncodedLength int
|
||||
}
|
||||
|
||||
// newAWSChunkedTrailerReader returns an initialized awsChunkedTrailerReader to
|
||||
// lazy reading aws-chunk content encoded trailers.
|
||||
func newAWSChunkedTrailerReader(trailers map[string]awsChunkedTrailerValue) *awsChunkedTrailerReader {
|
||||
return &awsChunkedTrailerReader{
|
||||
trailers: trailers,
|
||||
trailerEncodedLength: trailerEncodedLength(trailers),
|
||||
}
|
||||
}
|
||||
|
||||
func trailerEncodedLength(trailers map[string]awsChunkedTrailerValue) (length int) {
|
||||
for name, trailer := range trailers {
|
||||
length += len(name) + len(trailerKeyValueSeparator)
|
||||
l := trailer.Length
|
||||
if l == -1 {
|
||||
return -1
|
||||
}
|
||||
length += l + len(crlf)
|
||||
}
|
||||
|
||||
return length
|
||||
}
|
||||
|
||||
// EncodedLength returns the length of the encoded trailers if the length could
|
||||
// be determined without retrieving the header values. Returns -1 if length is
|
||||
// unknown.
|
||||
func (r *awsChunkedTrailerReader) EncodedLength() (length int) {
|
||||
return r.trailerEncodedLength
|
||||
}
|
||||
|
||||
// Read populates the passed in byte slice with bytes from the encoded
|
||||
// trailers. Will lazy read header values first time Read is called.
|
||||
func (r *awsChunkedTrailerReader) Read(p []byte) (int, error) {
|
||||
if r.trailerEncodedLength == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if r.reader == nil {
|
||||
trailerLen := r.trailerEncodedLength
|
||||
if r.trailerEncodedLength == -1 {
|
||||
trailerLen = 0
|
||||
}
|
||||
r.reader = bytes.NewBuffer(make([]byte, 0, trailerLen))
|
||||
for name, trailer := range r.trailers {
|
||||
r.reader.WriteString(name)
|
||||
r.reader.WriteString(trailerKeyValueSeparator)
|
||||
v, err := trailer.Get()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get trailer value, %w", err)
|
||||
}
|
||||
r.reader.WriteString(v)
|
||||
r.reader.WriteString(crlf)
|
||||
}
|
||||
}
|
||||
|
||||
return r.reader.Read(p)
|
||||
}
|
||||
|
||||
// newUnsignedChunkReader returns an io.Reader encoding the underlying reader
|
||||
// as unsigned aws-chunked chunks. The returned reader will also include the
|
||||
// end chunk, but not the aws-chunked final `crlf` segment so trailers can be
|
||||
// added.
|
||||
//
|
||||
// If the payload size is -1 for unknown length the content will be buffered in
|
||||
// defaultChunkLength chunks before wrapped in aws-chunked chunk encoding.
|
||||
func newUnsignedChunkReader(reader io.Reader, payloadSize int64) io.Reader {
|
||||
if payloadSize == -1 {
|
||||
return newBufferedAWSChunkReader(reader, defaultChunkLength)
|
||||
}
|
||||
|
||||
var endChunk bytes.Buffer
|
||||
if payloadSize == 0 {
|
||||
endChunk.Write(finalChunkBytes)
|
||||
return &endChunk
|
||||
}
|
||||
|
||||
endChunk.WriteString(crlf)
|
||||
endChunk.Write(finalChunkBytes)
|
||||
|
||||
var header bytes.Buffer
|
||||
header.WriteString(strconv.FormatInt(payloadSize, 16))
|
||||
header.WriteString(crlf)
|
||||
return io.MultiReader(
|
||||
&header,
|
||||
reader,
|
||||
&endChunk,
|
||||
)
|
||||
}
|
||||
|
||||
// Provides a buffered aws-chunked chunk encoder of an underlying io.Reader.
|
||||
// Will include end chunk, but not the aws-chunked final `crlf` segment so
|
||||
// trailers can be added.
|
||||
//
|
||||
// Note does not implement support for chunk extensions, e.g. chunk signing.
|
||||
type bufferedAWSChunkReader struct {
|
||||
reader io.Reader
|
||||
chunkSize int
|
||||
chunkSizeStr string
|
||||
|
||||
headerBuffer *bytes.Buffer
|
||||
chunkBuffer *bytes.Buffer
|
||||
|
||||
multiReader io.Reader
|
||||
multiReaderLen int
|
||||
endChunkDone bool
|
||||
}
|
||||
|
||||
// newBufferedAWSChunkReader returns an bufferedAWSChunkReader for reading
|
||||
// aws-chunked encoded chunks.
|
||||
func newBufferedAWSChunkReader(reader io.Reader, chunkSize int) *bufferedAWSChunkReader {
|
||||
return &bufferedAWSChunkReader{
|
||||
reader: reader,
|
||||
chunkSize: chunkSize,
|
||||
chunkSizeStr: strconv.FormatInt(int64(chunkSize), 16),
|
||||
|
||||
headerBuffer: bytes.NewBuffer(make([]byte, 0, 64)),
|
||||
chunkBuffer: bytes.NewBuffer(make([]byte, 0, chunkSize+len(crlf))),
|
||||
}
|
||||
}
|
||||
|
||||
// Read attempts to read from the underlying io.Reader writing aws-chunked
|
||||
// chunk encoded bytes to p. When the underlying io.Reader has been completed
|
||||
// read the end chunk will be available. Once the end chunk is read, the reader
|
||||
// will return EOF.
|
||||
func (r *bufferedAWSChunkReader) Read(p []byte) (n int, err error) {
|
||||
if r.multiReaderLen == 0 && r.endChunkDone {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if r.multiReader == nil || r.multiReaderLen == 0 {
|
||||
r.multiReader, r.multiReaderLen, err = r.newMultiReader()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
n, err = r.multiReader.Read(p)
|
||||
r.multiReaderLen -= n
|
||||
|
||||
if err == io.EOF && !r.endChunkDone {
|
||||
// Edge case handling when the multi-reader has been completely read,
|
||||
// and returned an EOF, make sure that EOF only gets returned if the
|
||||
// end chunk was included in the multi-reader. Otherwise, the next call
|
||||
// to read will initialize the next chunk's multi-reader.
|
||||
err = nil
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// newMultiReader returns a new io.Reader for wrapping the next chunk. Will
|
||||
// return an error if the underlying reader can not be read from. Will never
|
||||
// return io.EOF.
|
||||
func (r *bufferedAWSChunkReader) newMultiReader() (io.Reader, int, error) {
|
||||
// io.Copy eats the io.EOF returned by io.LimitReader. Any error that
|
||||
// occurs here is due to an actual read error.
|
||||
n, err := io.Copy(r.chunkBuffer, io.LimitReader(r.reader, int64(r.chunkSize)))
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if n == 0 {
|
||||
// Early exit writing out only the end chunk. This does not include
|
||||
// aws-chunk's final `crlf` so that trailers can still be added by
|
||||
// upstream reader.
|
||||
r.headerBuffer.Reset()
|
||||
r.headerBuffer.WriteString("0")
|
||||
r.headerBuffer.WriteString(crlf)
|
||||
r.endChunkDone = true
|
||||
|
||||
return r.headerBuffer, r.headerBuffer.Len(), nil
|
||||
}
|
||||
r.chunkBuffer.WriteString(crlf)
|
||||
|
||||
chunkSizeStr := r.chunkSizeStr
|
||||
if int(n) != r.chunkSize {
|
||||
chunkSizeStr = strconv.FormatInt(n, 16)
|
||||
}
|
||||
|
||||
r.headerBuffer.Reset()
|
||||
r.headerBuffer.WriteString(chunkSizeStr)
|
||||
r.headerBuffer.WriteString(crlf)
|
||||
|
||||
return io.MultiReader(
|
||||
r.headerBuffer,
|
||||
r.chunkBuffer,
|
||||
), r.headerBuffer.Len() + r.chunkBuffer.Len(), nil
|
||||
}
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT.
|
||||
|
||||
package checksum
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.5.5"
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
package checksum
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
)
|
||||
|
||||
// InputMiddlewareOptions provides the options for the request
|
||||
// checksum middleware setup.
|
||||
type InputMiddlewareOptions struct {
|
||||
// GetAlgorithm is a function to get the checksum algorithm of the
|
||||
// input payload from the input parameters.
|
||||
//
|
||||
// Given the input parameter value, the function must return the algorithm
|
||||
// and true, or false if no algorithm is specified.
|
||||
GetAlgorithm func(interface{}) (string, bool)
|
||||
|
||||
// RequireChecksum indicates whether operation model forces middleware to compute the input payload's checksum.
|
||||
// If RequireChecksum is set to true, checksum will be calculated and RequestChecksumCalculation will be ignored,
|
||||
// otherwise RequestChecksumCalculation will be used to indicate if checksum will be calculated
|
||||
RequireChecksum bool
|
||||
|
||||
// RequestChecksumCalculation is the user config to opt-in/out request checksum calculation. If RequireChecksum is
|
||||
// set to true, checksum will be calculated and this field will be ignored, otherwise
|
||||
// RequestChecksumCalculation will be used to indicate if checksum will be calculated
|
||||
RequestChecksumCalculation aws.RequestChecksumCalculation
|
||||
|
||||
// Enables support for wrapping the serialized input payload with a
|
||||
// content-encoding: aws-check wrapper, and including a trailer for the
|
||||
// algorithm's checksum value.
|
||||
//
|
||||
// The checksum will not be computed, nor added as trailing checksum, if
|
||||
// the Algorithm's header is already set on the request.
|
||||
EnableTrailingChecksum bool
|
||||
|
||||
// Enables support for computing the SHA256 checksum of input payloads
|
||||
// along with the algorithm specified checksum. Prevents downstream
|
||||
// middleware handlers (computePayloadSHA256) re-reading the payload.
|
||||
//
|
||||
// The SHA256 payload checksum will only be used for computed for requests
|
||||
// that are not TLS, or do not enable trailing checksums.
|
||||
//
|
||||
// The SHA256 payload hash will not be computed, if the Algorithm's header
|
||||
// is already set on the request.
|
||||
EnableComputeSHA256PayloadHash bool
|
||||
|
||||
// Enables support for setting the aws-chunked decoded content length
|
||||
// header for the decoded length of the underlying stream. Will only be set
|
||||
// when used with trailing checksums, and aws-chunked content-encoding.
|
||||
EnableDecodedContentLengthHeader bool
|
||||
}
|
||||
|
||||
// AddInputMiddleware adds the middleware for performing checksum computing
|
||||
// of request payloads, and checksum validation of response payloads.
|
||||
//
|
||||
// Deprecated: This internal-only runtime API is frozen. Do not call or modify
|
||||
// it in new code. Checksum-enabled service operations now generate this
|
||||
// middleware setup code inline per #2507.
|
||||
func AddInputMiddleware(stack *middleware.Stack, options InputMiddlewareOptions) (err error) {
|
||||
// Initial checksum configuration look up middleware
|
||||
err = stack.Initialize.Add(&SetupInputContext{
|
||||
GetAlgorithm: options.GetAlgorithm,
|
||||
RequireChecksum: options.RequireChecksum,
|
||||
RequestChecksumCalculation: options.RequestChecksumCalculation,
|
||||
}, middleware.Before)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stack.Build.Remove("ContentChecksum")
|
||||
|
||||
inputChecksum := &ComputeInputPayloadChecksum{
|
||||
EnableTrailingChecksum: options.EnableTrailingChecksum,
|
||||
EnableComputePayloadHash: options.EnableComputeSHA256PayloadHash,
|
||||
EnableDecodedContentLengthHeader: options.EnableDecodedContentLengthHeader,
|
||||
}
|
||||
if err := stack.Finalize.Insert(inputChecksum, "ResolveEndpointV2", middleware.After); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If trailing checksum is not supported no need for finalize handler to be added.
|
||||
if options.EnableTrailingChecksum {
|
||||
trailerMiddleware := &AddInputChecksumTrailer{
|
||||
EnableTrailingChecksum: inputChecksum.EnableTrailingChecksum,
|
||||
EnableComputePayloadHash: inputChecksum.EnableComputePayloadHash,
|
||||
EnableDecodedContentLengthHeader: inputChecksum.EnableDecodedContentLengthHeader,
|
||||
}
|
||||
if err := stack.Finalize.Insert(trailerMiddleware, "Retry", middleware.After); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveInputMiddleware Removes the compute input payload checksum middleware
|
||||
// handlers from the stack.
|
||||
func RemoveInputMiddleware(stack *middleware.Stack) {
|
||||
id := (*SetupInputContext)(nil).ID()
|
||||
stack.Initialize.Remove(id)
|
||||
|
||||
id = (*ComputeInputPayloadChecksum)(nil).ID()
|
||||
stack.Finalize.Remove(id)
|
||||
}
|
||||
|
||||
// OutputMiddlewareOptions provides options for configuring output checksum
|
||||
// validation middleware.
|
||||
type OutputMiddlewareOptions struct {
|
||||
// GetValidationMode is a function to get the checksum validation
|
||||
// mode of the output payload from the input parameters.
|
||||
//
|
||||
// Given the input parameter value, the function must return the validation
|
||||
// mode and true, or false if no mode is specified.
|
||||
GetValidationMode func(interface{}) (string, bool)
|
||||
|
||||
// SetValidationMode is a function to set the checksum validation mode of input parameters
|
||||
SetValidationMode func(interface{}, string)
|
||||
|
||||
// ResponseChecksumValidation is the user config to opt-in/out response checksum validation
|
||||
ResponseChecksumValidation aws.ResponseChecksumValidation
|
||||
|
||||
// The set of checksum algorithms that should be used for response payload
|
||||
// checksum validation. The algorithm(s) used will be a union of the
|
||||
// output's returned algorithms and this set.
|
||||
//
|
||||
// Only the first algorithm in the union is currently used.
|
||||
ValidationAlgorithms []string
|
||||
|
||||
// If set the middleware will ignore output multipart checksums. Otherwise
|
||||
// a checksum format error will be returned by the middleware.
|
||||
IgnoreMultipartValidation bool
|
||||
|
||||
// When set the middleware will log when output does not have checksum or
|
||||
// algorithm to validate.
|
||||
LogValidationSkipped bool
|
||||
|
||||
// When set the middleware will log when the output contains a multipart
|
||||
// checksum that was, skipped and not validated.
|
||||
LogMultipartValidationSkipped bool
|
||||
}
|
||||
|
||||
// AddOutputMiddleware adds the middleware for validating response payload's
|
||||
// checksum.
|
||||
func AddOutputMiddleware(stack *middleware.Stack, options OutputMiddlewareOptions) error {
|
||||
err := stack.Initialize.Add(&setupOutputContext{
|
||||
GetValidationMode: options.GetValidationMode,
|
||||
SetValidationMode: options.SetValidationMode,
|
||||
ResponseChecksumValidation: options.ResponseChecksumValidation,
|
||||
}, middleware.Before)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Resolve a supported priority order list of algorithms to validate.
|
||||
algorithms := FilterSupportedAlgorithms(options.ValidationAlgorithms)
|
||||
|
||||
m := &validateOutputPayloadChecksum{
|
||||
Algorithms: algorithms,
|
||||
IgnoreMultipartValidation: options.IgnoreMultipartValidation,
|
||||
LogMultipartValidationSkipped: options.LogMultipartValidationSkipped,
|
||||
LogValidationSkipped: options.LogValidationSkipped,
|
||||
}
|
||||
|
||||
return stack.Deserialize.Add(m, middleware.After)
|
||||
}
|
||||
|
||||
// RemoveOutputMiddleware Removes the compute input payload checksum middleware
|
||||
// handlers from the stack.
|
||||
func RemoveOutputMiddleware(stack *middleware.Stack) {
|
||||
id := (*setupOutputContext)(nil).ID()
|
||||
stack.Initialize.Remove(id)
|
||||
|
||||
id = (*validateOutputPayloadChecksum)(nil).ID()
|
||||
stack.Deserialize.Remove(id)
|
||||
}
|
||||
Generated
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
package checksum
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
var supportedChecksumFeatures = map[Algorithm]awsmiddleware.UserAgentFeature{
|
||||
AlgorithmCRC32: awsmiddleware.UserAgentFeatureRequestChecksumCRC32,
|
||||
AlgorithmCRC32C: awsmiddleware.UserAgentFeatureRequestChecksumCRC32C,
|
||||
AlgorithmSHA1: awsmiddleware.UserAgentFeatureRequestChecksumSHA1,
|
||||
AlgorithmSHA256: awsmiddleware.UserAgentFeatureRequestChecksumSHA256,
|
||||
AlgorithmCRC64NVME: awsmiddleware.UserAgentFeatureRequestChecksumCRC64,
|
||||
}
|
||||
|
||||
// RequestChecksumMetricsTracking is the middleware to track operation request's checksum usage
|
||||
type RequestChecksumMetricsTracking struct {
|
||||
RequestChecksumCalculation aws.RequestChecksumCalculation
|
||||
UserAgent *awsmiddleware.RequestUserAgent
|
||||
}
|
||||
|
||||
// ID provides the middleware identifier
|
||||
func (m *RequestChecksumMetricsTracking) ID() string {
|
||||
return "AWSChecksum:RequestMetricsTracking"
|
||||
}
|
||||
|
||||
// HandleBuild checks request checksum config and checksum value sent
|
||||
// and sends corresponding feature id to user agent
|
||||
func (m *RequestChecksumMetricsTracking) HandleBuild(
|
||||
ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler,
|
||||
) (
|
||||
out middleware.BuildOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, fmt.Errorf("unknown request type %T", req)
|
||||
}
|
||||
|
||||
switch m.RequestChecksumCalculation {
|
||||
case aws.RequestChecksumCalculationWhenSupported:
|
||||
m.UserAgent.AddUserAgentFeature(awsmiddleware.UserAgentFeatureRequestChecksumWhenSupported)
|
||||
case aws.RequestChecksumCalculationWhenRequired:
|
||||
m.UserAgent.AddUserAgentFeature(awsmiddleware.UserAgentFeatureRequestChecksumWhenRequired)
|
||||
}
|
||||
|
||||
for algo, feat := range supportedChecksumFeatures {
|
||||
checksumHeader := AlgorithmHTTPHeader(algo)
|
||||
if checksum := req.Header.Get(checksumHeader); checksum != "" {
|
||||
m.UserAgent.AddUserAgentFeature(feat)
|
||||
}
|
||||
}
|
||||
|
||||
return next.HandleBuild(ctx, in)
|
||||
}
|
||||
|
||||
// ResponseChecksumMetricsTracking is the middleware to track operation response's checksum usage
|
||||
type ResponseChecksumMetricsTracking struct {
|
||||
ResponseChecksumValidation aws.ResponseChecksumValidation
|
||||
UserAgent *awsmiddleware.RequestUserAgent
|
||||
}
|
||||
|
||||
// ID provides the middleware identifier
|
||||
func (m *ResponseChecksumMetricsTracking) ID() string {
|
||||
return "AWSChecksum:ResponseMetricsTracking"
|
||||
}
|
||||
|
||||
// HandleBuild checks the response checksum config and sends corresponding feature id to user agent
|
||||
func (m *ResponseChecksumMetricsTracking) HandleBuild(
|
||||
ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler,
|
||||
) (
|
||||
out middleware.BuildOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, fmt.Errorf("unknown request type %T", req)
|
||||
}
|
||||
|
||||
switch m.ResponseChecksumValidation {
|
||||
case aws.ResponseChecksumValidationWhenSupported:
|
||||
m.UserAgent.AddUserAgentFeature(awsmiddleware.UserAgentFeatureResponseChecksumWhenSupported)
|
||||
case aws.ResponseChecksumValidationWhenRequired:
|
||||
m.UserAgent.AddUserAgentFeature(awsmiddleware.UserAgentFeatureResponseChecksumWhenRequired)
|
||||
}
|
||||
|
||||
return next.HandleBuild(ctx, in)
|
||||
}
|
||||
Generated
Vendored
+428
@@ -0,0 +1,428 @@
|
||||
package checksum
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
||||
internalcontext "github.com/aws/aws-sdk-go-v2/internal/context"
|
||||
presignedurlcust "github.com/aws/aws-sdk-go-v2/service/internal/presigned-url"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
const (
|
||||
streamingUnsignedPayloadTrailerPayloadHash = "STREAMING-UNSIGNED-PAYLOAD-TRAILER"
|
||||
)
|
||||
|
||||
// computedInputChecksumsKey is the metadata key for recording the algorithm the
|
||||
// checksum was computed for and the checksum value.
|
||||
type computedInputChecksumsKey struct{}
|
||||
|
||||
// GetComputedInputChecksums returns the map of checksum algorithm to their
|
||||
// computed value stored in the middleware Metadata. Returns false if no values
|
||||
// were stored in the Metadata.
|
||||
func GetComputedInputChecksums(m middleware.Metadata) (map[string]string, bool) {
|
||||
vs, ok := m.Get(computedInputChecksumsKey{}).(map[string]string)
|
||||
return vs, ok
|
||||
}
|
||||
|
||||
// SetComputedInputChecksums stores the map of checksum algorithm to their
|
||||
// computed value in the middleware Metadata. Overwrites any values that
|
||||
// currently exist in the metadata.
|
||||
func SetComputedInputChecksums(m *middleware.Metadata, vs map[string]string) {
|
||||
m.Set(computedInputChecksumsKey{}, vs)
|
||||
}
|
||||
|
||||
// ComputeInputPayloadChecksum middleware computes payload checksum
|
||||
type ComputeInputPayloadChecksum struct {
|
||||
// Enables support for wrapping the serialized input payload with a
|
||||
// content-encoding: aws-check wrapper, and including a trailer for the
|
||||
// algorithm's checksum value.
|
||||
//
|
||||
// The checksum will not be computed, nor added as trailing checksum, if
|
||||
// the Algorithm's header is already set on the request.
|
||||
EnableTrailingChecksum bool
|
||||
|
||||
// Enables support for computing the SHA256 checksum of input payloads
|
||||
// along with the algorithm specified checksum. Prevents downstream
|
||||
// middleware handlers (computePayloadSHA256) re-reading the payload.
|
||||
//
|
||||
// The SHA256 payload hash will only be used for computed for requests
|
||||
// that are not TLS, or do not enable trailing checksums.
|
||||
//
|
||||
// The SHA256 payload hash will not be computed, if the Algorithm's header
|
||||
// is already set on the request.
|
||||
EnableComputePayloadHash bool
|
||||
|
||||
// Enables support for setting the aws-chunked decoded content length
|
||||
// header for the decoded length of the underlying stream. Will only be set
|
||||
// when used with trailing checksums, and aws-chunked content-encoding.
|
||||
EnableDecodedContentLengthHeader bool
|
||||
|
||||
useTrailer bool
|
||||
}
|
||||
|
||||
type useTrailer struct{}
|
||||
|
||||
// ID provides the middleware's identifier.
|
||||
func (m *ComputeInputPayloadChecksum) ID() string {
|
||||
return "AWSChecksum:ComputeInputPayloadChecksum"
|
||||
}
|
||||
|
||||
type computeInputHeaderChecksumError struct {
|
||||
Msg string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e computeInputHeaderChecksumError) Error() string {
|
||||
const intro = "compute input header checksum failed"
|
||||
|
||||
if e.Err != nil {
|
||||
return fmt.Sprintf("%s, %s, %v", intro, e.Msg, e.Err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s, %s", intro, e.Msg)
|
||||
}
|
||||
func (e computeInputHeaderChecksumError) Unwrap() error { return e.Err }
|
||||
|
||||
// HandleFinalize handles computing the payload's checksum, in the following cases:
|
||||
// - Is HTTP, not HTTPS
|
||||
// - RequireChecksum is true, and no checksums were specified via the Input
|
||||
// - Trailing checksums are not supported
|
||||
//
|
||||
// The build handler must be inserted in the stack before ContentPayloadHash
|
||||
// and after ComputeContentLength.
|
||||
func (m *ComputeInputPayloadChecksum) HandleFinalize(
|
||||
ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler,
|
||||
) (
|
||||
out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
var checksum string
|
||||
algorithm, ok, err := getInputAlgorithm(ctx)
|
||||
if err != nil {
|
||||
return out, metadata, err
|
||||
}
|
||||
if !ok {
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, computeInputHeaderChecksumError{
|
||||
Msg: fmt.Sprintf("unknown request type %T", req),
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if algorithm == "" || checksum == "" || err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Record the checksum and algorithm that was computed
|
||||
SetComputedInputChecksums(&metadata, map[string]string{
|
||||
string(algorithm): checksum,
|
||||
})
|
||||
}()
|
||||
|
||||
// If any checksum header is already set nothing to do.
|
||||
for header := range req.Header {
|
||||
h := strings.ToUpper(header)
|
||||
if strings.HasPrefix(h, "X-AMZ-CHECKSUM-") {
|
||||
algorithm = Algorithm(strings.TrimPrefix(h, "X-AMZ-CHECKSUM-"))
|
||||
checksum = req.Header.Get(header)
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
}
|
||||
|
||||
computePayloadHash := m.EnableComputePayloadHash
|
||||
if v := v4.GetPayloadHash(ctx); v != "" {
|
||||
computePayloadHash = false
|
||||
}
|
||||
|
||||
stream := req.GetStream()
|
||||
streamLength, err := getRequestStreamLength(req)
|
||||
if err != nil {
|
||||
return out, metadata, computeInputHeaderChecksumError{
|
||||
Msg: "failed to determine stream length",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
// If trailing checksums are supported, the request is HTTPS, and the
|
||||
// stream is not nil or empty, instead switch to a trailing checksum.
|
||||
//
|
||||
// Nil and empty streams will always be handled as a request header,
|
||||
// regardless if the operation supports trailing checksums or not.
|
||||
if req.IsHTTPS() && !presignedurlcust.GetIsPresigning(ctx) {
|
||||
if stream != nil && streamLength != 0 && m.EnableTrailingChecksum {
|
||||
if m.EnableComputePayloadHash {
|
||||
// ContentSHA256Header middleware handles the header
|
||||
ctx = v4.SetPayloadHash(ctx, streamingUnsignedPayloadTrailerPayloadHash)
|
||||
}
|
||||
m.useTrailer = true
|
||||
ctx = middleware.WithStackValue(ctx, useTrailer{}, true)
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
|
||||
// If trailing checksums are not enabled but protocol is still HTTPS
|
||||
// disabling computing the payload hash. Downstream middleware handler
|
||||
// (ComputetPayloadHash) will set the payload hash to unsigned payload,
|
||||
// if signing was used.
|
||||
computePayloadHash = false
|
||||
}
|
||||
|
||||
// Only seekable streams are supported for non-trailing checksums, because
|
||||
// the stream needs to be rewound before the handler can continue.
|
||||
if stream != nil && !req.IsStreamSeekable() {
|
||||
return out, metadata, computeInputHeaderChecksumError{
|
||||
Msg: "unseekable stream is not supported without TLS and trailing checksum",
|
||||
}
|
||||
}
|
||||
|
||||
var sha256Checksum string
|
||||
checksum, sha256Checksum, err = computeStreamChecksum(
|
||||
algorithm, stream, computePayloadHash)
|
||||
if err != nil {
|
||||
return out, metadata, computeInputHeaderChecksumError{
|
||||
Msg: "failed to compute stream checksum",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
if err := req.RewindStream(); err != nil {
|
||||
return out, metadata, computeInputHeaderChecksumError{
|
||||
Msg: "failed to rewind stream",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
checksumHeader := AlgorithmHTTPHeader(algorithm)
|
||||
req.Header.Set(checksumHeader, checksum)
|
||||
|
||||
if computePayloadHash {
|
||||
ctx = v4.SetPayloadHash(ctx, sha256Checksum)
|
||||
}
|
||||
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
|
||||
type computeInputTrailingChecksumError struct {
|
||||
Msg string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e computeInputTrailingChecksumError) Error() string {
|
||||
const intro = "compute input trailing checksum failed"
|
||||
|
||||
if e.Err != nil {
|
||||
return fmt.Sprintf("%s, %s, %v", intro, e.Msg, e.Err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s, %s", intro, e.Msg)
|
||||
}
|
||||
func (e computeInputTrailingChecksumError) Unwrap() error { return e.Err }
|
||||
|
||||
// AddInputChecksumTrailer adds HTTP checksum when
|
||||
// - Is HTTPS, not HTTP
|
||||
// - A checksum was specified via the Input
|
||||
// - Trailing checksums are supported.
|
||||
type AddInputChecksumTrailer struct {
|
||||
EnableTrailingChecksum bool
|
||||
EnableComputePayloadHash bool
|
||||
EnableDecodedContentLengthHeader bool
|
||||
}
|
||||
|
||||
// ID identifies this middleware.
|
||||
func (*AddInputChecksumTrailer) ID() string {
|
||||
return "addInputChecksumTrailer"
|
||||
}
|
||||
|
||||
// HandleFinalize wraps the request body to write the trailing checksum.
|
||||
func (m *AddInputChecksumTrailer) HandleFinalize(
|
||||
ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler,
|
||||
) (
|
||||
out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
algorithm, ok, err := getInputAlgorithm(ctx)
|
||||
if err != nil {
|
||||
return out, metadata, computeInputTrailingChecksumError{
|
||||
Msg: "failed to get algorithm",
|
||||
Err: err,
|
||||
}
|
||||
} else if !ok {
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
|
||||
if enabled, _ := middleware.GetStackValue(ctx, useTrailer{}).(bool); !enabled {
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, computeInputTrailingChecksumError{
|
||||
Msg: fmt.Sprintf("unknown request type %T", req),
|
||||
}
|
||||
}
|
||||
|
||||
// Trailing checksums are only supported when TLS is enabled.
|
||||
if !req.IsHTTPS() {
|
||||
return out, metadata, computeInputTrailingChecksumError{
|
||||
Msg: "HTTPS required",
|
||||
}
|
||||
}
|
||||
|
||||
// If any checksum header is already set nothing to do.
|
||||
for header := range req.Header {
|
||||
if strings.HasPrefix(strings.ToLower(header), "x-amz-checksum-") {
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
}
|
||||
|
||||
stream := req.GetStream()
|
||||
streamLength, err := getRequestStreamLength(req)
|
||||
if err != nil {
|
||||
return out, metadata, computeInputTrailingChecksumError{
|
||||
Msg: "failed to determine stream length",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
if stream == nil || streamLength == 0 {
|
||||
// Nil and empty streams are handled by the Build handler. They are not
|
||||
// supported by the trailing checksums finalize handler. There is no
|
||||
// benefit to sending them as trailers compared to headers.
|
||||
return out, metadata, computeInputTrailingChecksumError{
|
||||
Msg: "nil or empty streams are not supported",
|
||||
}
|
||||
}
|
||||
|
||||
checksumReader, err := newComputeChecksumReader(stream, algorithm)
|
||||
if err != nil {
|
||||
return out, metadata, computeInputTrailingChecksumError{
|
||||
Msg: "failed to created checksum reader",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
awsChunkedReader := newUnsignedAWSChunkedEncoding(checksumReader,
|
||||
func(o *awsChunkedEncodingOptions) {
|
||||
o.Trailers[AlgorithmHTTPHeader(checksumReader.Algorithm())] = awsChunkedTrailerValue{
|
||||
Get: checksumReader.Base64Checksum,
|
||||
Length: checksumReader.Base64ChecksumLength(),
|
||||
}
|
||||
o.StreamLength = streamLength
|
||||
})
|
||||
|
||||
for key, values := range awsChunkedReader.HTTPHeaders() {
|
||||
for _, value := range values {
|
||||
req.Header.Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Setting the stream on the request will create a copy. The content length
|
||||
// is not updated until after the request is copied to prevent impacting
|
||||
// upstream middleware.
|
||||
req, err = req.SetStream(awsChunkedReader)
|
||||
if err != nil {
|
||||
return out, metadata, computeInputTrailingChecksumError{
|
||||
Msg: "failed updating request to trailing checksum wrapped stream",
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
req.ContentLength = awsChunkedReader.EncodedLength()
|
||||
in.Request = req
|
||||
|
||||
// Add decoded content length header if original stream's content length is known.
|
||||
if streamLength != -1 && m.EnableDecodedContentLengthHeader {
|
||||
req.Header.Set(decodedContentLengthHeaderName, strconv.FormatInt(streamLength, 10))
|
||||
}
|
||||
|
||||
out, metadata, err = next.HandleFinalize(ctx, in)
|
||||
if err == nil {
|
||||
checksum, err := checksumReader.Base64Checksum()
|
||||
if err != nil {
|
||||
return out, metadata, fmt.Errorf("failed to get computed checksum, %w", err)
|
||||
}
|
||||
|
||||
// Record the checksum and algorithm that was computed
|
||||
SetComputedInputChecksums(&metadata, map[string]string{
|
||||
string(algorithm): checksum,
|
||||
})
|
||||
}
|
||||
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
func getInputAlgorithm(ctx context.Context) (Algorithm, bool, error) {
|
||||
ctxAlgorithm := internalcontext.GetChecksumInputAlgorithm(ctx)
|
||||
if ctxAlgorithm == "" {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
algorithm, err := ParseAlgorithm(ctxAlgorithm)
|
||||
if err != nil {
|
||||
return "", false, fmt.Errorf(
|
||||
"failed to parse algorithm, %w", err)
|
||||
}
|
||||
|
||||
return algorithm, true, nil
|
||||
}
|
||||
|
||||
func computeStreamChecksum(algorithm Algorithm, stream io.Reader, computePayloadHash bool) (
|
||||
checksum string, sha256Checksum string, err error,
|
||||
) {
|
||||
hasher, err := NewAlgorithmHash(algorithm)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf(
|
||||
"failed to get hasher for checksum algorithm, %w", err)
|
||||
}
|
||||
|
||||
var sha256Hasher hash.Hash
|
||||
var batchHasher io.Writer = hasher
|
||||
|
||||
// Compute payload hash for the protocol. To prevent another handler
|
||||
// (computePayloadSHA256) re-reading body also compute the SHA256 for
|
||||
// request signing. If configured checksum algorithm is SHA256, don't
|
||||
// double wrap stream with another SHA256 hasher.
|
||||
if computePayloadHash && algorithm != AlgorithmSHA256 {
|
||||
sha256Hasher = sha256.New()
|
||||
batchHasher = io.MultiWriter(hasher, sha256Hasher)
|
||||
}
|
||||
|
||||
if stream != nil {
|
||||
if _, err = io.Copy(batchHasher, stream); err != nil {
|
||||
return "", "", fmt.Errorf(
|
||||
"failed to read stream to compute hash, %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
checksum = string(base64EncodeHashSum(hasher))
|
||||
if computePayloadHash {
|
||||
if algorithm != AlgorithmSHA256 {
|
||||
sha256Checksum = string(hexEncodeHashSum(sha256Hasher))
|
||||
} else {
|
||||
sha256Checksum = string(hexEncodeHashSum(hasher))
|
||||
}
|
||||
}
|
||||
|
||||
return checksum, sha256Checksum, nil
|
||||
}
|
||||
|
||||
func getRequestStreamLength(req *smithyhttp.Request) (int64, error) {
|
||||
if v := req.ContentLength; v > 0 {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
if length, ok, err := req.StreamLength(); err != nil {
|
||||
return 0, fmt.Errorf("failed getting request stream's length, %w", err)
|
||||
} else if ok {
|
||||
return length, nil
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
Generated
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
package checksum
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
|
||||
internalcontext "github.com/aws/aws-sdk-go-v2/internal/context"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
)
|
||||
|
||||
const (
|
||||
checksumValidationModeEnabled = "ENABLED"
|
||||
)
|
||||
|
||||
// SetupInputContext is the initial middleware that looks up the input
|
||||
// used to configure checksum behavior. This middleware must be executed before
|
||||
// input validation step or any other checksum middleware.
|
||||
type SetupInputContext struct {
|
||||
// GetAlgorithm is a function to get the checksum algorithm of the
|
||||
// input payload from the input parameters.
|
||||
//
|
||||
// Given the input parameter value, the function must return the algorithm
|
||||
// and true, or false if no algorithm is specified.
|
||||
GetAlgorithm func(interface{}) (string, bool)
|
||||
|
||||
// RequireChecksum indicates whether operation model forces middleware to compute the input payload's checksum.
|
||||
// If RequireChecksum is set to true, checksum will be calculated and RequestChecksumCalculation will be ignored,
|
||||
// otherwise RequestChecksumCalculation will be used to indicate if checksum will be calculated
|
||||
RequireChecksum bool
|
||||
|
||||
// RequestChecksumCalculation is the user config to opt-in/out request checksum calculation. If RequireChecksum is
|
||||
// set to true, checksum will be calculated and this field will be ignored, otherwise
|
||||
// RequestChecksumCalculation will be used to indicate if checksum will be calculated
|
||||
RequestChecksumCalculation aws.RequestChecksumCalculation
|
||||
}
|
||||
|
||||
// ID for the middleware
|
||||
func (m *SetupInputContext) ID() string {
|
||||
return "AWSChecksum:SetupInputContext"
|
||||
}
|
||||
|
||||
// HandleInitialize initialization middleware that setups up the checksum
|
||||
// context based on the input parameters provided in the stack.
|
||||
func (m *SetupInputContext) HandleInitialize(
|
||||
ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler,
|
||||
) (
|
||||
out middleware.InitializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
// nil check here is for operations that require checksum but do not have input algorithm setting
|
||||
if m.GetAlgorithm != nil {
|
||||
if algorithm, ok := m.GetAlgorithm(in.Parameters); ok {
|
||||
ctx = internalcontext.SetChecksumInputAlgorithm(ctx, algorithm)
|
||||
return next.HandleInitialize(ctx, in)
|
||||
}
|
||||
}
|
||||
|
||||
if m.RequireChecksum || m.RequestChecksumCalculation == aws.RequestChecksumCalculationWhenSupported {
|
||||
ctx = internalcontext.SetChecksumInputAlgorithm(ctx, string(AlgorithmCRC32))
|
||||
}
|
||||
|
||||
return next.HandleInitialize(ctx, in)
|
||||
}
|
||||
|
||||
type setupOutputContext struct {
|
||||
// GetValidationMode is a function to get the checksum validation
|
||||
// mode of the output payload from the input parameters.
|
||||
//
|
||||
// Given the input parameter value, the function must return the validation
|
||||
// mode and true, or false if no mode is specified.
|
||||
GetValidationMode func(interface{}) (string, bool)
|
||||
|
||||
// SetValidationMode is a function to set the checksum validation mode of input parameters
|
||||
SetValidationMode func(interface{}, string)
|
||||
|
||||
// ResponseChecksumValidation states user config to opt-in/out checksum validation
|
||||
ResponseChecksumValidation aws.ResponseChecksumValidation
|
||||
}
|
||||
|
||||
// ID for the middleware
|
||||
func (m *setupOutputContext) ID() string {
|
||||
return "AWSChecksum:SetupOutputContext"
|
||||
}
|
||||
|
||||
// HandleInitialize initialization middleware that setups up the checksum
|
||||
// context based on the input parameters provided in the stack.
|
||||
func (m *setupOutputContext) HandleInitialize(
|
||||
ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler,
|
||||
) (
|
||||
out middleware.InitializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
|
||||
mode, _ := m.GetValidationMode(in.Parameters)
|
||||
|
||||
if m.ResponseChecksumValidation == aws.ResponseChecksumValidationWhenSupported || mode == checksumValidationModeEnabled {
|
||||
m.SetValidationMode(in.Parameters, checksumValidationModeEnabled)
|
||||
ctx = setContextOutputValidationMode(ctx, checksumValidationModeEnabled)
|
||||
}
|
||||
|
||||
return next.HandleInitialize(ctx, in)
|
||||
}
|
||||
|
||||
// outputValidationModeKey is the key set on context used to identify if
|
||||
// output checksum validation is enabled.
|
||||
type outputValidationModeKey struct{}
|
||||
|
||||
// setContextOutputValidationMode sets the request checksum
|
||||
// algorithm on the context.
|
||||
//
|
||||
// Scoped to stack values.
|
||||
func setContextOutputValidationMode(ctx context.Context, value string) context.Context {
|
||||
return middleware.WithStackValue(ctx, outputValidationModeKey{}, value)
|
||||
}
|
||||
|
||||
// getContextOutputValidationMode returns response checksum validation state,
|
||||
// if one was specified. Empty string is returned if one is not specified.
|
||||
//
|
||||
// Scoped to stack values.
|
||||
func getContextOutputValidationMode(ctx context.Context) (v string) {
|
||||
v, _ = middleware.GetStackValue(ctx, outputValidationModeKey{}).(string)
|
||||
return v
|
||||
}
|
||||
Generated
Vendored
+128
@@ -0,0 +1,128 @@
|
||||
package checksum
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/smithy-go"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// outputValidationAlgorithmsUsedKey is the metadata key for indexing the algorithms
|
||||
// that were used, by the middleware's validation.
|
||||
type outputValidationAlgorithmsUsedKey struct{}
|
||||
|
||||
// GetOutputValidationAlgorithmsUsed returns the checksum algorithms used
|
||||
// stored in the middleware Metadata. Returns false if no algorithms were
|
||||
// stored in the Metadata.
|
||||
func GetOutputValidationAlgorithmsUsed(m middleware.Metadata) ([]string, bool) {
|
||||
vs, ok := m.Get(outputValidationAlgorithmsUsedKey{}).([]string)
|
||||
return vs, ok
|
||||
}
|
||||
|
||||
// SetOutputValidationAlgorithmsUsed stores the checksum algorithms used in the
|
||||
// middleware Metadata.
|
||||
func SetOutputValidationAlgorithmsUsed(m *middleware.Metadata, vs []string) {
|
||||
m.Set(outputValidationAlgorithmsUsedKey{}, vs)
|
||||
}
|
||||
|
||||
// validateOutputPayloadChecksum middleware computes payload checksum of the
|
||||
// received response and validates with checksum returned by the service.
|
||||
type validateOutputPayloadChecksum struct {
|
||||
// Algorithms represents a priority-ordered list of valid checksum
|
||||
// algorithm that should be validated when present in HTTP response
|
||||
// headers.
|
||||
Algorithms []Algorithm
|
||||
|
||||
// IgnoreMultipartValidation indicates multipart checksums ending with "-#"
|
||||
// will be ignored.
|
||||
IgnoreMultipartValidation bool
|
||||
|
||||
// When set the middleware will log when output does not have checksum or
|
||||
// algorithm to validate.
|
||||
LogValidationSkipped bool
|
||||
|
||||
// When set the middleware will log when the output contains a multipart
|
||||
// checksum that was, skipped and not validated.
|
||||
LogMultipartValidationSkipped bool
|
||||
}
|
||||
|
||||
func (m *validateOutputPayloadChecksum) ID() string {
|
||||
return "AWSChecksum:ValidateOutputPayloadChecksum"
|
||||
}
|
||||
|
||||
// HandleDeserialize is a Deserialize middleware that wraps the HTTP response
|
||||
// body with an io.ReadCloser that will validate its checksum.
|
||||
func (m *validateOutputPayloadChecksum) HandleDeserialize(
|
||||
ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler,
|
||||
) (
|
||||
out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
out, metadata, err = next.HandleDeserialize(ctx, in)
|
||||
if err != nil {
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
if mode := getContextOutputValidationMode(ctx); mode != checksumValidationModeEnabled {
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
response, ok := out.RawResponse.(*smithyhttp.Response)
|
||||
if !ok {
|
||||
return out, metadata, &smithy.DeserializationError{
|
||||
Err: fmt.Errorf("unknown transport type %T", out.RawResponse),
|
||||
}
|
||||
}
|
||||
|
||||
var expectedChecksum string
|
||||
var algorithmToUse Algorithm
|
||||
for _, algorithm := range m.Algorithms {
|
||||
value := response.Header.Get(AlgorithmHTTPHeader(algorithm))
|
||||
if len(value) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
expectedChecksum = value
|
||||
algorithmToUse = algorithm
|
||||
}
|
||||
|
||||
logger := middleware.GetLogger(ctx)
|
||||
|
||||
// Skip validation if no checksum algorithm or checksum is available.
|
||||
if len(expectedChecksum) == 0 || len(algorithmToUse) == 0 {
|
||||
if m.LogValidationSkipped {
|
||||
// TODO this probably should have more information about the
|
||||
// operation output that won't be validated.
|
||||
logger.Logf(logging.Warn,
|
||||
"Response has no supported checksum. Not validating response payload.")
|
||||
}
|
||||
return out, metadata, nil
|
||||
}
|
||||
|
||||
// Ignore multipart validation
|
||||
if m.IgnoreMultipartValidation && strings.Contains(expectedChecksum, "-") {
|
||||
if m.LogMultipartValidationSkipped {
|
||||
// TODO this probably should have more information about the
|
||||
// operation output that won't be validated.
|
||||
logger.Logf(logging.Warn, "Skipped validation of multipart checksum.")
|
||||
}
|
||||
return out, metadata, nil
|
||||
}
|
||||
|
||||
body, err := newValidateChecksumReader(response.Body, algorithmToUse, expectedChecksum)
|
||||
if err != nil {
|
||||
return out, metadata, fmt.Errorf("failed to create checksum validation reader, %w", err)
|
||||
}
|
||||
response.Body = body
|
||||
|
||||
// Update the metadata to include the set of the checksum algorithms that
|
||||
// will be validated.
|
||||
SetOutputValidationAlgorithmsUsed(&metadata, []string{
|
||||
string(algorithmToUse),
|
||||
})
|
||||
|
||||
return out, metadata, nil
|
||||
}
|
||||
+29
@@ -1,3 +1,32 @@
|
||||
# v1.12.12 (2025-01-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.12.11 (2025-01-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.12.10 (2025-01-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v1.12.9 (2025-01-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.12.8 (2025-01-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.12.7 (2024-12-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.12.6 (2024-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.12.5 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -3,4 +3,4 @@
|
||||
package presignedurl
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.12.5"
|
||||
const goModuleVersion = "1.12.12"
|
||||
|
||||
+416
@@ -0,0 +1,416 @@
|
||||
# v1.18.12 (2025-01-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.11 (2025-01-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.10 (2025-01-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
* **Dependency Update**: Upgrade to smithy-go v1.22.2.
|
||||
|
||||
# v1.18.9 (2025-01-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.8 (2025-01-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.7 (2024-12-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.6 (2024-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.5 (2024-11-18)
|
||||
|
||||
* **Dependency Update**: Update to smithy-go v1.22.1.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.4 (2024-11-06)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.3 (2024-10-28)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.2 (2024-10-08)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.1 (2024-10-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.18.0 (2024-10-04)
|
||||
|
||||
* **Feature**: Add support for HTTP client metrics.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.18 (2024-09-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.17 (2024-09-03)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.16 (2024-08-15)
|
||||
|
||||
* **Dependency Update**: Bump minimum Go version to 1.21.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.15 (2024-07-10.2)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.14 (2024-07-10)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.13 (2024-06-28)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.12 (2024-06-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.11 (2024-06-18)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.10 (2024-06-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.9 (2024-06-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.8 (2024-06-03)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.7 (2024-05-16)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.6 (2024-05-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.5 (2024-03-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.4 (2024-03-18)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.3 (2024-03-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.2 (2024-02-23)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.1 (2024-02-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.17.0 (2024-02-13)
|
||||
|
||||
* **Feature**: Bump minimum Go version to 1.20 per our language support policy.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.10 (2024-01-04)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.9 (2023-12-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.8 (2023-12-01)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.7 (2023-11-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.6 (2023-11-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.5 (2023-11-28.2)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.4 (2023-11-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.3 (2023-11-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.2 (2023-11-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.1 (2023-11-01)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.16.0 (2023-10-31)
|
||||
|
||||
* **Feature**: **BREAKING CHANGE**: Bump minimum go version to 1.19 per the revised [go version support policy](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-aligns-with-go-release-policy-on-supported-runtimes/).
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.6 (2023-10-12)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.5 (2023-10-06)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.4 (2023-08-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.3 (2023-08-18)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.2 (2023-08-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.1 (2023-08-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.0 (2023-07-31)
|
||||
|
||||
* **Feature**: Adds support for smithy-modeled endpoint resolution. A new rules-based endpoint resolution will be added to the SDK which will supercede and deprecate existing endpoint resolution. Specifically, EndpointResolver will be deprecated while BaseEndpoint and EndpointResolverV2 will take its place. For more information, please see the Endpoints section in our Developer Guide.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.14.5 (2023-07-28)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.14.4 (2023-07-13)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.14.3 (2023-06-13)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.14.2 (2023-04-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.14.1 (2023-04-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.14.0 (2023-03-21)
|
||||
|
||||
* **Feature**: port v1 sdk 100-continue http header customization for s3 PutObject/UploadPart request and enable user config
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.24 (2023-03-10)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.23 (2023-02-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.22 (2023-02-03)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.21 (2022-12-15)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.20 (2022-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.19 (2022-10-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.18 (2022-10-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.17 (2022-09-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.16 (2022-09-14)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.15 (2022-09-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.14 (2022-08-31)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.13 (2022-08-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.12 (2022-08-11)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.11 (2022-08-09)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.10 (2022-08-08)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.9 (2022-08-01)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.8 (2022-07-05)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.7 (2022-06-29)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.6 (2022-06-07)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.5 (2022-05-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.4 (2022-04-25)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.3 (2022-03-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.2 (2022-03-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.1 (2022-03-23)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.0 (2022-03-08)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.12.0 (2022-02-24)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.11.0 (2022-01-14)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.10.0 (2022-01-07)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.9.2 (2021-12-02)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.9.1 (2021-11-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.9.0 (2021-11-06)
|
||||
|
||||
* **Feature**: The SDK now supports configuration of FIPS and DualStack endpoints using environment variables, shared configuration, or programmatically.
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.8.0 (2021-10-21)
|
||||
|
||||
* **Feature**: Updated to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.7.2 (2021-10-11)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.7.1 (2021-09-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.7.0 (2021-09-02)
|
||||
|
||||
* **Feature**: Add support for S3 Multi-Region Access Point ARNs.
|
||||
|
||||
# v1.6.0 (2021-08-27)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.5.3 (2021-08-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.5.2 (2021-08-04)
|
||||
|
||||
* **Dependency Update**: Updated `github.com/aws/smithy-go` to latest version.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.5.1 (2021-07-15)
|
||||
|
||||
* **Dependency Update**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.5.0 (2021-06-25)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.0 (2021-06-04)
|
||||
|
||||
* **Feature**: The handling of AccessPoint and Outpost ARNs have been updated.
|
||||
|
||||
# v1.3.1 (2021-05-20)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.0 (2021-05-14)
|
||||
|
||||
* **Feature**: Constant has been added to modules to enable runtime version inspection for reporting.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Generated
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
package arn
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws/arn"
|
||||
)
|
||||
|
||||
// AccessPointARN provides representation
|
||||
type AccessPointARN struct {
|
||||
arn.ARN
|
||||
AccessPointName string
|
||||
}
|
||||
|
||||
// GetARN returns the base ARN for the Access Point resource
|
||||
func (a AccessPointARN) GetARN() arn.ARN {
|
||||
return a.ARN
|
||||
}
|
||||
|
||||
// ParseAccessPointResource attempts to parse the ARN's resource as an
|
||||
// AccessPoint resource.
|
||||
//
|
||||
// Supported Access point resource format:
|
||||
// - Access point format: arn:{partition}:s3:{region}:{accountId}:accesspoint/{accesspointName}
|
||||
// - example: arn:aws:s3:us-west-2:012345678901:accesspoint/myaccesspoint
|
||||
func ParseAccessPointResource(a arn.ARN, resParts []string) (AccessPointARN, error) {
|
||||
if isFIPS(a.Region) {
|
||||
return AccessPointARN{}, InvalidARNError{ARN: a, Reason: "FIPS region not allowed in ARN"}
|
||||
}
|
||||
if len(a.AccountID) == 0 {
|
||||
return AccessPointARN{}, InvalidARNError{ARN: a, Reason: "account-id not set"}
|
||||
}
|
||||
if len(resParts) == 0 {
|
||||
return AccessPointARN{}, InvalidARNError{ARN: a, Reason: "resource-id not set"}
|
||||
}
|
||||
if len(resParts) > 1 {
|
||||
return AccessPointARN{}, InvalidARNError{ARN: a, Reason: "sub resource not supported"}
|
||||
}
|
||||
|
||||
resID := resParts[0]
|
||||
if len(strings.TrimSpace(resID)) == 0 {
|
||||
return AccessPointARN{}, InvalidARNError{ARN: a, Reason: "resource-id not set"}
|
||||
}
|
||||
|
||||
return AccessPointARN{
|
||||
ARN: a,
|
||||
AccessPointName: resID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func isFIPS(region string) bool {
|
||||
return strings.HasPrefix(region, "fips-") || strings.HasSuffix(region, "-fips")
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
package arn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws/arn"
|
||||
)
|
||||
|
||||
var supportedServiceARN = []string{
|
||||
"s3",
|
||||
"s3-outposts",
|
||||
"s3-object-lambda",
|
||||
}
|
||||
|
||||
func isSupportedServiceARN(service string) bool {
|
||||
for _, name := range supportedServiceARN {
|
||||
if name == service {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Resource provides the interfaces abstracting ARNs of specific resource
|
||||
// types.
|
||||
type Resource interface {
|
||||
GetARN() arn.ARN
|
||||
String() string
|
||||
}
|
||||
|
||||
// ResourceParser provides the function for parsing an ARN's resource
|
||||
// component into a typed resource.
|
||||
type ResourceParser func(arn.ARN) (Resource, error)
|
||||
|
||||
// ParseResource parses an AWS ARN into a typed resource for the S3 API.
|
||||
func ParseResource(a arn.ARN, resParser ResourceParser) (resARN Resource, err error) {
|
||||
if len(a.Partition) == 0 {
|
||||
return nil, InvalidARNError{ARN: a, Reason: "partition not set"}
|
||||
}
|
||||
|
||||
if !isSupportedServiceARN(a.Service) {
|
||||
return nil, InvalidARNError{ARN: a, Reason: "service is not supported"}
|
||||
}
|
||||
|
||||
if len(a.Resource) == 0 {
|
||||
return nil, InvalidARNError{ARN: a, Reason: "resource not set"}
|
||||
}
|
||||
|
||||
return resParser(a)
|
||||
}
|
||||
|
||||
// SplitResource splits the resource components by the ARN resource delimiters.
|
||||
func SplitResource(v string) []string {
|
||||
var parts []string
|
||||
var offset int
|
||||
|
||||
for offset <= len(v) {
|
||||
idx := strings.IndexAny(v[offset:], "/:")
|
||||
if idx < 0 {
|
||||
parts = append(parts, v[offset:])
|
||||
break
|
||||
}
|
||||
parts = append(parts, v[offset:idx+offset])
|
||||
offset += idx + 1
|
||||
}
|
||||
|
||||
return parts
|
||||
}
|
||||
|
||||
// IsARN returns whether the given string is an ARN
|
||||
func IsARN(s string) bool {
|
||||
return arn.IsARN(s)
|
||||
}
|
||||
|
||||
// InvalidARNError provides the error for an invalid ARN error.
|
||||
type InvalidARNError struct {
|
||||
ARN arn.ARN
|
||||
Reason string
|
||||
}
|
||||
|
||||
// Error returns a string denoting the occurred InvalidARNError
|
||||
func (e InvalidARNError) Error() string {
|
||||
return fmt.Sprintf("invalid Amazon %s ARN, %s, %s", e.ARN.Service, e.Reason, e.ARN.String())
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package arn
|
||||
|
||||
import "fmt"
|
||||
|
||||
// arnable is implemented by the relevant S3/S3Control
|
||||
// operations which have members that may need ARN
|
||||
// processing.
|
||||
type arnable interface {
|
||||
SetARNMember(string) error
|
||||
GetARNMember() (*string, bool)
|
||||
}
|
||||
|
||||
// GetARNField would be called during middleware execution
|
||||
// to retrieve a member value that is an ARN in need of
|
||||
// processing.
|
||||
func GetARNField(input interface{}) (*string, bool) {
|
||||
v, ok := input.(arnable)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return v.GetARNMember()
|
||||
}
|
||||
|
||||
// SetARNField would called during middleware exeuction
|
||||
// to set a member value that required ARN processing.
|
||||
func SetARNField(input interface{}, v string) error {
|
||||
params, ok := input.(arnable)
|
||||
if !ok {
|
||||
return fmt.Errorf("Params does not contain arn field member")
|
||||
}
|
||||
return params.SetARNMember(v)
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
package arn
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws/arn"
|
||||
)
|
||||
|
||||
// OutpostARN interface that should be satisfied by outpost ARNs
|
||||
type OutpostARN interface {
|
||||
Resource
|
||||
GetOutpostID() string
|
||||
}
|
||||
|
||||
// ParseOutpostARNResource will parse a provided ARNs resource using the appropriate ARN format
|
||||
// and return a specific OutpostARN type
|
||||
//
|
||||
// Currently supported outpost ARN formats:
|
||||
// * Outpost AccessPoint ARN format:
|
||||
// - ARN format: arn:{partition}:s3-outposts:{region}:{accountId}:outpost/{outpostId}/accesspoint/{accesspointName}
|
||||
// - example: arn:aws:s3-outposts:us-west-2:012345678901:outpost/op-1234567890123456/accesspoint/myaccesspoint
|
||||
//
|
||||
// * Outpost Bucket ARN format:
|
||||
// - ARN format: arn:{partition}:s3-outposts:{region}:{accountId}:outpost/{outpostId}/bucket/{bucketName}
|
||||
// - example: arn:aws:s3-outposts:us-west-2:012345678901:outpost/op-1234567890123456/bucket/mybucket
|
||||
//
|
||||
// Other outpost ARN formats may be supported and added in the future.
|
||||
func ParseOutpostARNResource(a arn.ARN, resParts []string) (OutpostARN, error) {
|
||||
if len(a.Region) == 0 {
|
||||
return nil, InvalidARNError{ARN: a, Reason: "region not set"}
|
||||
}
|
||||
|
||||
if isFIPS(a.Region) {
|
||||
return nil, InvalidARNError{ARN: a, Reason: "FIPS region not allowed in ARN"}
|
||||
}
|
||||
|
||||
if len(a.AccountID) == 0 {
|
||||
return nil, InvalidARNError{ARN: a, Reason: "account-id not set"}
|
||||
}
|
||||
|
||||
// verify if outpost id is present and valid
|
||||
if len(resParts) == 0 || len(strings.TrimSpace(resParts[0])) == 0 {
|
||||
return nil, InvalidARNError{ARN: a, Reason: "outpost resource-id not set"}
|
||||
}
|
||||
|
||||
// verify possible resource type exists
|
||||
if len(resParts) < 3 {
|
||||
return nil, InvalidARNError{
|
||||
ARN: a, Reason: "incomplete outpost resource type. Expected bucket or access-point resource to be present",
|
||||
}
|
||||
}
|
||||
|
||||
// Since we know this is a OutpostARN fetch outpostID
|
||||
outpostID := strings.TrimSpace(resParts[0])
|
||||
|
||||
switch resParts[1] {
|
||||
case "accesspoint":
|
||||
accesspointARN, err := ParseAccessPointResource(a, resParts[2:])
|
||||
if err != nil {
|
||||
return OutpostAccessPointARN{}, err
|
||||
}
|
||||
return OutpostAccessPointARN{
|
||||
AccessPointARN: accesspointARN,
|
||||
OutpostID: outpostID,
|
||||
}, nil
|
||||
|
||||
case "bucket":
|
||||
bucketName, err := parseBucketResource(a, resParts[2:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return OutpostBucketARN{
|
||||
ARN: a,
|
||||
BucketName: bucketName,
|
||||
OutpostID: outpostID,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return nil, InvalidARNError{ARN: a, Reason: "unknown resource set for outpost ARN"}
|
||||
}
|
||||
}
|
||||
|
||||
// OutpostAccessPointARN represents outpost access point ARN.
|
||||
type OutpostAccessPointARN struct {
|
||||
AccessPointARN
|
||||
OutpostID string
|
||||
}
|
||||
|
||||
// GetOutpostID returns the outpost id of outpost access point arn
|
||||
func (o OutpostAccessPointARN) GetOutpostID() string {
|
||||
return o.OutpostID
|
||||
}
|
||||
|
||||
// OutpostBucketARN represents the outpost bucket ARN.
|
||||
type OutpostBucketARN struct {
|
||||
arn.ARN
|
||||
BucketName string
|
||||
OutpostID string
|
||||
}
|
||||
|
||||
// GetOutpostID returns the outpost id of outpost bucket arn
|
||||
func (o OutpostBucketARN) GetOutpostID() string {
|
||||
return o.OutpostID
|
||||
}
|
||||
|
||||
// GetARN retrives the base ARN from outpost bucket ARN resource
|
||||
func (o OutpostBucketARN) GetARN() arn.ARN {
|
||||
return o.ARN
|
||||
}
|
||||
|
||||
// parseBucketResource attempts to parse the ARN's bucket resource and retrieve the
|
||||
// bucket resource id.
|
||||
//
|
||||
// parseBucketResource only parses the bucket resource id.
|
||||
func parseBucketResource(a arn.ARN, resParts []string) (bucketName string, err error) {
|
||||
if len(resParts) == 0 {
|
||||
return bucketName, InvalidARNError{ARN: a, Reason: "bucket resource-id not set"}
|
||||
}
|
||||
if len(resParts) > 1 {
|
||||
return bucketName, InvalidARNError{ARN: a, Reason: "sub resource not supported"}
|
||||
}
|
||||
|
||||
bucketName = strings.TrimSpace(resParts[0])
|
||||
if len(bucketName) == 0 {
|
||||
return bucketName, InvalidARNError{ARN: a, Reason: "bucket resource-id not set"}
|
||||
}
|
||||
return bucketName, err
|
||||
}
|
||||
Generated
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
package arn
|
||||
|
||||
// S3ObjectLambdaARN represents an ARN for the s3-object-lambda service
|
||||
type S3ObjectLambdaARN interface {
|
||||
Resource
|
||||
|
||||
isS3ObjectLambdasARN()
|
||||
}
|
||||
|
||||
// S3ObjectLambdaAccessPointARN is an S3ObjectLambdaARN for the Access Point resource type
|
||||
type S3ObjectLambdaAccessPointARN struct {
|
||||
AccessPointARN
|
||||
}
|
||||
|
||||
func (s S3ObjectLambdaAccessPointARN) isS3ObjectLambdasARN() {}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package s3shared
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws/arn"
|
||||
)
|
||||
|
||||
// ARNLookup is the initial middleware that looks up if an arn is provided.
|
||||
// This middleware is responsible for fetching ARN from a arnable field, and registering the ARN on
|
||||
// middleware context. This middleware must be executed before input validation step or any other
|
||||
// arn processing middleware.
|
||||
type ARNLookup struct {
|
||||
|
||||
// GetARNValue takes in a input interface and returns a ptr to string and a bool
|
||||
GetARNValue func(interface{}) (*string, bool)
|
||||
}
|
||||
|
||||
// ID for the middleware
|
||||
func (m *ARNLookup) ID() string {
|
||||
return "S3Shared:ARNLookup"
|
||||
}
|
||||
|
||||
// HandleInitialize handles the behavior of this initialize step
|
||||
func (m *ARNLookup) HandleInitialize(ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) (
|
||||
out middleware.InitializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
// check if GetARNValue is supported
|
||||
if m.GetARNValue == nil {
|
||||
return next.HandleInitialize(ctx, in)
|
||||
}
|
||||
|
||||
// check is input resource is an ARN; if not go to next
|
||||
v, ok := m.GetARNValue(in.Parameters)
|
||||
if !ok || v == nil || !arn.IsARN(*v) {
|
||||
return next.HandleInitialize(ctx, in)
|
||||
}
|
||||
|
||||
// if ARN process ResourceRequest and put it on ctx
|
||||
av, err := arn.Parse(*v)
|
||||
if err != nil {
|
||||
return out, metadata, fmt.Errorf("error parsing arn: %w", err)
|
||||
}
|
||||
// set parsed arn on context
|
||||
ctx = setARNResourceOnContext(ctx, av)
|
||||
|
||||
return next.HandleInitialize(ctx, in)
|
||||
}
|
||||
|
||||
// arnResourceKey is the key set on context used to identify, retrive an ARN resource
|
||||
// if present on the context.
|
||||
type arnResourceKey struct{}
|
||||
|
||||
// SetARNResourceOnContext sets the S3 ARN on the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func setARNResourceOnContext(ctx context.Context, value arn.ARN) context.Context {
|
||||
return middleware.WithStackValue(ctx, arnResourceKey{}, value)
|
||||
}
|
||||
|
||||
// GetARNResourceFromContext returns an ARN from context and a bool indicating
|
||||
// presence of ARN on ctx.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func GetARNResourceFromContext(ctx context.Context) (arn.ARN, bool) {
|
||||
v, ok := middleware.GetStackValue(ctx, arnResourceKey{}).(arn.ARN)
|
||||
return v, ok
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package config
|
||||
|
||||
import "context"
|
||||
|
||||
// UseARNRegionProvider is an interface for retrieving external configuration value for UseARNRegion
|
||||
type UseARNRegionProvider interface {
|
||||
GetS3UseARNRegion(ctx context.Context) (value bool, found bool, err error)
|
||||
}
|
||||
|
||||
// DisableMultiRegionAccessPointsProvider is an interface for retrieving external configuration value for DisableMultiRegionAccessPoints
|
||||
type DisableMultiRegionAccessPointsProvider interface {
|
||||
GetS3DisableMultiRegionAccessPoints(ctx context.Context) (value bool, found bool, err error)
|
||||
}
|
||||
|
||||
// ResolveUseARNRegion extracts the first instance of a UseARNRegion from the config slice.
|
||||
// Additionally returns a boolean to indicate if the value was found in provided configs, and error if one is encountered.
|
||||
func ResolveUseARNRegion(ctx context.Context, configs []interface{}) (value bool, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if p, ok := cfg.(UseARNRegionProvider); ok {
|
||||
value, found, err = p.GetS3UseARNRegion(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ResolveDisableMultiRegionAccessPoints extracts the first instance of a DisableMultiRegionAccessPoints from the config slice.
|
||||
// Additionally returns a boolean to indicate if the value was found in provided configs, and error if one is encountered.
|
||||
func ResolveDisableMultiRegionAccessPoints(ctx context.Context, configs []interface{}) (value bool, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if p, ok := cfg.(DisableMultiRegionAccessPointsProvider); ok {
|
||||
value, found, err = p.GetS3DisableMultiRegionAccessPoints(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
package s3shared
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/internal/s3shared/arn"
|
||||
)
|
||||
|
||||
// TODO: fix these error statements to be relevant to v2 sdk
|
||||
|
||||
const (
|
||||
invalidARNErrorErrCode = "InvalidARNError"
|
||||
configurationErrorErrCode = "ConfigurationError"
|
||||
)
|
||||
|
||||
// InvalidARNError denotes the error for Invalid ARN
|
||||
type InvalidARNError struct {
|
||||
message string
|
||||
resource arn.Resource
|
||||
origErr error
|
||||
}
|
||||
|
||||
// Error returns the InvalidARN error string
|
||||
func (e InvalidARNError) Error() string {
|
||||
var extra string
|
||||
if e.resource != nil {
|
||||
extra = "ARN: " + e.resource.String()
|
||||
}
|
||||
msg := invalidARNErrorErrCode + " : " + e.message
|
||||
if extra != "" {
|
||||
msg = msg + "\n\t" + extra
|
||||
}
|
||||
|
||||
return msg
|
||||
}
|
||||
|
||||
// OrigErr is the original error wrapped by Invalid ARN Error
|
||||
func (e InvalidARNError) Unwrap() error {
|
||||
return e.origErr
|
||||
}
|
||||
|
||||
// NewInvalidARNError denotes invalid arn error
|
||||
func NewInvalidARNError(resource arn.Resource, err error) InvalidARNError {
|
||||
return InvalidARNError{
|
||||
message: "invalid ARN",
|
||||
origErr: err,
|
||||
resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
// NewInvalidARNWithUnsupportedPartitionError ARN not supported for the target partition
|
||||
func NewInvalidARNWithUnsupportedPartitionError(resource arn.Resource, err error) InvalidARNError {
|
||||
return InvalidARNError{
|
||||
message: "resource ARN not supported for the target ARN partition",
|
||||
origErr: err,
|
||||
resource: resource,
|
||||
}
|
||||
}
|
||||
|
||||
// NewInvalidARNWithFIPSError ARN not supported for FIPS region
|
||||
//
|
||||
// Deprecated: FIPS will not appear in the ARN region component.
|
||||
func NewInvalidARNWithFIPSError(resource arn.Resource, err error) InvalidARNError {
|
||||
return InvalidARNError{
|
||||
message: "resource ARN not supported for FIPS region",
|
||||
resource: resource,
|
||||
origErr: err,
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigurationError is used to denote a client configuration error
|
||||
type ConfigurationError struct {
|
||||
message string
|
||||
resource arn.Resource
|
||||
clientPartitionID string
|
||||
clientRegion string
|
||||
origErr error
|
||||
}
|
||||
|
||||
// Error returns the Configuration error string
|
||||
func (e ConfigurationError) Error() string {
|
||||
extra := fmt.Sprintf("ARN: %s, client partition: %s, client region: %s",
|
||||
e.resource, e.clientPartitionID, e.clientRegion)
|
||||
|
||||
msg := configurationErrorErrCode + " : " + e.message
|
||||
if extra != "" {
|
||||
msg = msg + "\n\t" + extra
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// OrigErr is the original error wrapped by Configuration Error
|
||||
func (e ConfigurationError) Unwrap() error {
|
||||
return e.origErr
|
||||
}
|
||||
|
||||
// NewClientPartitionMismatchError stub
|
||||
func NewClientPartitionMismatchError(resource arn.Resource, clientPartitionID, clientRegion string, err error) ConfigurationError {
|
||||
return ConfigurationError{
|
||||
message: "client partition does not match provided ARN partition",
|
||||
origErr: err,
|
||||
resource: resource,
|
||||
clientPartitionID: clientPartitionID,
|
||||
clientRegion: clientRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// NewClientRegionMismatchError denotes cross region access error
|
||||
func NewClientRegionMismatchError(resource arn.Resource, clientPartitionID, clientRegion string, err error) ConfigurationError {
|
||||
return ConfigurationError{
|
||||
message: "client region does not match provided ARN region",
|
||||
origErr: err,
|
||||
resource: resource,
|
||||
clientPartitionID: clientPartitionID,
|
||||
clientRegion: clientRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFailedToResolveEndpointError denotes endpoint resolving error
|
||||
func NewFailedToResolveEndpointError(resource arn.Resource, clientPartitionID, clientRegion string, err error) ConfigurationError {
|
||||
return ConfigurationError{
|
||||
message: "endpoint resolver failed to find an endpoint for the provided ARN region",
|
||||
origErr: err,
|
||||
resource: resource,
|
||||
clientPartitionID: clientPartitionID,
|
||||
clientRegion: clientRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// NewClientConfiguredForFIPSError denotes client config error for unsupported cross region FIPS access
|
||||
func NewClientConfiguredForFIPSError(resource arn.Resource, clientPartitionID, clientRegion string, err error) ConfigurationError {
|
||||
return ConfigurationError{
|
||||
message: "client configured for fips but cross-region resource ARN provided",
|
||||
origErr: err,
|
||||
resource: resource,
|
||||
clientPartitionID: clientPartitionID,
|
||||
clientRegion: clientRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFIPSConfigurationError denotes a configuration error when a client or request is configured for FIPS
|
||||
func NewFIPSConfigurationError(resource arn.Resource, clientPartitionID, clientRegion string, err error) ConfigurationError {
|
||||
return ConfigurationError{
|
||||
message: "use of ARN is not supported when client or request is configured for FIPS",
|
||||
origErr: err,
|
||||
resource: resource,
|
||||
clientPartitionID: clientPartitionID,
|
||||
clientRegion: clientRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// NewClientConfiguredForAccelerateError denotes client config error for unsupported S3 accelerate
|
||||
func NewClientConfiguredForAccelerateError(resource arn.Resource, clientPartitionID, clientRegion string, err error) ConfigurationError {
|
||||
return ConfigurationError{
|
||||
message: "client configured for S3 Accelerate but is not supported with resource ARN",
|
||||
origErr: err,
|
||||
resource: resource,
|
||||
clientPartitionID: clientPartitionID,
|
||||
clientRegion: clientRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// NewClientConfiguredForCrossRegionFIPSError denotes client config error for unsupported cross region FIPS request
|
||||
func NewClientConfiguredForCrossRegionFIPSError(resource arn.Resource, clientPartitionID, clientRegion string, err error) ConfigurationError {
|
||||
return ConfigurationError{
|
||||
message: "client configured for FIPS with cross-region enabled but is supported with cross-region resource ARN",
|
||||
origErr: err,
|
||||
resource: resource,
|
||||
clientPartitionID: clientPartitionID,
|
||||
clientRegion: clientRegion,
|
||||
}
|
||||
}
|
||||
|
||||
// NewClientConfiguredForDualStackError denotes client config error for unsupported S3 Dual-stack
|
||||
func NewClientConfiguredForDualStackError(resource arn.Resource, clientPartitionID, clientRegion string, err error) ConfigurationError {
|
||||
return ConfigurationError{
|
||||
message: "client configured for S3 Dual-stack but is not supported with resource ARN",
|
||||
origErr: err,
|
||||
resource: resource,
|
||||
clientPartitionID: clientPartitionID,
|
||||
clientRegion: clientRegion,
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT.
|
||||
|
||||
package s3shared
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.18.12"
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package s3shared
|
||||
|
||||
import (
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
)
|
||||
|
||||
// hostID is used to retrieve host id from response metadata
|
||||
type hostID struct {
|
||||
}
|
||||
|
||||
// SetHostIDMetadata sets the provided host id over middleware metadata
|
||||
func SetHostIDMetadata(metadata *middleware.Metadata, id string) {
|
||||
metadata.Set(hostID{}, id)
|
||||
}
|
||||
|
||||
// GetHostIDMetadata retrieves the host id from middleware metadata
|
||||
// returns host id as string along with a boolean indicating presence of
|
||||
// hostId on middleware metadata.
|
||||
func GetHostIDMetadata(metadata middleware.Metadata) (string, bool) {
|
||||
if !metadata.Has(hostID{}) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
v, ok := metadata.Get(hostID{}).(string)
|
||||
if !ok {
|
||||
return "", true
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package s3shared
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
)
|
||||
|
||||
// clonedInputKey used to denote if request input was cloned.
|
||||
type clonedInputKey struct{}
|
||||
|
||||
// SetClonedInputKey sets a key on context to denote input was cloned previously.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func SetClonedInputKey(ctx context.Context, value bool) context.Context {
|
||||
return middleware.WithStackValue(ctx, clonedInputKey{}, value)
|
||||
}
|
||||
|
||||
// IsClonedInput retrieves if context key for cloned input was set.
|
||||
// If set, we can infer that the reuqest input was cloned previously.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func IsClonedInput(ctx context.Context) bool {
|
||||
v, _ := middleware.GetStackValue(ctx, clonedInputKey{}).(bool)
|
||||
return v
|
||||
}
|
||||
Generated
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
package s3shared
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
"github.com/aws/smithy-go/tracing"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
const metadataRetrieverID = "S3MetadataRetriever"
|
||||
|
||||
// AddMetadataRetrieverMiddleware adds request id, host id retriever middleware
|
||||
func AddMetadataRetrieverMiddleware(stack *middleware.Stack) error {
|
||||
// add metadata retriever middleware before operation deserializers so that it can retrieve metadata such as
|
||||
// host id, request id from response header returned by operation deserializers
|
||||
return stack.Deserialize.Insert(&metadataRetriever{}, "OperationDeserializer", middleware.Before)
|
||||
}
|
||||
|
||||
type metadataRetriever struct {
|
||||
}
|
||||
|
||||
// ID returns the middleware identifier
|
||||
func (m *metadataRetriever) ID() string {
|
||||
return metadataRetrieverID
|
||||
}
|
||||
|
||||
func (m *metadataRetriever) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) (
|
||||
out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
out, metadata, err = next.HandleDeserialize(ctx, in)
|
||||
|
||||
span, _ := tracing.GetSpan(ctx)
|
||||
|
||||
resp, ok := out.RawResponse.(*smithyhttp.Response)
|
||||
if !ok {
|
||||
// No raw response to wrap with.
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
// check for header for Request id
|
||||
if v := resp.Header.Get("X-Amz-Request-Id"); len(v) != 0 {
|
||||
// set reqID on metadata for successful responses.
|
||||
awsmiddleware.SetRequestIDMetadata(&metadata, v)
|
||||
span.SetProperty("aws.request_id", v)
|
||||
}
|
||||
|
||||
// look up host-id
|
||||
if v := resp.Header.Get("X-Amz-Id-2"); len(v) != 0 {
|
||||
// set reqID on metadata for successful responses.
|
||||
SetHostIDMetadata(&metadata, v)
|
||||
span.SetProperty("aws.extended_request_id", v)
|
||||
}
|
||||
|
||||
return out, metadata, err
|
||||
}
|
||||
Generated
Vendored
+77
@@ -0,0 +1,77 @@
|
||||
package s3shared
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
awsarn "github.com/aws/aws-sdk-go-v2/aws/arn"
|
||||
"github.com/aws/aws-sdk-go-v2/service/internal/s3shared/arn"
|
||||
)
|
||||
|
||||
// ResourceRequest represents an ARN resource and api request metadata
|
||||
type ResourceRequest struct {
|
||||
Resource arn.Resource
|
||||
// RequestRegion is the region configured on the request config
|
||||
RequestRegion string
|
||||
|
||||
// SigningRegion is the signing region resolved for the request
|
||||
SigningRegion string
|
||||
|
||||
// PartitionID is the resolved partition id for the provided request region
|
||||
PartitionID string
|
||||
|
||||
// UseARNRegion indicates if client should use the region provided in an ARN resource
|
||||
UseARNRegion bool
|
||||
|
||||
// UseFIPS indicates if te client is configured for FIPS
|
||||
UseFIPS bool
|
||||
}
|
||||
|
||||
// ARN returns the resource ARN
|
||||
func (r ResourceRequest) ARN() awsarn.ARN {
|
||||
return r.Resource.GetARN()
|
||||
}
|
||||
|
||||
// ResourceConfiguredForFIPS returns true if resource ARNs region is FIPS
|
||||
//
|
||||
// Deprecated: FIPS will not be present in the ARN region
|
||||
func (r ResourceRequest) ResourceConfiguredForFIPS() bool {
|
||||
return IsFIPS(r.ARN().Region)
|
||||
}
|
||||
|
||||
// AllowCrossRegion returns a bool value to denote if S3UseARNRegion flag is set
|
||||
func (r ResourceRequest) AllowCrossRegion() bool {
|
||||
return r.UseARNRegion
|
||||
}
|
||||
|
||||
// IsCrossPartition returns true if request is configured for region of another partition, than
|
||||
// the partition that resource ARN region resolves to. IsCrossPartition will not return an error,
|
||||
// if request is not configured with a specific partition id. This might happen if customer provides
|
||||
// custom endpoint url, but does not associate a partition id with it.
|
||||
func (r ResourceRequest) IsCrossPartition() (bool, error) {
|
||||
rv := r.PartitionID
|
||||
if len(rv) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
av := r.Resource.GetARN().Partition
|
||||
if len(av) == 0 {
|
||||
return false, fmt.Errorf("no partition id for provided ARN")
|
||||
}
|
||||
|
||||
return !strings.EqualFold(rv, av), nil
|
||||
}
|
||||
|
||||
// IsCrossRegion returns true if request signing region is not same as arn region
|
||||
func (r ResourceRequest) IsCrossRegion() bool {
|
||||
v := r.SigningRegion
|
||||
return !strings.EqualFold(v, r.Resource.GetARN().Region)
|
||||
}
|
||||
|
||||
// IsFIPS returns true if region is a fips pseudo-region
|
||||
//
|
||||
// Deprecated: FIPS should be specified via EndpointOptions.
|
||||
func IsFIPS(region string) bool {
|
||||
return strings.HasPrefix(region, "fips-") ||
|
||||
strings.HasSuffix(region, "-fips")
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user