Repository: opensearch-project/opensearch-go
Pull request #255 (2023-03-20 23:59:35)

Bump github.com/aws/aws-sdk-go from 1.44.210 to 1.44.225
Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.210 to 1.44.225.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/aws/aws-sdk-go/releases">github.com/aws/aws-sdk-go's releases</a>.</em></p>
<blockquote>
<h1>Release v1.44.225 (2023-03-20)</h1>
<h3>Service Client Updates</h3>
<ul>
<li><code>service/application-autoscaling</code>: Updates service API, documentation, and examples</li>
<li><code>service/neptune</code>: Updates service API and documentation
<ul>
<li>This release makes following few changes. db-cluster-identifier is now a required parameter of create-db-instance. describe-db-cluster will now return PendingModifiedValues and GlobalClusterIdentifier fields in the response.</li>
</ul>
</li>
<li><code>service/s3outposts</code>: Updates service API and documentation</li>
<li><code>service/workdocs</code>: Updates service API, documentation, and paginators
<ul>
<li>This release adds a new API, SearchResources, which enable users to search through metadata and content of folders, documents, document versions and comments in a WorkDocs site.</li>
</ul>
</li>
</ul>
<h1>Release v1.44.224 (2023-03-17)</h1>
<h3>Service Client Updates</h3>
<ul>
<li><code>service/billingconductor</code>: Updates service API and documentation</li>
<li><code>service/config</code>: Updates service API and documentation</li>
<li><code>service/dms</code>: Updates service API and documentation
<ul>
<li>S3 setting to create AWS Glue Data Catalog. Oracle setting to control conversion of timestamp column. Support for Kafka SASL Plain authentication. Setting to map boolean from PostgreSQL to Redshift. SQL Server settings to force lob lookup on inline LOBs and to control access of database logs.</li>
</ul>
</li>
</ul>
<h1>Release v1.44.223 (2023-03-16)</h1>
<h3>Service Client Updates</h3>
<ul>
<li><code>service/guardduty</code>: Updates service API, documentation, and paginators
<ul>
<li>Updated 9 APIs for feature enablement to reflect expansion of GuardDuty to features. Added new APIs and updated existing APIs to support RDS Protection GA.</li>
</ul>
</li>
<li><code>service/resource-explorer-2</code>: Updates service documentation</li>
<li><code>service/runtime.sagemaker</code>: Updates service documentation</li>
</ul>
<h1>Release v1.44.222 (2023-03-15)</h1>
<h3>Service Client Updates</h3>
<ul>
<li><code>service/migrationhubstrategy</code>: Updates service API and documentation</li>
<li><code>service/s3control</code>: Updates service API and documentation
<ul>
<li>Added support for S3 Object Lambda aliases.</li>
</ul>
</li>
<li><code>service/securitylake</code>: Updates service API and documentation</li>
</ul>
<h3>SDK Bugs</h3>
<ul>
<li><code>private/protocol</code>: Fix error message being elided in restJson errors.
<ul>
<li>Use request body error message for unknown errors when header is absent.</li>
</ul>
</li>
</ul>
<h1>Release v1.44.221 (2023-03-14)</h1>
<h3>Service Client Updates</h3>
<ul>
<li><code>service/application-autoscaling</code>: Updates service API and documentation</li>
<li><code>service/dataexchange</code>: Updates service API and documentation</li>
<li><code>service/directconnect</code>: Updates service documentation</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/aws/aws-sdk-go/commit/dce57316190007a9731bd14dc0f86a8a487b0b14"><code>dce5731</code></a> Release v1.44.225 (2023-03-20) (<a href="https://redirect.github.com/aws/aws-sdk-go/issues/4766">#4766</a>)</li>
<li><a href="https://github.com/aws/aws-sdk-go/commit/02f19bf6079638a9b67f60887a6f949783d022c1"><code>02f19bf</code></a> Merge pull request <a href="https://redirect.github.com/aws/aws-sdk-go/issues/4759">#4759</a> from d3QUone/fix-typo</li>
<li><a href="https://github.com/aws/aws-sdk-go/commit/cf6d104c9d69fbab925d65849626f910ec099389"><code>cf6d104</code></a> Release v1.44.224 (2023-03-17) (<a href="https://redirect.github.com/aws/aws-sdk-go/issues/4765">#4765</a>)</li>
<li><a href="https://github.com/aws/aws-sdk-go/commit/cf6b9a88deb223597d4b720e08ae2a3f3e743ae0"><code>cf6b9a8</code></a> Release v1.44.223 (2023-03-16) (<a href="https://redirect.github.com/aws/aws-sdk-go/issues/4764">#4764</a>)</li>
<li><a href="https://github.com/aws/aws-sdk-go/commit/229c49ae2fa0c725e10ad59a9108aa19bb690fb9"><code>229c49a</code></a> Release v1.44.222 (2023-03-15) (<a href="https://redirect.github.com/aws/aws-sdk-go/issues/4763">#4763</a>)</li>
<li><a href="https://github.com/aws/aws-sdk-go/commit/1aa1fa61cec25f4336014e25760318dca25c55e9"><code>1aa1fa6</code></a> Merge pull request <a href="https://redirect.github.com/aws/aws-sdk-go/issues/4762">#4762</a> from aws/fix-changelog-pending</li>
<li><a href="https://github.com/aws/aws-sdk-go/commit/0775cabc18a9d3b03f14c419fbd476fe8d4f3d8e"><code>0775cab</code></a> fix pending changelog</li>
<li><a href="https://github.com/aws/aws-sdk-go/commit/25c011f7b5e74f6c926e4e0808e177f790eeebe6"><code>25c011f</code></a> Merge pull request <a href="https://redirect.github.com/aws/aws-sdk-go/issues/4740">#4740</a> from jar-b/fix-unknown-code-header-only</li>
<li><a href="https://github.com/aws/aws-sdk-go/commit/5afcc94c1b96154ab2abe48d22002f983af2a7d7"><code>5afcc94</code></a> Fix typo in AssumeRoleTokenProvider description</li>
<li><a href="https://github.com/aws/aws-sdk-go/commit/7d0e04c42bd1927603d38f0702f0d240d753a706"><code>7d0e04c</code></a> Release v1.44.221 (2023-03-14) (<a href="https://redirect.github.com/aws/aws-sdk-go/issues/4757">#4757</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/aws/aws-sdk-go/compare/v1.44.210...v1.44.225">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go&package-manager=go_modules&previous-version=1.44.210&new-version=1.44.225)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

VachaShah: @dependabot recreate

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d30a2a..6bfcdfc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 - Bumps `github.com/aws/aws-sdk-go-v2/config` from 1.18.9 to 1.18.12
 - Bumps `github.com/aws/aws-sdk-go-v2/config` from 1.18.8 to 1.18.10
 - Bumps `github.com/aws/aws-sdk-go-v2/config` from 1.17.10 to 1.18.9
-- Bumps `github.com/aws/aws-sdk-go` from 1.44.195 to 1.44.210
+- Bumps `github.com/aws/aws-sdk-go` from 1.44.195 to 1.44.225
 - Bumps `github.com/aws/aws-sdk-go` from 1.44.185 to 1.44.205
 - Bumps `github.com/aws/aws-sdk-go` from 1.44.180 to 1.44.195
 - Bumps `github.com/aws/aws-sdk-go` from 1.44.176 to 1.44.185
@@ -18,7 +18,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 ### Added
 - Github workflow for changelog verification ([#172](https://github.com/opensearch-project/opensearch-go/pull/172))
 - Add Go Documentation link for the client ([#182](https://github.com/opensearch-project/opensearch-go/pull/182))
-- Support for Amazon OpenSearch Serverless ([#216](https://github.com/opensearch-project/opensearch-go/pull/216))
+- Support for Amazon OpenSearch Serverless ([#216](https://github.com/opensearch-project/opensearch-go/pull/216), [#259](https://github.com/opensearch-project/opensearch-go/pull/259))
 
 ### Dependencies
 - Bumps `github.com/stretchr/testify` from 1.8.0 to 1.8.1
diff --git a/USER_GUIDE.md b/USER_GUIDE.md
index 7cf07b4..faaf1e3 100644
--- a/USER_GUIDE.md
+++ b/USER_GUIDE.md
@@ -184,37 +184,40 @@ func main() {
 
 	// Create an AWS request Signer and load AWS configuration using default config folder or env vars.
 	// See https://docs.aws.amazon.com/opensearch-service/latest/developerguide/request-signing.html#request-signing-go
-	signer, err := requestsigner.NewSigner(session.Options{SharedConfigState: session.SharedConfigEnable})
+	signer, err := requestsigner.NewSignerWithService(
+		session.Options{SharedConfigState: session.SharedConfigEnable},
+		requestsigner.OpenSearchService, // Use requestsigner.OpenSearchServerless for Amazon OpenSearch Serverless.
+	)
 	if err != nil {
-		log.Fatal(err) // Do not log.fatal in a production ready app.
+		log.Fatalf("failed to create signer: %v", err) // Do not log.fatal in a production ready app.
 	}
 
-	// Create an opensearch client and use the request-signer
+	// Create an opensearch client and use the request-signer.
 	client, err := opensearch.NewClient(opensearch.Config{
 		Addresses: []string{endpoint},
 		Signer:    signer,
 	})
 	if err != nil {
-		log.Fatal("client creation err", err)
+		log.Fatalf("failed to create new opensearch client: %v", err)
 	}
 
 	ping := opensearchapi.PingRequest{}
 
 	resp, err := ping.Do(ctx, client)
 	if err != nil {
-		log.Fatal(err)
+		log.Fatalf("failed to ping: %v", err)
 	}
 	defer resp.Body.Close()
 
 	if resp.IsError() {
-		log.Println("ping response status ", resp.Status())
+		log.Printf("ping response status: %q", resp.Status())
 
 		respBody, err := io.ReadAll(resp.Body)
 		if err != nil {
-			log.Fatal("response body read err", err)
+			log.Fatalf("failed to read response body body: %v", err)
 		}
 
-		log.Fatal("ping resp body", respBody)
+		log.Fatalf("ping resp body: %s", respBody)
 	}
 
 	log.Println("PING OK")
@@ -231,6 +234,7 @@ package main
 import (
 	"context"
 	"log"
+	"strings"
 
 	"github.com/aws/aws-sdk-go-v2/aws"
 	"github.com/aws/aws-sdk-go-v2/config"
@@ -251,25 +255,25 @@ func main() {
 		),
 	)
 	if err != nil {
-		log.Fatal(err) // Do not log.fatal in a production ready app.
+		log.Fatalf("failed to load aws configuraiton: %v", err) // Do not log.fatal in a production ready app.
 	}
 
 	// Create an AWS request Signer and load AWS configuration using default config folder or env vars.
-	signer, err := requestsigner.NewSignerWithService(awsCfg, "es") // "aoss" for Amazon OpenSearch Serverless
+	signer, err := requestsigner.NewSignerWithService(awsCfg, "es") // Use "aoss" for Amazon OpenSearch Serverless
 	if err != nil {
-		log.Fatal(err) // Do not log.fatal in a production ready app.
+		log.Fatalf("failed to create signer: %v", err)
 	}
 
-	// Create an opensearch client and use the request-signer
+	// Create an opensearch client and use the request-signer.
 	client, err := opensearch.NewClient(opensearch.Config{
 		Addresses: []string{endpoint},
 		Signer:    signer,
 	})
 	if err != nil {
-		log.Fatal("client creation err", err)
+		log.Fatalf("failed to create new opensearch client: %v", err)
 	}
 
-	indexName = "go-test-index"
+	indexName := "go-test-index"
 
 	// Define index mapping.
 	mapping := strings.NewReader(`{
@@ -287,11 +291,9 @@ func main() {
 	}
 	createIndexResponse, err := createIndex.Do(context.Background(), client)
 	if err != nil {
-		log.Println("Error ", err.Error())
-		log.Println("failed to create index ", err)
-		log.Fatal("create response body read err", err)
+		log.Fatalf("failed to create index: %v", err)
 	}
-	log.Println(createIndexResponse)
+	log.Printf("created index: %#v", createIndexResponse)
 
 	// Delete previously created index.
 	deleteIndex := opensearchapi.IndicesDeleteRequest{
@@ -300,10 +302,9 @@ func main() {
 
 	deleteIndexResponse, err := deleteIndex.Do(context.Background(), client)
 	if err != nil {
-		log.Println("failed to delete index ", err)
-		log.Fatal("delete index response body read err", err)
+		log.Fatalf("failed to delete index: %v", err)
 	}
-	log.Println("deleting index", deleteIndexResponse)
+	log.Printf("deleted index: %#v", deleteIndexResponse)
 }
 
 func getCredentialProvider(accessKey, secretAccessKey, token string) aws.CredentialsProviderFunc {
diff --git a/go.mod b/go.mod
index 186812b..fcaf1ec 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/opensearch-project/opensearch-go/v2
 go 1.15
 
 require (
-	github.com/aws/aws-sdk-go v1.44.210
+	github.com/aws/aws-sdk-go v1.44.225
 	github.com/aws/aws-sdk-go-v2 v1.17.6
 	github.com/aws/aws-sdk-go-v2/config v1.18.18
 	github.com/stretchr/testify v1.8.2
diff --git a/go.sum b/go.sum
index 5135e97..64b0c33 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-github.com/aws/aws-sdk-go v1.44.210 h1:/cqRMHSSgzLEKILIDGwhaX2hiIpyRurw7MRy6aaSufg=
-github.com/aws/aws-sdk-go v1.44.210/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
+github.com/aws/aws-sdk-go v1.44.225 h1:JNJpUg+M1cm4jtKnyex//Mw1Rv8QN/kWT3dtr+oLdW4=
+github.com/aws/aws-sdk-go v1.44.225/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
 github.com/aws/aws-sdk-go-v2 v1.17.6 h1:Y773UK7OBqhzi5VDXMi1zVGsoj+CVHs2eaC2bDsLwi0=
 github.com/aws/aws-sdk-go-v2 v1.17.6/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
 github.com/aws/aws-sdk-go-v2/config v1.18.18 h1:/ePABXvXl3ESlzUGnkkvvNnRFw3Gh13dyqaq0Qo3JcU=
diff --git a/signer/aws/aws.go b/signer/aws/aws.go
index 42abc33..3df651e 100644
--- a/signer/aws/aws.go
+++ b/signer/aws/aws.go
@@ -11,6 +11,8 @@ package aws
 
 import (
 	"bytes"
+	"crypto/sha256"
+	"encoding/hex"
 	"errors"
 	"fmt"
 	"io"
@@ -22,28 +24,34 @@ import (
 	v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
 )
 
-// OpenSearchService AWS OpenSearchService Name
+// OpenSearchService Amazon OpenSearch Service Name
 const OpenSearchService = "es"
 
-// Signer is a interface that will implement opensearchtransport.Signer
+// OpenSearchServerless Amazon OpenSearch Serverless Name
+const OpenSearchServerless = "aoss"
+
+const emptyBodySHA256 = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
+
+// Signer is an interface that will implement opensearchtransport.Signer
 type Signer struct {
 	session session.Session
 	service string
 }
 
-// NewSigner returns an instance of Signer for AWS OpenSearchService
+// NewSigner returns an instance of Signer for configured for Amazon OpenSearch Service.
+// Use NewSignerWithService to configure it for another service such as Amazon OpenSearch Serverless.
 func NewSigner(opts session.Options) (*Signer, error) {
 	return NewSignerWithService(opts, OpenSearchService)
 }
 
-// NewSignerWithService returns an instance of Signer for given service
+// NewSignerWithService returns an instance of Signer for a given service.
 func NewSignerWithService(opts session.Options, service string) (*Signer, error) {
 	if len(strings.TrimSpace(service)) < 1 {
 		return nil, errors.New("service cannot be empty")
 	}
 	awsSession, err := session.NewSessionWithOptions(opts)
 	if err != nil {
-		return nil, fmt.Errorf("failed to get session from given option %v due to %s", opts, err)
+		return nil, fmt.Errorf("failed to get session from given options %v: %v", opts, err)
 	}
 	return &Signer{
 		session: *awsSession,
@@ -51,25 +59,50 @@ func NewSignerWithService(opts session.Options, service string) (*Signer, error)
 	}, nil
 }
 
-// SignRequest signs the request using SigV4
+// SignRequest signs the request using SigV4.
 func (s Signer) SignRequest(req *http.Request) error {
-	signer := v4.NewSigner(s.session.Config.Credentials)
-	return sign(req, s.session.Config.Region, s.service, signer)
-
+	return sign(req, s.session.Config.Region, s.service, v4.NewSigner(s.session.Config.Credentials))
 }
 
-func sign(req *http.Request, region *string, serviceName string, signer *v4.Signer) (err error) {
+func sign(req *http.Request, region *string, serviceName string, signer *v4.Signer) error {
 	if region == nil || len(*region) == 0 {
 		return fmt.Errorf("aws region cannot be empty")
 	}
-	if req.Body == nil {
-		_, err = signer.Sign(req, nil, serviceName, *region, time.Now().UTC())
-		return
+
+	var body io.ReadSeeker
+	var contentSha256Hash = emptyBodySHA256
+
+	if req.Body != nil {
+		b, err := io.ReadAll(req.Body)
+		if err != nil {
+			return fmt.Errorf("failed to read request body: %v", err)
+		}
+		body = bytes.NewReader(b)
+		hash, err := hexEncodedSha256(b)
+		if err != nil {
+			return fmt.Errorf("failed to calculate hash of request body: %v", err)
+		}
+		contentSha256Hash = hash
 	}
-	buf, err := io.ReadAll(req.Body)
-	if err != nil {
+	// Add the "X-Amz-Content-Sha256" header as required by Amazon OpenSearch Serverless.
+	req.Header.Set("X-Amz-Content-Sha256", contentSha256Hash)
+
+	if _, err := signer.Sign(req, body, serviceName, *region, time.Now().UTC()); err != nil {
 		return err
 	}
-	_, err = signer.Sign(req, bytes.NewReader(buf), serviceName, *region, time.Now().UTC())
-	return
+
+	return nil
+}
+
+func hexEncodedSha256(b []byte) (string, error) {
+	hasher := sha256.New()
+
+	_, err := hasher.Write(b)
+	if err != nil {
+		return "", fmt.Errorf("failed to write: %v", err)
+	}
+
+	digest := hasher.Sum(nil)
+
+	return hex.EncodeToString(digest), nil
 }


Test patch:
diff --git a/signer/aws/aws_test.go b/signer/aws/aws_test.go
index 3c049af..48ef4d8 100644
--- a/signer/aws/aws_test.go
+++ b/signer/aws/aws_test.go
@@ -11,26 +11,29 @@ package aws
 
 import (
 	"bytes"
+	"errors"
+	"io"
 	"net/http"
-	"os"
 	"testing"
 
 	"github.com/aws/aws-sdk-go/aws"
-	"github.com/aws/aws-sdk-go/aws/session"
-
 	"github.com/aws/aws-sdk-go/aws/credentials"
+	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/stretchr/testify/assert"
 )
 
+func TestConstants(t *testing.T) {
+	assert.Equal(t, "es", OpenSearchService)
+	assert.Equal(t, "aoss", OpenSearchServerless)
+	assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", emptyBodySHA256)
+}
+
 func TestV4Signer(t *testing.T) {
+
 	t.Run("sign request failed due to no region found", func(t *testing.T) {
 		req, err := http.NewRequest(http.MethodGet, "https://localhost:9200", nil)
 		assert.NoError(t, err)
-		region := os.Getenv("AWS_REGION")
-		os.Setenv("AWS_REGION", "")
-		defer func() {
-			os.Setenv("AWS_REGION", region)
-		}()
+
 		sessionOptions := session.Options{
 			Config: aws.Config{
 				Credentials: credentials.NewStaticCredentials("AKID", "SECRET_KEY", "TOKEN"),
@@ -38,19 +41,15 @@ func TestV4Signer(t *testing.T) {
 		}
 		signer, err := NewSigner(sessionOptions)
 		assert.NoError(t, err)
-		err = signer.SignRequest(req)
 
-		assert.EqualErrorf(
-			t, err, "aws region cannot be empty", "unexpected error")
+		err = signer.SignRequest(req)
+		assert.EqualError(t, err, "aws region cannot be empty")
 	})
+
 	t.Run("sign request success", func(t *testing.T) {
 		req, err := http.NewRequest(http.MethodGet, "https://localhost:9200", nil)
 		assert.NoError(t, err)
-		region := os.Getenv("AWS_REGION")
-		os.Setenv("AWS_REGION", "us-west-2")
-		defer func() {
-			os.Setenv("AWS_REGION", region)
-		}()
+
 		sessionOptions := session.Options{
 			Config: aws.Config{
 				Region:      aws.String("us-west-2"),
@@ -59,11 +58,14 @@ func TestV4Signer(t *testing.T) {
 		}
 		signer, err := NewSigner(sessionOptions)
 		assert.NoError(t, err)
+
 		err = signer.SignRequest(req)
 		assert.NoError(t, err)
+
 		q := req.Header
 		assert.NotEmpty(t, q.Get("Authorization"))
 		assert.NotEmpty(t, q.Get("X-Amz-Date"))
+		assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", q.Get("X-Amz-Content-Sha256"))
 	})
 
 	t.Run("sign request success with body", func(t *testing.T) {
@@ -71,11 +73,7 @@ func TestV4Signer(t *testing.T) {
 			http.MethodPost, "https://localhost:9200",
 			bytes.NewBuffer([]byte(`some data`)))
 		assert.NoError(t, err)
-		region := os.Getenv("AWS_REGION")
-		os.Setenv("AWS_REGION", "us-west-2")
-		defer func() {
-			os.Setenv("AWS_REGION", region)
-		}()
+
 		sessionOptions := session.Options{
 			Config: aws.Config{
 				Region:      aws.String("us-west-2"),
@@ -84,39 +82,41 @@ func TestV4Signer(t *testing.T) {
 		}
 		signer, err := NewSigner(sessionOptions)
 		assert.NoError(t, err)
+
 		err = signer.SignRequest(req)
 		assert.NoError(t, err)
+
 		q := req.Header
 		assert.NotEmpty(t, q.Get("Authorization"))
 		assert.NotEmpty(t, q.Get("X-Amz-Date"))
+		assert.Equal(t, "1307990e6ba5ca145eb35e99182a9bec46531bc54ddf656a602c780fa0240dee", q.Get("X-Amz-Content-Sha256"))
 	})
 
-	t.Run("sign request success with body for other AWS Services", func(t *testing.T) {
+	t.Run("sign request success with body for OpenSearch Service Serverless", func(t *testing.T) {
 		req, err := http.NewRequest(
 			http.MethodPost, "https://localhost:9200",
 			bytes.NewBuffer([]byte(`some data`)))
 		assert.NoError(t, err)
-		region := os.Getenv("AWS_REGION")
-		os.Setenv("AWS_REGION", "us-west-2")
-		defer func() {
-			os.Setenv("AWS_REGION", region)
-		}()
+
 		sessionOptions := session.Options{
 			Config: aws.Config{
 				Region:      aws.String("us-west-2"),
 				Credentials: credentials.NewStaticCredentials("AKID", "SECRET_KEY", "TOKEN"),
 			},
 		}
-		signer, err := NewSignerWithService(sessionOptions, "ec")
+		signer, err := NewSignerWithService(sessionOptions, OpenSearchServerless)
 		assert.NoError(t, err)
+
 		err = signer.SignRequest(req)
 		assert.NoError(t, err)
+
 		q := req.Header
 		assert.NotEmpty(t, q.Get("Authorization"))
 		assert.NotEmpty(t, q.Get("X-Amz-Date"))
+		assert.Equal(t, "1307990e6ba5ca145eb35e99182a9bec46531bc54ddf656a602c780fa0240dee", q.Get("X-Amz-Content-Sha256"))
 	})
 
-	t.Run("sign request failed due to invalid service", func(t *testing.T) {
+	t.Run("new signer failed due to empty service", func(t *testing.T) {
 		sessionOptions := session.Options{
 			Config: aws.Config{
 				Region:      aws.String("us-west-2"),
@@ -125,6 +125,41 @@ func TestV4Signer(t *testing.T) {
 		}
 		_, err := NewSignerWithService(sessionOptions, "")
 		assert.EqualError(t, err, "service cannot be empty")
+	})
 
+	t.Run("new signer failed due to blank service", func(t *testing.T) {
+		sessionOptions := session.Options{
+			Config: aws.Config{
+				Region:      aws.String("us-west-2"),
+				Credentials: credentials.NewStaticCredentials("AKID", "SECRET_KEY", "TOKEN"),
+			},
+		}
+		_, err := NewSignerWithService(sessionOptions, "	 ")
+		assert.EqualError(t, err, "service cannot be empty")
+	})
+
+	t.Run("sign request failed due to invalid body", func(t *testing.T) {
+		req, err := http.NewRequest(http.MethodPost, "https://localhost:9200", nil)
+		assert.NoError(t, err)
+
+		req.Body = io.NopCloser(brokenReader("boom"))
+
+		sessionOptions := session.Options{
+			Config: aws.Config{
+				Region:      aws.String("us-west-2"),
+				Credentials: credentials.NewStaticCredentials("AKID", "SECRET_KEY", "TOKEN"),
+			},
+		}
+		signer, err := NewSigner(sessionOptions)
+		assert.NoError(t, err)
+
+		err = signer.SignRequest(req)
+		assert.EqualError(t, err, "failed to read request body: boom")
 	})
 }
+
+type brokenReader string
+
+func (br brokenReader) Read([]byte) (int, error) {
+	return 0, errors.New(string(br))
+}