diff --git a/command/cp.go b/command/cp.go index 7ed06bfee..99b2f474a 100644 --- a/command/cp.go +++ b/command/cp.go @@ -864,7 +864,14 @@ func (c Copy) shouldOverride(ctx context.Context, srcurl *url.URL, dsturl *url.U return err } - dstClient, err := storage.NewClient(ctx, dsturl, c.storageOpts) + // Apply destination-region override so the HEAD request is signed for the + // correct region. Without this, --no-clobber with --destination-region + // sends a mis-signed HEAD that returns HTTP 400 AuthorizationHeaderMalformed. + dstOpts := c.storageOpts + if c.dstRegion != "" { + dstOpts.SetRegion(c.dstRegion) + } + dstClient, err := storage.NewClient(ctx, dsturl, dstOpts) if err != nil { return err } diff --git a/command/cp_test.go b/command/cp_test.go index ccac38ff3..8039a9041 100644 --- a/command/cp_test.go +++ b/command/cp_test.go @@ -1,13 +1,44 @@ package command import ( + "context" "io" "os" "testing" "gotest.tools/v3/assert" + + "github.com/peak/s5cmd/v2/storage" + "github.com/peak/s5cmd/v2/storage/url" ) +// TestShouldOverride_NoFlagsIsNoop verifies that shouldOverride returns nil +// immediately when none of the override-gating flags are set. This confirms +// the fast-path remains intact and also serves as a compile-time check that +// the Copy struct fields referenced by shouldOverride (including dstRegion) +// are spelled correctly. +func TestShouldOverride_NoFlagsIsNoop(t *testing.T) { + t.Parallel() + + srcURL, _ := url.New("s3://bucket/src.txt") + dstURL, _ := url.New("s3://bucket/dst.txt") + + c := Copy{ + noClobber: false, + ifSizeDiffer: false, + ifSourceNewer: false, + // dstRegion is set; the fix ensures it would be forwarded to dstOpts + // when the function proceeds past the early return. + dstRegion: "us-west-2", + storageOpts: storage.Options{}, + } + + // shouldOverride must return nil (no-op) when no override flag is set. + if err := c.shouldOverride(context.Background(), srcURL, dstURL); err != nil { + t.Errorf("expected nil when no override flags set, got %v", err) + } +} + func TestGuessContentType(t *testing.T) { t.Parallel()