Repository: gruntwork-io/terratest (language: go)

Issue:
Nil Pointer error occurs when the Body is not set in http_helper.HttpDoOptions
**Describe the bug**
Calling certain functions in `http_helper` without a request body results in nil pointer dereference errors.

**To Reproduce**

```go
doOpts := http_helper.HttpDoOptions{
	Method:    "GET",
	Url:       clientConf.ClientEndpoints.MembersAddresses,
	Headers:   headers,
	TlsConfig: nil,
	Timeout:   10,
	//Body: strings.NewReader(""), -> workaround
}
response := http_helper.HTTPDoWithRetryWithOptions(s.T(), doOpts, 200, 10, 10*time.Second)
```

```
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x18 pc=0x10490e7e8]

goroutine 6 [running]:
testing.tRunner.func1.2({0x104bae500, 0x104dd21e0})
	/usr/local/go/src/testing/testing.go:1545 +0x1c8
testing.tRunner.func1()
	/usr/local/go/src/testing/testing.go:1548 +0x360
panic({0x104bae500?, 0x104dd21e0?})
	/usr/local/go/src/runtime/panic.go:914 +0x218
io.ReadAll({0x0, 0x0})
	/usr/local/go/src/io/io.go:704 +0x58
github.com/gruntwork-io/terratest/modules/http-helper.HTTPDoWithRetryWithOptionsE({0x104c0a650, 0x140001544e0}, {{0x104aeadb2, 0x3}, {0x104b70e00, 0x0}, {0x0, 0x0}, 0x1400007f140, 0x0, ...}, ...)
	.../terratest/modules/http-helper/http_helper.go:351 +0x68
github.com/gruntwork-io/terratest/modules/http-helper.HTTPDoWithRetryWithOptions({0x104c0a650, 0x140001544e0}, {{0x104aeadb2, 0x3}, {0x104b70e00, 0x0}, {0x0, 0x0}, 0x1400007f140, 0x0, ...}, ...)
	.../terratest/modules/http-helper/http_helper.go:316 +0x64
...
```

**Expected behavior**
I would expect to call the function and send an HTTP request successfully without needing to set an empty `noop` reader body using `strings.NewReader("")` in the `HttpDoOptions`.

**Versions**
- Terratest version: v0.41.12



Discussion / hints:

Fix patch:
diff --git a/modules/http-helper/http_helper.go b/modules/http-helper/http_helper.go
index 06efc2ce1..802fa78a4 100644
--- a/modules/http-helper/http_helper.go
+++ b/modules/http-helper/http_helper.go
@@ -346,11 +346,15 @@ func HTTPDoWithRetryWithOptionsE(
 	t testing.TestingT, options HttpDoOptions, expectedStatus int,
 	retries int, sleepBetweenRetries time.Duration,
 ) (string, error) {
-	// The request body is closed after a request is complete.
-	// Extract the underlying data and cache it so we can reuse for retried requests
-	data, err := io.ReadAll(options.Body)
-	if err != nil {
-		return "", err
+	var data []byte
+	if options.Body != nil {
+		// The request body is closed after a request is complete.
+		// Read the underlying data and cache it, so we can reuse for retried requests.
+		b, err := io.ReadAll(options.Body)
+		if err != nil {
+			return "", err
+		}
+		data = b
 	}
 
 	options.Body = nil


Test patch:
diff --git a/modules/http-helper/http_helper_test.go b/modules/http-helper/http_helper_test.go
index 8fbc2d2c4..5dfc5af04 100644
--- a/modules/http-helper/http_helper_test.go
+++ b/modules/http-helper/http_helper_test.go
@@ -139,6 +139,21 @@ func TestErrorWithRetry(t *testing.T) {
 	}
 }
 
+func TestEmptyRequestBodyWithRetryWithOptions(t *testing.T) {
+	t.Parallel()
+	ts := getTestServerForFunction(bodyCopyHandler)
+	defer ts.Close()
+
+	options := HttpDoOptions{
+		Method: "GET",
+		Url:    ts.URL,
+		Body:   nil,
+	}
+
+	response := HTTPDoWithRetryWithOptions(t, options, 200, 0, time.Second)
+	require.Equal(t, "", response)
+}
+
 func bodyCopyHandler(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusOK)
 	body, _ := io.ReadAll(r.Body)