pkg/data_http.go (70 lines of code) (raw):
package pkg
import (
"crypto/tls"
"fmt"
"github.com/Azure/golden"
"io"
"log"
"net/http"
"net/url"
"os"
"strings"
"github.com/hashicorp/go-retryablehttp"
"golang.org/x/net/http/httpproxy"
)
var _ Data = &HttpDatasource{}
type HttpDatasource struct {
*golden.BaseBlock
*BaseData
Url string `hcl:"url"`
Method string `hcl:"method,optional" default:"GET" validate:"oneof=GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATCH"`
RequestBody string `hcl:"request_body,optional"`
RequestHeaders map[string]string `hcl:"request_headers,optional"`
RetryMax int `hcl:"retry_max,optional" default:"4"`
ResponseBody string `attribute:"response_body"`
ResponseHeaders map[string]string `attribute:"response_headers"`
StatusCode int `attribute:"status_code"`
}
func (h *HttpDatasource) ExecuteDuringPlan() error {
tr, ok := http.DefaultTransport.(*http.Transport)
if !ok {
return fmt.Errorf("error http: can't configure http transport")
}
clonedTr := tr.Clone()
// Prevent issues with tests caching the proxy configuration.
clonedTr.Proxy = func(req *http.Request) (*url.URL, error) {
return httpproxy.FromEnvironment().ProxyFunc()(req.URL)
}
if clonedTr.TLSClientConfig == nil {
clonedTr.TLSClientConfig = &tls.Config{}
}
retryClient := retryablehttp.NewClient()
retryClient.Logger = log.New(os.Stderr, fmt.Sprintf("%s:", h.Address()), log.LstdFlags)
retryClient.HTTPClient.Transport = clonedTr
retryClient.RetryMax = h.RetryMax
request, err := retryablehttp.NewRequestWithContext(h.Context(), h.Method, h.Url, strings.NewReader(h.RequestBody))
if err != nil {
return fmt.Errorf("error creating request %s, %s", h.Address(), err.Error())
}
for k, v := range h.RequestHeaders {
request.Header.Set(k, v)
}
response, err := retryClient.Do(request)
if err != nil {
return fmt.Errorf("error making request %s, detail: %s", h.Address(), err.Error())
}
defer func() { _ = response.Body.Close() }()
bytes, err := io.ReadAll(response.Body)
if err != nil {
return fmt.Errorf("error reading response body %s, detail: %s", h.Address(), err.Error())
}
h.ResponseBody = string(bytes)
h.ResponseHeaders = make(map[string]string)
for k, v := range response.Header {
// Concatenate according to RFC9110 https://www.rfc-editor.org/rfc/rfc9110.html#section-5.2
h.ResponseHeaders[k] = strings.Join(v, ", ")
}
h.StatusCode = response.StatusCode
return nil
}
func (h *HttpDatasource) Type() string {
return "http"
}