]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/aws/aws-sdk-go/aws/signer/v4/v4.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / aws / aws-sdk-go / aws / signer / v4 / v4.go
index 434ac872dee4af38bdc5d341868609d5edd79885..523db79f8d2acd018359c66615070684f7bb2244 100644 (file)
@@ -45,7 +45,7 @@
 // If signing a request intended for HTTP2 server, and you're using Go 1.6.2
 // through 1.7.4 you should use the URL.RawPath as the pre-escaped form of the
 // request URL. https://github.com/golang/go/issues/16847 points to a bug in
-// Go pre 1.8 that failes to make HTTP2 requests using absolute URL in the HTTP
+// Go pre 1.8 that fails to make HTTP2 requests using absolute URL in the HTTP
 // message. URL.Opaque generally will force Go to make requests with absolute URL.
 // URL.RawPath does not do this, but RawPath must be a valid escaping of Path
 // or url.EscapedPath will ignore the RawPath escaping.
@@ -55,7 +55,6 @@
 package v4
 
 import (
-       "bytes"
        "crypto/hmac"
        "crypto/sha256"
        "encoding/hex"
@@ -72,6 +71,7 @@ import (
        "github.com/aws/aws-sdk-go/aws"
        "github.com/aws/aws-sdk-go/aws/credentials"
        "github.com/aws/aws-sdk-go/aws/request"
+       "github.com/aws/aws-sdk-go/internal/sdkio"
        "github.com/aws/aws-sdk-go/private/protocol/rest"
 )
 
@@ -98,25 +98,25 @@ var ignoredHeaders = rules{
 var requiredSignedHeaders = rules{
        whitelist{
                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{}{},
+                       "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{}{},
@@ -134,7 +134,9 @@ var requiredSignedHeaders = rules{
                        "X-Amz-Server-Side-Encryption-Customer-Key":                   struct{}{},
                        "X-Amz-Server-Side-Encryption-Customer-Key-Md5":               struct{}{},
                        "X-Amz-Storage-Class":                                         struct{}{},
+                       "X-Amz-Tagging":                                               struct{}{},
                        "X-Amz-Website-Redirect-Location":                             struct{}{},
+                       "X-Amz-Content-Sha256":                                        struct{}{},
                },
        },
        patterns{"X-Amz-Meta-"},
@@ -180,7 +182,7 @@ type Signer struct {
        // http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
        DisableURIPathEscaping bool
 
-       // Disales the automatical setting of the HTTP request's Body field with the
+       // Disables the automatical setting of the HTTP request's Body field with the
        // io.ReadSeeker passed in to the signer. This is useful if you're using a
        // custom wrapper around the body for the io.ReadSeeker and want to preserve
        // the Body value on the Request.Body.
@@ -269,7 +271,7 @@ type signingCtx struct {
 // "X-Amz-Content-Sha256" header with a precomputed value. The signer will
 // only compute the hash if the request header value is empty.
 func (v4 Signer) Sign(r *http.Request, body io.ReadSeeker, service, region string, signTime time.Time) (http.Header, error) {
-       return v4.signWithBody(r, body, service, region, 0, signTime)
+       return v4.signWithBody(r, body, service, region, 0, false, signTime)
 }
 
 // Presign signs AWS v4 requests with the provided body, service name, region
@@ -303,10 +305,10 @@ func (v4 Signer) Sign(r *http.Request, body io.ReadSeeker, service, region strin
 // presigned request's signature you can set the "X-Amz-Content-Sha256"
 // HTTP header and that will be included in the request's signature.
 func (v4 Signer) Presign(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) {
-       return v4.signWithBody(r, body, service, region, exp, signTime)
+       return v4.signWithBody(r, body, service, region, exp, true, signTime)
 }
 
-func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) {
+func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, isPresign bool, signTime time.Time) (http.Header, error) {
        currentTimeFn := v4.currentTimeFn
        if currentTimeFn == nil {
                currentTimeFn = time.Now
@@ -318,7 +320,7 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
                Query:                  r.URL.Query(),
                Time:                   signTime,
                ExpireTime:             exp,
-               isPresign:              exp != 0,
+               isPresign:              isPresign,
                ServiceName:            service,
                Region:                 region,
                DisableURIPathEscaping: v4.DisableURIPathEscaping,
@@ -340,8 +342,11 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
                return http.Header{}, err
        }
 
+       ctx.sanitizeHostForHeader()
        ctx.assignAmzQueryValues()
-       ctx.build(v4.DisableHeaderHoisting)
+       if err := ctx.build(v4.DisableHeaderHoisting); err != nil {
+               return nil, err
+       }
 
        // If the request is not presigned the body should be attached to it. This
        // prevents the confusion of wanting to send a signed request without
@@ -364,6 +369,10 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
        return ctx.SignedHeaderVals, nil
 }
 
+func (ctx *signingCtx) sanitizeHostForHeader() {
+       request.SanitizeHostForHeader(ctx.Request)
+}
+
 func (ctx *signingCtx) handlePresignRemoval() {
        if !ctx.isPresign {
                return
@@ -402,7 +411,7 @@ var SignRequestHandler = request.NamedHandler{
 }
 
 // SignSDKRequest signs an AWS request with the V4 signature. This
-// request handler is bested used only with the SDK's built in service client's
+// request handler should only be used with the SDK's built in service client's
 // API operation requests.
 //
 // This function should not be used on its on its own, but in conjunction with
@@ -413,7 +422,7 @@ var SignRequestHandler = request.NamedHandler{
 // If the credentials of the request's config are set to
 // credentials.AnonymousCredentials the request will not be signed.
 func SignSDKRequest(req *request.Request) {
-       signSDKRequestWithCurrTime(req, time.Now)
+       SignSDKRequestWithCurrentTime(req, time.Now)
 }
 
 // BuildNamedHandler will build a generic handler for signing.
@@ -421,12 +430,15 @@ func BuildNamedHandler(name string, opts ...func(*Signer)) request.NamedHandler
        return request.NamedHandler{
                Name: name,
                Fn: func(req *request.Request) {
-                       signSDKRequestWithCurrTime(req, time.Now, opts...)
+                       SignSDKRequestWithCurrentTime(req, time.Now, opts...)
                },
        }
 }
 
-func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer)) {
+// SignSDKRequestWithCurrentTime will sign the SDK's request using the time
+// function passed in. Behaves the same as SignSDKRequest with the exception
+// the request is signed with the value returned by the current time function.
+func SignSDKRequestWithCurrentTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer)) {
        // If the request does not need to be signed ignore the signing of the
        // request if the AnonymousCredentials object is used.
        if req.Config.Credentials == credentials.AnonymousCredentials {
@@ -462,13 +474,9 @@ func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time
                opt(v4)
        }
 
-       signingTime := req.Time
-       if !req.LastSignedAt.IsZero() {
-               signingTime = req.LastSignedAt
-       }
-
+       curTime := curTimeFn()
        signedHeaders, err := v4.signWithBody(req.HTTPRequest, req.GetBody(),
-               name, region, req.ExpireTime, signingTime,
+               name, region, req.ExpireTime, req.ExpireTime > 0, curTime,
        )
        if err != nil {
                req.Error = err
@@ -477,7 +485,7 @@ func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time
        }
 
        req.SignedHeaderVals = signedHeaders
-       req.LastSignedAt = curTimeFn()
+       req.LastSignedAt = curTime
 }
 
 const logSignInfoMsg = `DEBUG: Request Signature:
@@ -499,10 +507,14 @@ func (v4 *Signer) logSigningInfo(ctx *signingCtx) {
        v4.Logger.Log(msg)
 }
 
-func (ctx *signingCtx) build(disableHeaderHoisting bool) {
+func (ctx *signingCtx) build(disableHeaderHoisting bool) error {
        ctx.buildTime()             // no depends
        ctx.buildCredentialString() // no depends
 
+       if err := ctx.buildBodyDigest(); err != nil {
+               return err
+       }
+
        unsignedHeaders := ctx.Request.Header
        if ctx.isPresign {
                if !disableHeaderHoisting {
@@ -514,7 +526,6 @@ func (ctx *signingCtx) build(disableHeaderHoisting bool) {
                }
        }
 
-       ctx.buildBodyDigest()
        ctx.buildCanonicalHeaders(ignoredHeaders, unsignedHeaders)
        ctx.buildCanonicalString() // depends on canon headers / signed headers
        ctx.buildStringToSign()    // depends on canon string
@@ -530,6 +541,8 @@ func (ctx *signingCtx) build(disableHeaderHoisting bool) {
                }
                ctx.Request.Header.Set("Authorization", strings.Join(parts, ", "))
        }
+
+       return nil
 }
 
 func (ctx *signingCtx) buildTime() {
@@ -604,14 +617,18 @@ func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) {
        headerValues := make([]string, len(headers))
        for i, k := range headers {
                if k == "host" {
-                       headerValues[i] = "host:" + ctx.Request.URL.Host
+                       if ctx.Request.Host != "" {
+                               headerValues[i] = "host:" + ctx.Request.Host
+                       } else {
+                               headerValues[i] = "host:" + ctx.Request.URL.Host
+                       }
                } else {
                        headerValues[i] = k + ":" +
                                strings.Join(ctx.SignedHeaderVals[k], ",")
                }
        }
-
-       ctx.canonicalHeaders = strings.Join(stripExcessSpaces(headerValues), "\n")
+       stripExcessSpaces(headerValues)
+       ctx.canonicalHeaders = strings.Join(headerValues, "\n")
 }
 
 func (ctx *signingCtx) buildCanonicalString() {
@@ -652,21 +669,34 @@ func (ctx *signingCtx) buildSignature() {
        ctx.signature = hex.EncodeToString(signature)
 }
 
-func (ctx *signingCtx) buildBodyDigest() {
+func (ctx *signingCtx) buildBodyDigest() error {
        hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
        if hash == "" {
-               if ctx.unsignedPayload || (ctx.isPresign && ctx.ServiceName == "s3") {
+               includeSHA256Header := ctx.unsignedPayload ||
+                       ctx.ServiceName == "s3" ||
+                       ctx.ServiceName == "glacier"
+
+               s3Presign := ctx.isPresign && ctx.ServiceName == "s3"
+
+               if ctx.unsignedPayload || s3Presign {
                        hash = "UNSIGNED-PAYLOAD"
+                       includeSHA256Header = !s3Presign
                } else if ctx.Body == nil {
                        hash = emptyStringSHA256
                } else {
+                       if !aws.IsReaderSeekable(ctx.Body) {
+                               return fmt.Errorf("cannot use unseekable request body %T, for signed request with body", ctx.Body)
+                       }
                        hash = hex.EncodeToString(makeSha256Reader(ctx.Body))
                }
-               if ctx.unsignedPayload || ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" {
+
+               if includeSHA256Header {
                        ctx.Request.Header.Set("X-Amz-Content-Sha256", hash)
                }
        }
        ctx.bodyDigest = hash
+
+       return nil
 }
 
 // isRequestSigned returns if the request is currently signed or presigned
@@ -706,56 +736,61 @@ func makeSha256(data []byte) []byte {
 
 func makeSha256Reader(reader io.ReadSeeker) []byte {
        hash := sha256.New()
-       start, _ := reader.Seek(0, 1)
-       defer reader.Seek(start, 0)
+       start, _ := reader.Seek(0, sdkio.SeekCurrent)
+       defer reader.Seek(start, sdkio.SeekStart)
+
+       // Use CopyN to avoid allocating the 32KB buffer in io.Copy for bodies
+       // smaller than 32KB. Fall back to io.Copy if we fail to determine the size.
+       size, err := aws.SeekerLen(reader)
+       if err != nil {
+               io.Copy(hash, reader)
+       } else {
+               io.CopyN(hash, reader, size)
+       }
 
-       io.Copy(hash, reader)
        return hash.Sum(nil)
 }
 
-const doubleSpaces = "  "
-
-var doubleSpaceBytes = []byte(doubleSpaces)
+const doubleSpace = "  "
 
-func stripExcessSpaces(headerVals []string) []string {
-       vals := make([]string, len(headerVals))
-       for i, str := range headerVals {
-               // Trim leading and trailing spaces
-               trimmed := strings.TrimSpace(str)
+// stripExcessSpaces will rewrite the passed in slice's string values to not
+// contain multiple side-by-side spaces.
+func stripExcessSpaces(vals []string) {
+       var j, k, l, m, spaces int
+       for i, str := range vals {
+               // Trim trailing spaces
+               for j = len(str) - 1; j >= 0 && str[j] == ' '; j-- {
+               }
 
-               idx := strings.Index(trimmed, doubleSpaces)
-               var buf []byte
-               for idx > -1 {
-                       // Multiple adjacent spaces found
-                       if buf == nil {
-                               // first time create the buffer
-                               buf = []byte(trimmed)
-                       }
+               // Trim leading spaces
+               for k = 0; k < j && str[k] == ' '; k++ {
+               }
+               str = str[k : j+1]
 
-                       stripToIdx := -1
-                       for j := idx + 1; j < len(buf); j++ {
-                               if buf[j] != ' ' {
-                                       buf = append(buf[:idx+1], buf[j:]...)
-                                       stripToIdx = j
-                                       break
-                               }
-                       }
+               // Strip multiple spaces.
+               j = strings.Index(str, doubleSpace)
+               if j < 0 {
+                       vals[i] = str
+                       continue
+               }
 
-                       if stripToIdx >= 0 {
-                               idx = bytes.Index(buf[stripToIdx:], doubleSpaceBytes)
-                               if idx >= 0 {
-                                       idx += stripToIdx
+               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 {
-                               idx = -1
+                               // End of multiple spaces.
+                               spaces = 0
+                               buf[m] = buf[k]
+                               m++
                        }
                }
 
-               if buf != nil {
-                       vals[i] = string(buf)
-               } else {
-                       vals[i] = trimmed
-               }
+               vals[i] = string(buf[:m])
        }
-       return vals
 }