github.com/mponton/terratest@v0.44.0/modules/http-helper/http_helper.go (about)

     1  // Package http_helper contains helpers to interact with deployed resources through HTTP.
     2  package http_helper
     3  
     4  import (
     5  	"bytes"
     6  	"crypto/tls"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/mponton/terratest/modules/logger"
    15  	"github.com/mponton/terratest/modules/retry"
    16  	"github.com/mponton/terratest/modules/testing"
    17  )
    18  
    19  type HttpGetOptions struct {
    20  	Url       string
    21  	TlsConfig *tls.Config
    22  	Timeout   int
    23  }
    24  
    25  type HttpDoOptions struct {
    26  	Method    string
    27  	Url       string
    28  	Body      io.Reader
    29  	Headers   map[string]string
    30  	TlsConfig *tls.Config
    31  	Timeout   int
    32  }
    33  
    34  // HttpGet performs an HTTP GET, with an optional pointer to a custom TLS configuration, on the given URL and
    35  // return the HTTP status code and body. If there's any error, fail the test.
    36  func HttpGet(t testing.TestingT, url string, tlsConfig *tls.Config) (int, string) {
    37  	return HttpGetWithOptions(t, HttpGetOptions{Url: url, TlsConfig: tlsConfig, Timeout: 10})
    38  }
    39  
    40  // HttpGetWithOptions performs an HTTP GET, with an optional pointer to a custom TLS configuration, on the given URL and
    41  // return the HTTP status code and body. If there's any error, fail the test.
    42  func HttpGetWithOptions(t testing.TestingT, options HttpGetOptions) (int, string) {
    43  	statusCode, body, err := HttpGetWithOptionsE(t, options)
    44  	if err != nil {
    45  		t.Fatal(err)
    46  	}
    47  	return statusCode, body
    48  }
    49  
    50  // HttpGetE performs an HTTP GET, with an optional pointer to a custom TLS configuration, on the given URL and
    51  // return the HTTP status code, body, and any error.
    52  func HttpGetE(t testing.TestingT, url string, tlsConfig *tls.Config) (int, string, error) {
    53  	return HttpGetWithOptionsE(t, HttpGetOptions{Url: url, TlsConfig: tlsConfig, Timeout: 10})
    54  }
    55  
    56  // HttpGetWithOptionsE performs an HTTP GET, with an optional pointer to a custom TLS configuration, on the given URL and
    57  // return the HTTP status code, body, and any error.
    58  func HttpGetWithOptionsE(t testing.TestingT, options HttpGetOptions) (int, string, error) {
    59  	logger.Logf(t, "Making an HTTP GET call to URL %s", options.Url)
    60  
    61  	// Set HTTP client transport config
    62  	tr := http.DefaultTransport.(*http.Transport).Clone()
    63  	tr.TLSClientConfig = options.TlsConfig
    64  
    65  	client := http.Client{
    66  		// By default, Go does not impose a timeout, so an HTTP connection attempt can hang for a LONG time.
    67  		Timeout: time.Duration(options.Timeout) * time.Second,
    68  		// Include the previously created transport config
    69  		Transport: tr,
    70  	}
    71  
    72  	resp, err := client.Get(options.Url)
    73  	if err != nil {
    74  		return -1, "", err
    75  	}
    76  
    77  	defer resp.Body.Close()
    78  	body, err := ioutil.ReadAll(resp.Body)
    79  
    80  	if err != nil {
    81  		return -1, "", err
    82  	}
    83  
    84  	return resp.StatusCode, strings.TrimSpace(string(body)), nil
    85  }
    86  
    87  // HttpGetWithValidation performs an HTTP GET on the given URL and verify that you get back the expected status code and body. If either
    88  // doesn't match, fail the test.
    89  func HttpGetWithValidation(t testing.TestingT, url string, tlsConfig *tls.Config, expectedStatusCode int, expectedBody string) {
    90  	options := HttpGetOptions{Url: url, TlsConfig: tlsConfig, Timeout: 10}
    91  	HttpGetWithValidationWithOptions(t, options, expectedStatusCode, expectedBody)
    92  }
    93  
    94  // HttpGetWithValidationWithOptions performs an HTTP GET on the given URL and verify that you get back the expected status code and body. If either
    95  // doesn't match, fail the test.
    96  func HttpGetWithValidationWithOptions(t testing.TestingT, options HttpGetOptions, expectedStatusCode int, expectedBody string) {
    97  	err := HttpGetWithValidationWithOptionsE(t, options, expectedStatusCode, expectedBody)
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  }
   102  
   103  // HttpGetWithValidationE performs an HTTP GET on the given URL and verify that you get back the expected status code and body. If either
   104  // doesn't match, return an error.
   105  func HttpGetWithValidationE(t testing.TestingT, url string, tlsConfig *tls.Config, expectedStatusCode int, expectedBody string) error {
   106  	options := HttpGetOptions{Url: url, TlsConfig: tlsConfig, Timeout: 10}
   107  	return HttpGetWithValidationWithOptionsE(t, options, expectedStatusCode, expectedBody)
   108  }
   109  
   110  // HttpGetWithValidationWithOptionsE performs an HTTP GET on the given URL and verify that you get back the expected status code and body. If either
   111  // doesn't match, return an error.
   112  func HttpGetWithValidationWithOptionsE(t testing.TestingT, options HttpGetOptions, expectedStatusCode int, expectedBody string) error {
   113  	return HttpGetWithCustomValidationWithOptionsE(t, options, func(statusCode int, body string) bool {
   114  		return statusCode == expectedStatusCode && body == expectedBody
   115  	})
   116  }
   117  
   118  // HttpGetWithCustomValidation performs an HTTP GET on the given URL and validate the returned status code and body using the given function.
   119  func HttpGetWithCustomValidation(t testing.TestingT, url string, tlsConfig *tls.Config, validateResponse func(int, string) bool) {
   120  	HttpGetWithCustomValidationWithOptions(t, HttpGetOptions{Url: url, TlsConfig: tlsConfig, Timeout: 10}, validateResponse)
   121  }
   122  
   123  // HttpGetWithCustomValidationWithOptions performs an HTTP GET on the given URL and validate the returned status code and body using the given function.
   124  func HttpGetWithCustomValidationWithOptions(t testing.TestingT, options HttpGetOptions, validateResponse func(int, string) bool) {
   125  	err := HttpGetWithCustomValidationWithOptionsE(t, options, validateResponse)
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  }
   130  
   131  // HttpGetWithCustomValidationE performs an HTTP GET on the given URL and validate the returned status code and body using the given function.
   132  func HttpGetWithCustomValidationE(t testing.TestingT, url string, tlsConfig *tls.Config, validateResponse func(int, string) bool) error {
   133  	return HttpGetWithCustomValidationWithOptionsE(t, HttpGetOptions{Url: url, TlsConfig: tlsConfig, Timeout: 10}, validateResponse)
   134  }
   135  
   136  // HttpGetWithCustomValidationWithOptionsE performs an HTTP GET on the given URL and validate the returned status code and body using the given function.
   137  func HttpGetWithCustomValidationWithOptionsE(t testing.TestingT, options HttpGetOptions, validateResponse func(int, string) bool) error {
   138  	statusCode, body, err := HttpGetWithOptionsE(t, options)
   139  
   140  	if err != nil {
   141  		return err
   142  	}
   143  
   144  	if !validateResponse(statusCode, body) {
   145  		return ValidationFunctionFailed{Url: options.Url, Status: statusCode, Body: body}
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  // HttpGetWithRetry repeatedly performs an HTTP GET on the given URL until the given status code and body are returned or until max
   152  // retries has been exceeded.
   153  func HttpGetWithRetry(t testing.TestingT, url string, tlsConfig *tls.Config, expectedStatus int, expectedBody string, retries int, sleepBetweenRetries time.Duration) {
   154  	options := HttpGetOptions{Url: url, TlsConfig: tlsConfig, Timeout: 10}
   155  	HttpGetWithRetryWithOptions(t, options, expectedStatus, expectedBody, retries, sleepBetweenRetries)
   156  }
   157  
   158  // HttpGetWithRetryWithOptions repeatedly performs an HTTP GET on the given URL until the given status code and body are returned or until max
   159  // retries has been exceeded.
   160  func HttpGetWithRetryWithOptions(t testing.TestingT, options HttpGetOptions, expectedStatus int, expectedBody string, retries int, sleepBetweenRetries time.Duration) {
   161  	err := HttpGetWithRetryWithOptionsE(t, options, expectedStatus, expectedBody, retries, sleepBetweenRetries)
   162  	if err != nil {
   163  		t.Fatal(err)
   164  	}
   165  }
   166  
   167  // HttpGetWithRetryE repeatedly performs an HTTP GET on the given URL until the given status code and body are returned or until max
   168  // retries has been exceeded.
   169  func HttpGetWithRetryE(t testing.TestingT, url string, tlsConfig *tls.Config, expectedStatus int, expectedBody string, retries int, sleepBetweenRetries time.Duration) error {
   170  	options := HttpGetOptions{Url: url, TlsConfig: tlsConfig, Timeout: 10}
   171  	return HttpGetWithRetryWithOptionsE(t, options, expectedStatus, expectedBody, retries, sleepBetweenRetries)
   172  }
   173  
   174  // HttpGetWithRetryWithOptionsE repeatedly performs an HTTP GET on the given URL until the given status code and body are returned or until max
   175  // retries has been exceeded.
   176  func HttpGetWithRetryWithOptionsE(t testing.TestingT, options HttpGetOptions, expectedStatus int, expectedBody string, retries int, sleepBetweenRetries time.Duration) error {
   177  	_, err := retry.DoWithRetryE(t, fmt.Sprintf("HTTP GET to URL %s", options.Url), retries, sleepBetweenRetries, func() (string, error) {
   178  		return "", HttpGetWithValidationWithOptionsE(t, options, expectedStatus, expectedBody)
   179  	})
   180  
   181  	return err
   182  }
   183  
   184  // HttpGetWithRetryWithCustomValidation repeatedly performs an HTTP GET on the given URL until the given validation function returns true or max retries
   185  // has been exceeded.
   186  func HttpGetWithRetryWithCustomValidation(t testing.TestingT, url string, tlsConfig *tls.Config, retries int, sleepBetweenRetries time.Duration, validateResponse func(int, string) bool) {
   187  	options := HttpGetOptions{Url: url, TlsConfig: tlsConfig, Timeout: 10}
   188  	HttpGetWithRetryWithCustomValidationWithOptions(t, options, retries, sleepBetweenRetries, validateResponse)
   189  }
   190  
   191  // HttpGetWithRetryWithCustomValidationWithOptions repeatedly performs an HTTP GET on the given URL until the given validation function returns true or max retries
   192  // has been exceeded.
   193  func HttpGetWithRetryWithCustomValidationWithOptions(t testing.TestingT, options HttpGetOptions, retries int, sleepBetweenRetries time.Duration, validateResponse func(int, string) bool) {
   194  	err := HttpGetWithRetryWithCustomValidationWithOptionsE(t, options, retries, sleepBetweenRetries, validateResponse)
   195  	if err != nil {
   196  		t.Fatal(err)
   197  	}
   198  }
   199  
   200  // HttpGetWithRetryWithCustomValidationE repeatedly performs an HTTP GET on the given URL until the given validation function returns true or max retries
   201  // has been exceeded.
   202  func HttpGetWithRetryWithCustomValidationE(t testing.TestingT, url string, tlsConfig *tls.Config, retries int, sleepBetweenRetries time.Duration, validateResponse func(int, string) bool) error {
   203  	options := HttpGetOptions{Url: url, TlsConfig: tlsConfig, Timeout: 10}
   204  	return HttpGetWithRetryWithCustomValidationWithOptionsE(t, options, retries, sleepBetweenRetries, validateResponse)
   205  }
   206  
   207  // HttpGetWithRetryWithCustomValidationWithOptionsE repeatedly performs an HTTP GET on the given URL until the given validation function returns true or max retries
   208  // has been exceeded.
   209  func HttpGetWithRetryWithCustomValidationWithOptionsE(t testing.TestingT, options HttpGetOptions, retries int, sleepBetweenRetries time.Duration, validateResponse func(int, string) bool) error {
   210  	_, err := retry.DoWithRetryE(t, fmt.Sprintf("HTTP GET to URL %s", options.Url), retries, sleepBetweenRetries, func() (string, error) {
   211  		return "", HttpGetWithCustomValidationWithOptionsE(t, options, validateResponse)
   212  	})
   213  
   214  	return err
   215  }
   216  
   217  // HTTPDo performs the given HTTP method on the given URL and return the HTTP status code and body.
   218  // If there's any error, fail the test.
   219  func HTTPDo(
   220  	t testing.TestingT, method string, url string, body io.Reader,
   221  	headers map[string]string, tlsConfig *tls.Config,
   222  ) (int, string) {
   223  	options := HttpDoOptions{
   224  		Method:    method,
   225  		Url:       url,
   226  		Body:      body,
   227  		Headers:   headers,
   228  		TlsConfig: tlsConfig,
   229  		Timeout:   10}
   230  	return HTTPDoWithOptions(t, options)
   231  }
   232  
   233  // HTTPDoWithOptions performs the given HTTP method on the given URL and return the HTTP status code and body.
   234  // If there's any error, fail the test.
   235  func HTTPDoWithOptions(
   236  	t testing.TestingT, options HttpDoOptions,
   237  ) (int, string) {
   238  	statusCode, respBody, err := HTTPDoWithOptionsE(t, options)
   239  	if err != nil {
   240  		t.Fatal(err)
   241  	}
   242  	return statusCode, respBody
   243  }
   244  
   245  // HTTPDoE performs the given HTTP method on the given URL and return the HTTP status code, body, and any error.
   246  func HTTPDoE(
   247  	t testing.TestingT, method string, url string, body io.Reader,
   248  	headers map[string]string, tlsConfig *tls.Config,
   249  ) (int, string, error) {
   250  	options := HttpDoOptions{
   251  		Method:    method,
   252  		Url:       url,
   253  		Body:      body,
   254  		Headers:   headers,
   255  		Timeout:   10,
   256  		TlsConfig: tlsConfig}
   257  	return HTTPDoWithOptionsE(t, options)
   258  }
   259  
   260  // HTTPDoWithOptionsE performs the given HTTP method on the given URL and return the HTTP status code, body, and any error.
   261  func HTTPDoWithOptionsE(
   262  	t testing.TestingT, options HttpDoOptions,
   263  ) (int, string, error) {
   264  	logger.Logf(t, "Making an HTTP %s call to URL %s", options.Method, options.Url)
   265  
   266  	tr := &http.Transport{
   267  		TLSClientConfig: options.TlsConfig,
   268  	}
   269  
   270  	client := http.Client{
   271  		// By default, Go does not impose a timeout, so an HTTP connection attempt can hang for a LONG time.
   272  		Timeout:   time.Duration(options.Timeout) * time.Second,
   273  		Transport: tr,
   274  	}
   275  
   276  	req := newRequest(options.Method, options.Url, options.Body, options.Headers)
   277  	resp, err := client.Do(req)
   278  	if err != nil {
   279  		return -1, "", err
   280  	}
   281  
   282  	defer resp.Body.Close()
   283  	respBody, err := ioutil.ReadAll(resp.Body)
   284  
   285  	if err != nil {
   286  		return -1, "", err
   287  	}
   288  
   289  	return resp.StatusCode, strings.TrimSpace(string(respBody)), nil
   290  }
   291  
   292  // HTTPDoWithRetry repeatedly performs the given HTTP method on the given URL until the given status code and body are
   293  // returned or until max retries has been exceeded.
   294  // The function compares the expected status code against the received one and fails if they don't match.
   295  func HTTPDoWithRetry(
   296  	t testing.TestingT, method string, url string,
   297  	body []byte, headers map[string]string, expectedStatus int,
   298  	retries int, sleepBetweenRetries time.Duration, tlsConfig *tls.Config,
   299  ) string {
   300  	options := HttpDoOptions{
   301  		Method:    method,
   302  		Url:       url,
   303  		Body:      bytes.NewReader(body),
   304  		Headers:   headers,
   305  		TlsConfig: tlsConfig,
   306  		Timeout:   10}
   307  	return HTTPDoWithRetryWithOptions(t, options, expectedStatus, retries, sleepBetweenRetries)
   308  }
   309  
   310  // HTTPDoWithRetryWithOptions repeatedly performs the given HTTP method on the given URL until the given status code and body are
   311  // returned or until max retries has been exceeded.
   312  // The function compares the expected status code against the received one and fails if they don't match.
   313  func HTTPDoWithRetryWithOptions(
   314  	t testing.TestingT, options HttpDoOptions, expectedStatus int,
   315  	retries int, sleepBetweenRetries time.Duration,
   316  ) string {
   317  	out, err := HTTPDoWithRetryWithOptionsE(t, options, expectedStatus, retries, sleepBetweenRetries)
   318  	if err != nil {
   319  		t.Fatal(err)
   320  	}
   321  	return out
   322  }
   323  
   324  // HTTPDoWithRetryE repeatedly performs the given HTTP method on the given URL until the given status code and body are
   325  // returned or until max retries has been exceeded.
   326  // The function compares the expected status code against the received one and fails if they don't match.
   327  func HTTPDoWithRetryE(
   328  	t testing.TestingT, method string, url string,
   329  	body []byte, headers map[string]string, expectedStatus int,
   330  	retries int, sleepBetweenRetries time.Duration, tlsConfig *tls.Config,
   331  ) (string, error) {
   332  	options := HttpDoOptions{
   333  		Method:    method,
   334  		Url:       url,
   335  		Body:      bytes.NewReader(body),
   336  		Headers:   headers,
   337  		TlsConfig: tlsConfig,
   338  		Timeout:   10}
   339  
   340  	return HTTPDoWithRetryWithOptionsE(t, options, expectedStatus, retries, sleepBetweenRetries)
   341  }
   342  
   343  // HTTPDoWithRetryWithOptionsE repeatedly performs the given HTTP method on the given URL until the given status code and body are
   344  // returned or until max retries has been exceeded.
   345  // The function compares the expected status code against the received one and fails if they don't match.
   346  func HTTPDoWithRetryWithOptionsE(
   347  	t testing.TestingT, options HttpDoOptions, expectedStatus int,
   348  	retries int, sleepBetweenRetries time.Duration,
   349  ) (string, error) {
   350  	// The request body is closed after a request is complete.
   351  	// Extract the underlying data and cache it so we can reuse for retried requests
   352  	data, err := io.ReadAll(options.Body)
   353  	if err != nil {
   354  		return "", err
   355  	}
   356  
   357  	options.Body = nil
   358  
   359  	out, err := retry.DoWithRetryE(
   360  		t, fmt.Sprintf("HTTP %s to URL %s", options.Method, options.Url), retries,
   361  		sleepBetweenRetries, func() (string, error) {
   362  			options.Body = bytes.NewReader(data)
   363  			statusCode, out, err := HTTPDoWithOptionsE(t, options)
   364  			if err != nil {
   365  				return "", err
   366  			}
   367  			logger.Logf(t, "output: %v", out)
   368  			if statusCode != expectedStatus {
   369  				return "", ValidationFunctionFailed{Url: options.Url, Status: statusCode}
   370  			}
   371  			return out, nil
   372  		})
   373  
   374  	return out, err
   375  }
   376  
   377  // HTTPDoWithValidationRetry repeatedly performs the given HTTP method on the given URL until the given status code and
   378  // body are returned or until max retries has been exceeded.
   379  func HTTPDoWithValidationRetry(
   380  	t testing.TestingT, method string, url string,
   381  	body []byte, headers map[string]string, expectedStatus int,
   382  	expectedBody string, retries int, sleepBetweenRetries time.Duration, tlsConfig *tls.Config,
   383  ) {
   384  	options := HttpDoOptions{
   385  		Method:    method,
   386  		Url:       url,
   387  		Body:      bytes.NewReader(body),
   388  		Headers:   headers,
   389  		TlsConfig: tlsConfig,
   390  		Timeout:   10}
   391  
   392  	HTTPDoWithValidationRetryWithOptions(t, options, expectedStatus, expectedBody, retries, sleepBetweenRetries)
   393  }
   394  
   395  // HTTPDoWithValidationRetryWithOptions repeatedly performs the given HTTP method on the given URL until the given status code and
   396  // body are returned or until max retries has been exceeded.
   397  func HTTPDoWithValidationRetryWithOptions(
   398  	t testing.TestingT, options HttpDoOptions, expectedStatus int,
   399  	expectedBody string, retries int, sleepBetweenRetries time.Duration,
   400  ) {
   401  	err := HTTPDoWithValidationRetryWithOptionsE(t, options, expectedStatus, expectedBody, retries, sleepBetweenRetries)
   402  	if err != nil {
   403  		t.Fatal(err)
   404  	}
   405  }
   406  
   407  // HTTPDoWithValidationRetryE repeatedly performs the given HTTP method on the given URL until the given status code and
   408  // body are returned or until max retries has been exceeded.
   409  func HTTPDoWithValidationRetryE(
   410  	t testing.TestingT, method string, url string,
   411  	body []byte, headers map[string]string, expectedStatus int,
   412  	expectedBody string, retries int, sleepBetweenRetries time.Duration, tlsConfig *tls.Config,
   413  ) error {
   414  	options := HttpDoOptions{
   415  		Method:    method,
   416  		Url:       url,
   417  		Body:      bytes.NewReader(body),
   418  		Headers:   headers,
   419  		TlsConfig: tlsConfig,
   420  		Timeout:   10}
   421  
   422  	return HTTPDoWithValidationRetryWithOptionsE(t, options, expectedStatus, expectedBody, retries, sleepBetweenRetries)
   423  }
   424  
   425  // HTTPDoWithValidationRetryWithOptionsE repeatedly performs the given HTTP method on the given URL until the given status code and
   426  // body are returned or until max retries has been exceeded.
   427  func HTTPDoWithValidationRetryWithOptionsE(
   428  	t testing.TestingT, options HttpDoOptions, expectedStatus int,
   429  	expectedBody string, retries int, sleepBetweenRetries time.Duration,
   430  ) error {
   431  	_, err := retry.DoWithRetryE(t, fmt.Sprintf("HTTP %s to URL %s", options.Method, options.Url), retries,
   432  		sleepBetweenRetries, func() (string, error) {
   433  			return "", HTTPDoWithValidationWithOptionsE(t, options, expectedStatus, expectedBody)
   434  		})
   435  
   436  	return err
   437  }
   438  
   439  // HTTPDoWithValidation performs the given HTTP method on the given URL and verify that you get back the expected status
   440  // code and body. If either doesn't match, fail the test.
   441  func HTTPDoWithValidation(t testing.TestingT, method string, url string, body io.Reader, headers map[string]string, expectedStatusCode int, expectedBody string, tlsConfig *tls.Config) {
   442  	options := HttpDoOptions{
   443  		Method:    method,
   444  		Url:       url,
   445  		Body:      body,
   446  		Headers:   headers,
   447  		TlsConfig: tlsConfig,
   448  		Timeout:   10}
   449  
   450  	HTTPDoWithValidationWithOptions(t, options, expectedStatusCode, expectedBody)
   451  }
   452  
   453  // HTTPDoWithValidationWithOptions performs the given HTTP method on the given URL and verify that you get back the expected status
   454  // code and body. If either doesn't match, fail the test.
   455  func HTTPDoWithValidationWithOptions(t testing.TestingT, options HttpDoOptions, expectedStatusCode int, expectedBody string) {
   456  	err := HTTPDoWithValidationWithOptionsE(t, options, expectedStatusCode, expectedBody)
   457  	if err != nil {
   458  		t.Fatal(err)
   459  	}
   460  }
   461  
   462  // HTTPDoWithValidationE performs the given HTTP method on the given URL and verify that you get back the expected status
   463  // code and body. If either doesn't match, return an error.
   464  func HTTPDoWithValidationE(t testing.TestingT, method string, url string, body io.Reader, headers map[string]string, expectedStatusCode int, expectedBody string, tlsConfig *tls.Config) error {
   465  	options := HttpDoOptions{
   466  		Method:    method,
   467  		Url:       url,
   468  		Body:      body,
   469  		Headers:   headers,
   470  		TlsConfig: tlsConfig,
   471  		Timeout:   10}
   472  
   473  	return HTTPDoWithValidationWithOptionsE(t, options, expectedStatusCode, expectedBody)
   474  }
   475  
   476  // HTTPDoWithValidationWithOptionsE performs the given HTTP method on the given URL and verify that you get back the expected status
   477  // code and body. If either doesn't match, return an error.
   478  func HTTPDoWithValidationWithOptionsE(t testing.TestingT, options HttpDoOptions, expectedStatusCode int, expectedBody string) error {
   479  	return HTTPDoWithCustomValidationWithOptionsE(t, options, func(statusCode int, body string) bool {
   480  		return statusCode == expectedStatusCode && body == expectedBody
   481  	})
   482  }
   483  
   484  // HTTPDoWithCustomValidation performs the given HTTP method on the given URL and validate the returned status code and
   485  // body using the given function.
   486  func HTTPDoWithCustomValidation(t testing.TestingT, method string, url string, body io.Reader, headers map[string]string, validateResponse func(int, string) bool, tlsConfig *tls.Config) {
   487  	options := HttpDoOptions{
   488  		Method:    method,
   489  		Url:       url,
   490  		Body:      body,
   491  		Headers:   headers,
   492  		TlsConfig: tlsConfig,
   493  		Timeout:   10}
   494  
   495  	HTTPDoWithCustomValidationWithOptions(t, options, validateResponse)
   496  }
   497  
   498  // HTTPDoWithCustomValidationWithOptions performs the given HTTP method on the given URL and validate the returned status code and
   499  // body using the given function.
   500  func HTTPDoWithCustomValidationWithOptions(t testing.TestingT, options HttpDoOptions, validateResponse func(int, string) bool) {
   501  	err := HTTPDoWithCustomValidationWithOptionsE(t, options, validateResponse)
   502  	if err != nil {
   503  		t.Fatal(err)
   504  	}
   505  }
   506  
   507  // HTTPDoWithCustomValidationE performs the given HTTP method on the given URL and validate the returned status code and
   508  // body using the given function.
   509  func HTTPDoWithCustomValidationE(t testing.TestingT, method string, url string, body io.Reader, headers map[string]string, validateResponse func(int, string) bool, tlsConfig *tls.Config) error {
   510  	options := HttpDoOptions{
   511  		Method:    method,
   512  		Url:       url,
   513  		Body:      body,
   514  		Headers:   headers,
   515  		TlsConfig: tlsConfig,
   516  		Timeout:   10}
   517  
   518  	return HTTPDoWithCustomValidationWithOptionsE(t, options, validateResponse)
   519  }
   520  
   521  // HTTPDoWithCustomValidationWithOptionsE performs the given HTTP method on the given URL and validate the returned status code and
   522  // body using the given function.
   523  func HTTPDoWithCustomValidationWithOptionsE(t testing.TestingT, options HttpDoOptions, validateResponse func(int, string) bool) error {
   524  	statusCode, respBody, err := HTTPDoWithOptionsE(t, options)
   525  
   526  	if err != nil {
   527  		return err
   528  	}
   529  
   530  	if !validateResponse(statusCode, respBody) {
   531  		return ValidationFunctionFailed{Url: options.Url, Status: statusCode, Body: respBody}
   532  	}
   533  
   534  	return nil
   535  }
   536  
   537  func newRequest(method string, url string, body io.Reader, headers map[string]string) *http.Request {
   538  	req, err := http.NewRequest(method, url, body)
   539  	if err != nil {
   540  		return nil
   541  	}
   542  	for k, v := range headers {
   543  		switch k {
   544  		case "Host":
   545  			req.Host = v
   546  		default:
   547  			req.Header.Add(k, v)
   548  		}
   549  	}
   550  	return req
   551  }