func (c *Client) Put(t TestingT, urlStr string, body fmt.Stringer, expectedStatuses ...int) *Response {
	return c.Do(t, c.NewRequest(t, "PUT", urlStr, body), expectedStatuses...)
}
Put makes PUT request. See Do for more details.
package gophers
import (
	"fmt"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"github.com/fatih/color"
	"github.com/go-gophers/gophers/config"
)
var initColorsOnce sync.Once
func initColors() {
	initColorsOnce.Do(func() {
		if config.Default.NoColors {
			color.NoColor = true
		}
	})
}
const (
	defaultUserAgent = "github.com/go-gophers/gophers"
)
type Client struct {
	Base           url.URL
	HTTPClient     *http.Client
	DefaultHeaders http.Header
	DefaultCookies []http.Cookie
}
func NewClient(base url.URL) *Client {
	return &Client{
		Base:       base,
		HTTPClient: http.DefaultClient,
		DefaultHeaders: http.Header{
			"User-Agent": []string{defaultUserAgent},
		},
	}
}
func (c *Client) NewRequest(t TestingT, method string, urlStr string, body fmt.Stringer) *Request {
	initColors()
	r, err := http.NewRequest(method, urlStr, nil)
	if err != nil {
		t.Fatalf("can't create request: %s", err)
	}
	req := &Request{Request: r}
	req.SetBodyStringer(body)
	newURL := c.Base
	if strings.HasSuffix(newURL.Path, "/") && strings.HasPrefix(req.URL.Path, "/") {
		req.URL.Path = strings.TrimPrefix(req.URL.Path, "/")
	}
	newURL.Path += req.URL.Path
	q := newURL.Query()
	for k, vs := range req.URL.Query() {
		for _, v := range vs {
			q.Add(k, v)
		}
	}
	newURL.RawQuery = q.Encode()
	req.URL = &newURL
	for k, vs := range c.DefaultHeaders {
		for _, v := range vs {
			req.Header.Add(k, v)
		}
	}
	for _, c := range c.DefaultCookies {
		req.AddCookie(&c)
	}
	return req
}
func (c *Client) Do(t TestingT, req *Request, expectedStatuses ...int) *Response {
	status, headers, body, err := dumpRequest(req.Request)
	if err != nil {
		t.Fatalf("can't dump request: %s", err)
	}
	repr := bodyRepr(req.Header.Get("Content-Type"), body)
	colorF := func(b []byte) string { return color.BlueString("%s", string(b)) }
	if config.Default.Verbose {
		t.Logf("\n%s\n%s\n\n%s\n", colorF(status), colorF(headers), colorF(repr))
	} else {
		t.Logf("%s\n", colorF(status))
	}
	if req.Recorder != nil && req.RequestWC != nil {
		err = req.Recorder.RecordRequest(req.Request, status, headers, repr, req.RequestWC)
		if err != nil {
			t.Fatalf("can't record request: %s", err)
		}
		if f, ok := req.RequestWC.(*os.File); ok {
			t.Logf("request recorded to %s", f.Name())
		} else {
			t.Logf("request recorded")
		}
	}
	r, err := c.HTTPClient.Do(req.Request)
	if r != nil {
		origBody := r.Body
		defer func() {
			err = origBody.Close()
			if err != nil {
				t.Fatalf("can't close response body: %s", err)
			}
		}()
	}
	if err != nil {
		t.Fatalf("can't make request: %s", err)
	}
	req.Body = body
	req.Request.Body = errorReadCloser{}
	resp := &Response{Response: r}
	status, headers, body, err = dumpResponse(resp.Response)
	if err != nil {
		t.Fatalf("can't dump response: %s", err)
	}
	resp.Body = body
	resp.Response.Body = errorReadCloser{}
	repr = bodyRepr(resp.Header.Get("Content-Type"), body)
	switch {
	case resp.StatusCode >= 400:
		colorF = func(b []byte) string { return color.RedString("%s", string(b)) }
	case resp.StatusCode >= 300:
		colorF = func(b []byte) string { return color.YellowString("%s", string(b)) }
	default:
		colorF = func(b []byte) string { return color.GreenString("%s", string(b)) }
	}
	if config.Default.Verbose {
		t.Logf("\n%s\n%s\n\n%s\n", colorF(status), colorF(headers), colorF(repr))
	} else {
		t.Logf("%s\n", colorF(status))
	}
	if req.Recorder != nil && req.ResponseWC != nil {
		err = req.Recorder.RecordResponse(resp.Response, status, headers, repr, req.ResponseWC)
		if err != nil {
			t.Fatalf("can't record response: %s", err)
		}
		if f, ok := req.ResponseWC.(*os.File); ok {
			t.Logf("response recorded to %s", f.Name())
		} else {
			t.Logf("response recorded")
		}
	}
	if len(expectedStatuses) > 0 {
		var found bool
		for _, s := range expectedStatuses {
			if resp.StatusCode == s {
				found = true
				break
			}
		}
		if !found {
			t.Errorf("%s %s: expected status code to be in %v, got %s", req.Method, req.URL.String(), expectedStatuses, resp.Status)
		}
	}
	return resp
}
func (c *Client) Head(t TestingT, urlStr string, expectedStatuses ...int) *Response {
	return c.Do(t, c.NewRequest(t, "HEAD", urlStr, nil), expectedStatuses...)
}
func (c *Client) Get(t TestingT, urlStr string, expectedStatuses ...int) *Response {
	return c.Do(t, c.NewRequest(t, "GET", urlStr, nil), expectedStatuses...)
}
func (c *Client) Post(t TestingT, urlStr string, body fmt.Stringer, expectedStatuses ...int) *Response {
	return c.Do(t, c.NewRequest(t, "POST", urlStr, body), expectedStatuses...)
}