func (f *Fetcher) Fetch(ctx context.Context, url *URL) (*Page, error) {
	var maxAttempts = f.maxAttempts()
	var attempt int
	var resp *http.Response
	var err error
	for {
		if attempt++; attempt > maxAttempts {
			return nil, fmt.Errorf(
				"ant: max attempts of %d reached - %w",
				maxAttempts,
				err,
			)
		}
		if resp, err = f.fetch(ctx, url); err == nil {
			break
		}
		f.discard(resp)
		if isTemporary(err) {
			if err := f.backoff(ctx, attempt); err != nil {
				return nil, err
			}
			continue
		}
		if err, ok := err.(*FetchError); ok {
			if err.Status == 404 {
				return nil, nil
			}
		}
		return nil, err
	}
	return &Page{
		URL:    resp.Request.URL,
		Header: resp.Header,
		body:   resp.Body,
	}, nil
}
Fetch fetches a page by URL.

The method uses the configured client to make a new request
parse the response and return a page.

The method returns a nil page and nil error when the status
code is 404.

The will retry the request when the status code is temporary
or when a temporary network error occures.

The returned page contains the response's body, the body must
be read until EOF and closed so that the client can re-use the
underlying TCP connection.
package ant
import (
	"context"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"
)
type StaticAgent string
func (sa StaticAgent) String() string {
	return string(sa)
}
var (
	UserAgent = StaticAgent("antbot")
	DefaultFetcher = &Fetcher{
		Client:    DefaultClient,
		UserAgent: UserAgent,
	}
	minBackoff = 50 * time.Millisecond
	maxBackoff = 1 * time.Second
)
type FetchError struct {
	URL    *url.URL
	Status int
}
func (err FetchError) Error() string {
	return fmt.Sprintf("ant: fetch %q - %d %s",
		err.URL,
		err.Status,
		http.StatusText(err.Status),
	)
}
func (err FetchError) Temporary() bool {
	return err.Status == 503 ||  
		err.Status == 504 ||  
		err.Status == 429  
}
func Fetch(ctx context.Context, rawurl string) (*Page, error) {
	u, err := url.Parse(rawurl)
	if err != nil {
		return nil, err
	}
	return DefaultFetcher.Fetch(ctx, u)
}
type Fetcher struct {
	Client Client
	UserAgent fmt.Stringer
	MaxAttempts int
	MinBackoff time.Duration
	MaxBackoff time.Duration
}