diff --git a/aws/signer/v4/v4.go b/aws/signer/v4/v4.go index 06ba7773ab5..54add923ba5 100644 --- a/aws/signer/v4/v4.go +++ b/aws/signer/v4/v4.go @@ -409,6 +409,7 @@ func (s *httpSigner) buildCanonicalHeaders(host string, rule v4Internal.Rule, he if length > 0 { const contentLengthHeader = "content-length" + header.Del(contentLengthHeader) headers = append(headers, contentLengthHeader) signed[contentLengthHeader] = append(signed[contentLengthHeader], strconv.FormatInt(length, 10)) } diff --git a/aws/signer/v4/v4_test.go b/aws/signer/v4/v4_test.go index 6cd881394e9..bd69644bb5c 100644 --- a/aws/signer/v4/v4_test.go +++ b/aws/signer/v4/v4_test.go @@ -5,6 +5,7 @@ import ( "context" "crypto/sha256" "encoding/hex" + "fmt" "io" "io/ioutil" "net/http" @@ -246,6 +247,38 @@ func TestRequestHost(t *testing.T) { t.Errorf("canonical host header invalid") } } + +func TestSign_buildCanonicalHeadersContentLengthPresent(t *testing.T) { + body := `{"description": "this is a test"}` + req, _ := buildRequest("dynamodb", "us-east-1", body) + req.URL.RawQuery = "Foo=z&Foo=o&Foo=m&Foo=a" + req.Host = "myhost" + + contentLength := fmt.Sprintf("%d", len([]byte(body))) + req.Header.Add("Content-Length", contentLength) + + query := req.URL.Query() + query.Set("X-Amz-Expires", "5") + req.URL.RawQuery = query.Encode() + + ctx := &httpSigner{ + ServiceName: "dynamodb", + Region: "us-east-1", + Request: req, + Time: v4Internal.NewSigningTime(time.Now()), + KeyDerivator: v4Internal.NewSigningKeyDeriver(), + } + + build, err := ctx.Build() + if err != nil { + t.Fatalf("expected no error, got %v", err) + } + + if !strings.Contains(build.CanonicalString, "content-length:"+contentLength+"\n") { + t.Errorf("canonical header content-length invalid") + } +} + func TestSign_buildCanonicalHeaders(t *testing.T) { serviceName := "mockAPI" region := "mock-region"