Skip to content

Commit 03e26c9

Browse files
author
Seth Ammons
authored
Merge pull request #35 from pritamsarkar007/master
cofigurable retries for HTTP 429
2 parents 38b020c + 64d8539 commit 03e26c9

2 files changed

Lines changed: 205 additions & 7 deletions

File tree

pester.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ type Client struct {
4848
wg *sync.WaitGroup
4949

5050
sync.Mutex
51-
ErrLog []ErrEntry
51+
ErrLog []ErrEntry
52+
RetryOnHTTP429 bool
5253
}
5354

5455
// ErrEntry is used to provide the LogString() data and is populated
@@ -93,11 +94,12 @@ func init() {
9394
// New constructs a new DefaultClient with sensible default values
9495
func New() *Client {
9596
return &Client{
96-
Concurrency: DefaultClient.Concurrency,
97-
MaxRetries: DefaultClient.MaxRetries,
98-
Backoff: DefaultClient.Backoff,
99-
ErrLog: DefaultClient.ErrLog,
100-
wg: &sync.WaitGroup{},
97+
Concurrency: DefaultClient.Concurrency,
98+
MaxRetries: DefaultClient.MaxRetries,
99+
Backoff: DefaultClient.Backoff,
100+
ErrLog: DefaultClient.ErrLog,
101+
wg: &sync.WaitGroup{},
102+
RetryOnHTTP429: false,
101103
}
102104
}
103105

@@ -279,7 +281,7 @@ func (c *Client) pester(p params) (*http.Response, error) {
279281
// Early return if we have a valid result
280282
// Only retry (ie, continue the loop) on 5xx status codes and 429
281283

282-
if err == nil && resp.StatusCode < 500 && resp.StatusCode != 429 {
284+
if err == nil && resp.StatusCode < 500 && (resp.StatusCode != 429 || (resp.StatusCode == 429 && !c.RetryOnHTTP429)) {
283285
multiplexCh <- result{resp: resp, err: err, req: n, retry: i}
284286
return
285287
}
@@ -422,6 +424,11 @@ func (c *Client) PostForm(url string, data url.Values) (resp *http.Response, err
422424
return c.pester(params{method: "PostForm", url: url, data: data, verb: "POST"})
423425
}
424426

427+
// set RetryOnHTTP429 for clients,
428+
func (c *Client) SetRetryOnHTTP429(flag bool) {
429+
c.RetryOnHTTP429 = flag
430+
}
431+
425432
////////////////////////////////////////
426433
// Provide self-constructing variants //
427434
////////////////////////////////////////

pester_test.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,76 @@ func TestConcurrentRequests(t *testing.T) {
4040
}
4141
}
4242

43+
func TestConcurrentRequestsWith429DefaultClient(t *testing.T) {
44+
t.Parallel()
45+
46+
c := New()
47+
c.Concurrency = 3
48+
c.KeepLog = true
49+
50+
port, err := serverWith429()
51+
if err != nil {
52+
t.Fatal("unable to start server", err)
53+
}
54+
55+
url := fmt.Sprintf("http://localhost:%d", port)
56+
57+
response, err := c.Get(url)
58+
if err != nil {
59+
t.Fatal("unable to GET", err)
60+
}
61+
c.Wait()
62+
63+
response.Body.Close()
64+
c.Wait()
65+
66+
// in the event of an error, let's see what the logs were
67+
t.Log("\n", c.LogString())
68+
69+
if got, want := c.LogErrCount(), 0; got != want {
70+
t.Errorf("got %d attempts, want %d", got, want)
71+
}
72+
}
73+
74+
func TestConcurrentRequestsWith400(t *testing.T) {
75+
t.Parallel()
76+
77+
c := New()
78+
c.Concurrency = 3
79+
c.KeepLog = true
80+
c.SetRetryOnHTTP429(true)
81+
82+
port, err := serverWith400()
83+
if err != nil {
84+
t.Fatal("unable to start server", err)
85+
}
86+
87+
url := fmt.Sprintf("http://localhost:%d", port)
88+
89+
response, err := c.Get(url)
90+
if err != nil {
91+
t.Fatal("unable to GET", err)
92+
}
93+
c.Wait()
94+
95+
response.Body.Close()
96+
c.Wait()
97+
98+
// in the event of an error, let's see what the logs were
99+
t.Log("\n", c.LogString())
100+
101+
if got, want := c.LogErrCount(), 0; got != want {
102+
t.Errorf("got %d attempts, want %d", got, want)
103+
}
104+
}
105+
43106
func TestConcurrentRequestsWith429(t *testing.T) {
44107
t.Parallel()
45108

46109
c := New()
47110
c.Concurrency = 3
48111
c.KeepLog = true
112+
c.SetRetryOnHTTP429(true)
49113

50114
port, err := serverWith429()
51115
if err != nil {
@@ -71,13 +135,79 @@ func TestConcurrentRequestsWith429(t *testing.T) {
71135
}
72136
}
73137

138+
func TestMaxRetriesConcurrentRequestsWith429DefaultClient(t *testing.T) {
139+
t.Parallel()
140+
141+
c := New()
142+
c.Concurrency = 3
143+
c.KeepLog = true
144+
c.MaxRetries = 5
145+
146+
port, err := serverWith429()
147+
if err != nil {
148+
t.Fatal("unable to start server", err)
149+
}
150+
151+
url := fmt.Sprintf("http://localhost:%d", port)
152+
153+
response, err := c.Get(url)
154+
if err != nil {
155+
t.Fatal("unable to GET", err)
156+
}
157+
c.Wait()
158+
159+
response.Body.Close()
160+
c.Wait()
161+
162+
// in the event of an error, let's see what the logs were
163+
t.Log("\n", c.LogString())
164+
165+
if got, want := c.LogErrCount(), 0; got != want {
166+
t.Errorf("got %d attempts, want %d", got, want)
167+
}
168+
}
169+
170+
func TestMaxRetriesConcurrentRequestsWith400(t *testing.T) {
171+
t.Parallel()
172+
173+
c := New()
174+
c.Concurrency = 3
175+
c.KeepLog = true
176+
c.MaxRetries = 5
177+
c.SetRetryOnHTTP429(true)
178+
179+
port, err := serverWith400()
180+
if err != nil {
181+
t.Fatal("unable to start server", err)
182+
}
183+
184+
url := fmt.Sprintf("http://localhost:%d", port)
185+
186+
response, err := c.Get(url)
187+
if err != nil {
188+
t.Fatal("unable to GET", err)
189+
}
190+
c.Wait()
191+
192+
response.Body.Close()
193+
c.Wait()
194+
195+
// in the event of an error, let's see what the logs were
196+
t.Log("\n", c.LogString())
197+
198+
if got, want := c.LogErrCount(), 0; got != want {
199+
t.Errorf("got %d attempts, want %d", got, want)
200+
}
201+
}
202+
74203
func TestMaxRetriesConcurrentRequestsWith429(t *testing.T) {
75204
t.Parallel()
76205

77206
c := New()
78207
c.Concurrency = 3
79208
c.KeepLog = true
80209
c.MaxRetries = 5
210+
c.SetRetryOnHTTP429(true)
81211

82212
port, err := serverWith429()
83213
if err != nil {
@@ -127,13 +257,44 @@ func TestConcurrent2Retry0(t *testing.T) {
127257
}
128258
}
129259

260+
func TestConcurrent2Retry0for429DefaultClient(t *testing.T) {
261+
t.Parallel()
262+
263+
c := New()
264+
c.Concurrency = 2
265+
c.MaxRetries = 0
266+
c.KeepLog = true
267+
268+
port, err := serverWith429()
269+
if err != nil {
270+
t.Fatal("unable to start server", err)
271+
}
272+
273+
url := fmt.Sprintf("http://localhost:%d", port)
274+
275+
_, getErr := c.Get(url)
276+
277+
if getErr != nil {
278+
t.Fatal("unable to GET", getErr)
279+
}
280+
c.Wait()
281+
282+
// in the event of an error, let's see what the logs were
283+
t.Log("\n", c.LogString())
284+
285+
if got, want := c.LogErrCount(), 0; got != want {
286+
t.Errorf("got %d attempts, want %d", got, want)
287+
}
288+
}
289+
130290
func TestConcurrent2Retry0for429(t *testing.T) {
131291
t.Parallel()
132292

133293
c := New()
134294
c.Concurrency = 2
135295
c.MaxRetries = 0
136296
c.KeepLog = true
297+
c.SetRetryOnHTTP429(true)
137298

138299
port, err := serverWith429()
139300
if err != nil {
@@ -629,3 +790,33 @@ func serverWith429() (int, error) {
629790

630791
return port, nil
631792
}
793+
794+
func serverWith400() (int, error) {
795+
mux := http.NewServeMux()
796+
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
797+
798+
w.WriteHeader(http.StatusBadRequest)
799+
w.Write([]byte("400 Bad Request"))
800+
})
801+
l, err := net.Listen("tcp", ":0")
802+
if err != nil {
803+
return -1, fmt.Errorf("unable to secure listener %v", err)
804+
}
805+
go func() {
806+
if err := http.Serve(l, mux); err != nil {
807+
log.Fatalf("slow-server error %v", err)
808+
}
809+
}()
810+
811+
var port int
812+
_, sport, err := net.SplitHostPort(l.Addr().String())
813+
if err == nil {
814+
port, err = strconv.Atoi(sport)
815+
}
816+
817+
if err != nil {
818+
return -1, fmt.Errorf("unable to determine port %v", err)
819+
}
820+
821+
return port, nil
822+
}

0 commit comments

Comments
 (0)