func (c *Client) Head(url string) (resp *http.Response, err error) {
	return c.pester(params{method: "Head", url: url, verb: "HEAD"})
}
Head provides the same functionality as http.Client.Head
package pester
import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"net/url"
	"sync"
	"time"
)
var ErrUnexpectedMethod = errors.New("unexpected client method, must be one of Do, Get, Head, Post, or PostFrom")
var ErrReadingBody = errors.New("error reading body")
var ErrReadingRequestBody = errors.New("error reading request body")
type Client struct {
	hc *http.Client
	Transport     http.RoundTripper
	CheckRedirect func(req *http.Request, via []*http.Request) error
	Jar           http.CookieJar
	Timeout       time.Duration
	Concurrency int
	MaxRetries  int
	Backoff     BackoffStrategy
	KeepLog     bool
	LogHook     LogHook
	SuccessReqNum   int
	SuccessRetryNum int
	wg *sync.WaitGroup
	sync.Mutex
	ErrLog         []ErrEntry
	RetryOnHTTP429 bool
}
type ErrEntry struct {
	Time    time.Time
	Method  string
	URL     string
	Verb    string
	Request int
	Retry   int
	Attempt int
	Err     error
}
type result struct {
	resp  *http.Response
	err   error
	req   int
	retry int
}
type params struct {
	method   string
	verb     string
	req      *http.Request
	url      string
	bodyType string
	body     io.Reader
	data     url.Values
}
var random *rand.Rand
func init() {
	random = rand.New(rand.NewSource(time.Now().UnixNano()))
}
func New() *Client {
	return &Client{
		Concurrency:    DefaultClient.Concurrency,
		MaxRetries:     DefaultClient.MaxRetries,
		Backoff:        DefaultClient.Backoff,
		ErrLog:         DefaultClient.ErrLog,
		wg:             &sync.WaitGroup{},
		RetryOnHTTP429: false,
	}
}
func NewExtendedClient(hc *http.Client) *Client {
	c := New()
	c.hc = hc
	return c
}
type LogHook func(e ErrEntry)
type BackoffStrategy func(retry int) time.Duration
var DefaultClient = &Client{Concurrency: 1, MaxRetries: 3, Backoff: DefaultBackoff, ErrLog: []ErrEntry{}}
func DefaultBackoff(_ int) time.Duration {
	return 1 * time.Second
}
func ExponentialBackoff(i int) time.Duration {
	return time.Duration(1<<uint(i)) * time.Second
}
func ExponentialJitterBackoff(i int) time.Duration {
	return jitter(int(1 << uint(i)))
}
func LinearBackoff(i int) time.Duration {
	return time.Duration(i) * time.Second
}
func LinearJitterBackoff(i int) time.Duration {
	return jitter(i)
}
func jitter(i int) time.Duration {
	ms := i * 1000
	maxJitter := ms / 3
	ms += random.Intn(2*maxJitter) - maxJitter
	if ms <= 0 {
		ms = 1
	}
	return time.Duration(ms) * time.Millisecond
}
func (c *Client) Wait() {
	c.wg.Wait()
}
func (c *Client) pester(p params) (*http.Response, error) {
	resultCh := make(chan result)
	multiplexCh := make(chan result)
	finishCh := make(chan struct{})
	totalSentRequests := &sync.WaitGroup{}
	totalSentRequests.Add(1)
	defer totalSentRequests.Done()
	allRequestsBackCh := make(chan struct{})
	go func() {
		totalSentRequests.Wait()
		close(allRequestsBackCh)
	}()
	concurrency := c.Concurrency
	if p.verb != "GET" {
		concurrency = 1
	}
	c.Lock()
	if c.hc == nil {
		c.hc = &http.Client{}
		c.hc.Transport = c.Transport
		c.hc.CheckRedirect = c.CheckRedirect
		c.hc.Jar = c.Jar
		c.hc.Timeout = c.Timeout
	}
	c.Unlock()
	httpClient := http.Client{
		Transport:     c.hc.Transport,
		CheckRedirect: c.hc.CheckRedirect,
		Jar:           c.hc.Jar,
		Timeout:       c.hc.Timeout,
	}
	var originalRequestBody []byte
	var originalBody []byte
	var err error
	if p.req != nil && p.req.Body != nil {
		originalRequestBody, err = ioutil.ReadAll(p.req.Body)
		if err != nil {
			return nil, ErrReadingRequestBody
		}
		p.req.Body.Close()
	}
	if p.body != nil {
		originalBody, err = ioutil.ReadAll(p.body)
		if err != nil {
			return nil, ErrReadingBody
		}
	}
	AttemptLimit := c.MaxRetries
	if AttemptLimit <= 0 {
		AttemptLimit = 1
	}
	for req := 0; req < concurrency; req++ {
		c.wg.Add(1)
		totalSentRequests.Add(1)
		go func(n int, p params) {
			defer c.wg.Done()
			defer totalSentRequests.Done()
			var err error
			for i := 1; i <= AttemptLimit; i++ {
				c.wg.Add(1)
				defer c.wg.Done()
				select {
				case <-finishCh:
					return
				default:
				}
				if len(originalRequestBody) > 0 {
					p.req.Body = ioutil.NopCloser(bytes.NewBuffer(originalRequestBody))
				}
				if len(originalBody) > 0 {
					p.body = bytes.NewBuffer(originalBody)
				}
				var resp *http.Response
				switch p.method {
				case "Do":
					resp, err = httpClient.Do(p.req)
				case "Get":
					resp, err = httpClient.Get(p.url)
				case "Head":
					resp, err = httpClient.Head(p.url)
				case "Post":
					resp, err = httpClient.Post(p.url, p.bodyType, p.body)
				case "PostForm":
					resp, err = httpClient.PostForm(p.url, p.data)
				default:
					err = ErrUnexpectedMethod
				}
				if err == nil && resp.StatusCode < 500 && (resp.StatusCode != 429 || (resp.StatusCode == 429 && !c.RetryOnHTTP429)) {
					multiplexCh <- result{resp: resp, err: err, req: n, retry: i}
					return
				}
				c.log(ErrEntry{
					Time:    time.Now(),
					Method:  p.method,
					Verb:    p.verb,
					URL:     p.url,
					Request: n,
					Retry:   i + 1,  
					Attempt: i,
					Err:     err,
				})
				if i == AttemptLimit {
					multiplexCh <- result{resp: resp, err: err}
					return
				}
				if p.req != nil {
					ctx := p.req.Context()
					select {
					case <-ctx.Done():
						multiplexCh <- result{resp: resp, err: ctx.Err()}
						return
					default:
					}
				}
				if resp != nil {
					resp.Body.Close()
				}
				<-time.After(c.Backoff(i) + 1*time.Microsecond)
			}
		}(req, p)
	}
	go func() {
		gotFirstResult := false
		for {
			select {
			case res := <-multiplexCh:
				if !gotFirstResult {
					gotFirstResult = true
					close(finishCh)
					resultCh <- res
				} else if res.resp != nil {
					io.Copy(ioutil.Discard, res.resp.Body)
					res.resp.Body.Close()
				}
			case <-allRequestsBackCh:
				return
			}
		}
	}()
	res := <-resultCh
	c.Lock()
	defer c.Unlock()
	c.SuccessReqNum = res.req
	c.SuccessRetryNum = res.retry
	return res.resp, res.err
}
func (c *Client) LogString() string {
	c.Lock()
	defer c.Unlock()
	var res string
	for _, e := range c.ErrLog {
		res += c.FormatError(e)
	}
	return res
}
func (c *Client) FormatError(e ErrEntry) string {
	return fmt.Sprintf("%d %s [%s] %s request-%d retry-%d error: %s\n",
		e.Time.Unix(), e.Method, e.Verb, e.URL, e.Request, e.Retry, e.Err)
}
func (c *Client) LogErrCount() int {
	c.Lock()
	defer c.Unlock()
	return len(c.ErrLog)
}
func (c *Client) EmbedHTTPClient(hc *http.Client) {
	c.hc = hc
}
func (c *Client) log(e ErrEntry) {
	if c.KeepLog {
		c.Lock()
		defer c.Unlock()
		c.ErrLog = append(c.ErrLog, e)
	} else if c.LogHook != nil {
		c.LogHook(e)
	}
}
func (c *Client) Do(req *http.Request) (resp *http.Response, err error) {
	return c.pester(params{method: "Do", req: req, verb: req.Method, url: req.URL.String()})
}
func (c *Client) Get(url string) (resp *http.Response, err error) {
	return c.pester(params{method: "Get", url: url, verb: "GET"})
}