Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a retry policy to retry for HTTP Error codes >= 500 and refactor #26

Merged
merged 2 commits into from
Nov 15, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 14 additions & 20 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ var DefaultOptionsSingle = Options{
KillIdleConn: false,
}

// NewClient creates a new Client with default settings.
func NewClient(options Options) *Client {
func createClient(options Options, retrypolicy CheckRetry, backoff Backoff) *Client {
httpclient := DefaultClient()
httpclient2 := DefaultClient()
if err := http2.ConfigureTransport(httpclient2.Transport.(*http.Transport)); err != nil {
Expand All @@ -92,33 +91,28 @@ func NewClient(options Options) *Client {
c := &Client{
HTTPClient: httpclient,
HTTPClient2: httpclient2,
CheckRetry: DefaultRetryPolicy(),
Backoff: DefaultBackoff(),
CheckRetry: retrypolicy,
Backoff: backoff,
options: options,
}

c.setKillIdleConnections()
return c
}

// NewWithHTTPClient creates a new Client with default settings and provided http.Client
func NewWithHTTPClient(client *http.Client, options Options) *Client {
httpclient2 := DefaultClient()
httpclient2.Transport = client.Transport.(*http.Transport).Clone()
if err := http2.ConfigureTransport(httpclient2.Transport.(*http.Transport)); err != nil {
return nil
}
c := &Client{
HTTPClient: client,
HTTPClient2: httpclient2,
CheckRetry: DefaultRetryPolicy(),
Backoff: DefaultBackoff(),
// NewClient creates a new Client with default settings.
func NewClient(options Options) *Client {
return createClient(options, DefaultRetryPolicy(), DefaultBackoff())
}

options: options,
}
// NewClient creates a new Client with HTTPErrorRetryPolicy settings.
func NewClientHTTPRetryPolicy(options Options) *Client {
return createClient(options, HTTPErrorRetryPolicy(), DefaultBackoff())
}

c.setKillIdleConnections()
return c
// NewWithHTTPClient creates a new Client with default settings and provided http.Client
func NewWithHTTPClient(client *http.Client, options Options) *Client {
return createClient(options, DefaultRetryPolicy(), DefaultBackoff())
}

// setKillIdleConnections sets the kill idle conns switch in two scenarios
Expand Down
82 changes: 34 additions & 48 deletions retry.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package retryablehttp
import (
"context"
"crypto/x509"
"errors"
"net/http"
"net/url"
"regexp"
Expand Down Expand Up @@ -34,69 +35,54 @@ type CheckRetry func(ctx context.Context, resp *http.Response, err error) (bool,
// will retry on connection errors and server errors.
func DefaultRetryPolicy() func(ctx context.Context, resp *http.Response, err error) (bool, error) {
return func(ctx context.Context, resp *http.Response, err error) (bool, error) {
// do not retry on context.Canceled or context.DeadlineExceeded
//fmt.Printf("jkajsuiohsd %v\n", ctx.Err())
if ctx.Err() != nil {
return false, ctx.Err()
}

if err != nil {
if v, ok := err.(*url.Error); ok {
// Don't retry if the error was due to too many redirects.
if redirectsErrorRegex.MatchString(v.Error()) {
return false, nil
}

// Don't retry if the error was due to an invalid protocol scheme.
if schemeErrorRegex.MatchString(v.Error()) {
return false, nil
}

// Don't retry if the error was due to TLS cert verification failure.
if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
return false, nil
}
}
return checkErrors(ctx, resp, err)
}
}

// The error is likely recoverable so retry.
return true, nil
// HTTPErrorRetryPolicy is to retry for HTTPCodes >= 500.
func HTTPErrorRetryPolicy() func(ctx context.Context, resp *http.Response, err error) (bool, error) {
return func(ctx context.Context, resp *http.Response, err error) (bool, error) {
if resp.StatusCode >= 500 {
return true, errors.New(resp.Status)
}

return false, nil
return checkErrors(ctx, resp, err)
}
}

// HostSprayRetryPolicy provides a callback for Client.CheckRetry, which
// will retry on connection errors and server errors.
func HostSprayRetryPolicy() func(ctx context.Context, resp *http.Response, err error) (bool, error) {
return func(ctx context.Context, resp *http.Response, err error) (bool, error) {
// do not retry on context.Canceled or context.DeadlineExceeded
if ctx.Err() != nil {
return false, ctx.Err()
}
return checkErrors(ctx, resp, err)
}
}

if err != nil {
if v, ok := err.(*url.Error); ok {
// Don't retry if the error was due to too many redirects.
if redirectsErrorRegex.MatchString(v.Error()) {
return false, nil
}
func checkErrors(ctx context.Context, resp *http.Response, err error) (bool, error) {
// do not retry on context.Canceled or context.DeadlineExceeded
if ctx.Err() != nil {
return false, ctx.Err()
}

// Don't retry if the error was due to an invalid protocol scheme.
if schemeErrorRegex.MatchString(v.Error()) {
return false, nil
}
if err != nil {
if v, ok := err.(*url.Error); ok {
// Don't retry if the error was due to too many redirects.
if redirectsErrorRegex.MatchString(v.Error()) {
return false, nil
}

// Don't retry if the error was due to TLS cert verification failure.
if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
return false, nil
}
// Don't retry if the error was due to an invalid protocol scheme.
if schemeErrorRegex.MatchString(v.Error()) {
return false, nil
}

// The error is likely recoverable so retry.
return true, nil
// Don't retry if the error was due to TLS cert verification failure.
if _, ok := v.Err.(x509.UnknownAuthorityError); ok {
return false, nil
}
}

return false, nil
// The error is likely recoverable so retry.
return true, nil
}
return false, nil
}