kubo/test/cli/harness/http_client.go
2023-01-17 21:27:54 +01:00

117 lines
2.8 KiB
Go

package harness
import (
"io"
"net/http"
"strings"
"text/template"
"time"
)
// HTTPClient is an HTTP client with some conveniences for testing.
// URLs are constructed from a base URL.
// The response body is buffered into a string.
// Internal errors cause panics so that tests don't need to check errors.
// The paths are evaluated as Go templates for readable string interpolation.
type HTTPClient struct {
Client *http.Client
BaseURL string
Timeout time.Duration
TemplateData any
}
type HTTPResponse struct {
Body string
StatusCode int
Headers http.Header
// The raw response. The body will be closed on this response.
Resp *http.Response
}
func (c *HTTPClient) WithHeader(k, v string) func(h *http.Request) {
return func(h *http.Request) {
h.Header.Add(k, v)
}
}
func (c *HTTPClient) DisableRedirects() *HTTPClient {
c.Client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
return c
}
// Do executes the request unchanged.
func (c *HTTPClient) Do(req *http.Request) *HTTPResponse {
log.Debugf("making HTTP req %s to %q with headers %+v", req.Method, req.URL.String(), req.Header)
resp, err := c.Client.Do(req)
if resp != nil && resp.Body != nil {
defer resp.Body.Close()
}
if err != nil {
panic(err)
}
bodyStr, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
return &HTTPResponse{
Body: string(bodyStr),
StatusCode: resp.StatusCode,
Headers: resp.Header,
Resp: resp,
}
}
// BuildURL constructs a request URL from the given path by interpolating the string and then appending it to the base URL.
func (c *HTTPClient) BuildURL(urlPath string) string {
sb := &strings.Builder{}
err := template.Must(template.New("test").Parse(urlPath)).Execute(sb, c.TemplateData)
if err != nil {
panic(err)
}
renderedPath := sb.String()
return c.BaseURL + renderedPath
}
func (c *HTTPClient) Get(urlPath string, opts ...func(*http.Request)) *HTTPResponse {
req, err := http.NewRequest(http.MethodGet, c.BuildURL(urlPath), nil)
if err != nil {
panic(err)
}
for _, o := range opts {
o(req)
}
return c.Do(req)
}
func (c *HTTPClient) Post(urlPath string, body io.Reader, opts ...func(*http.Request)) *HTTPResponse {
req, err := http.NewRequest(http.MethodPost, c.BuildURL(urlPath), body)
if err != nil {
panic(err)
}
for _, o := range opts {
o(req)
}
return c.Do(req)
}
func (c *HTTPClient) PostStr(urlpath, body string, opts ...func(*http.Request)) *HTTPResponse {
r := strings.NewReader(body)
return c.Post(urlpath, r, opts...)
}
func (c *HTTPClient) Head(urlPath string, opts ...func(*http.Request)) *HTTPResponse {
req, err := http.NewRequest(http.MethodHead, c.BuildURL(urlPath), nil)
if err != nil {
panic(err)
}
for _, o := range opts {
o(req)
}
return c.Do(req)
}