Skip to content

Commit 6147653

Browse files
committed
fix: add max retries reached
1 parent 8c2f4ff commit 6147653

File tree

3 files changed

+85
-52
lines changed

3 files changed

+85
-52
lines changed

out.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
http://localhost:8080/once-429

runner/runner.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,14 +1364,17 @@ func (r *Runner) retryLoop(
13641364
res := analyze(j.hp, j.protocol, j.target, j.method, j.origInput, j.scanopts)
13651365
output <- res
13661366

1367-
if res.StatusCode == http.StatusTooManyRequests &&
1368-
j.attempt < r.options.RetryRounds {
1367+
if res.StatusCode == http.StatusTooManyRequests {
1368+
if j.attempt >= r.options.RetryRounds {
1369+
return
1370+
}
13691371

13701372
j.attempt++
13711373
j.when = time.Now().Add(time.Duration(r.options.RetryDelay) * time.Millisecond)
13721374

13731375
select {
13741376
case <-ctx.Done():
1377+
return
13751378
case ch <- j:
13761379
}
13771380
}

runner/runner_test.go

Lines changed: 79 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,21 @@ package runner
33
import (
44
"context"
55
"fmt"
6+
"log"
67
"net/http"
8+
"net/http/httptest"
79
"os"
810
"strings"
11+
"sync"
12+
"sync/atomic"
913
"testing"
1014
"time"
1115

1216
_ "github.com/projectdiscovery/fdmax/autofdmax"
1317
"github.com/projectdiscovery/httpx/common/httpx"
1418
"github.com/projectdiscovery/mapcidr/asn"
1519
stringsutil "github.com/projectdiscovery/utils/strings"
20+
syncutil "github.com/projectdiscovery/utils/sync"
1621
"github.com/stretchr/testify/require"
1722
)
1823

@@ -315,70 +320,94 @@ func TestCreateNetworkpolicyInstance_AllowDenyFlags(t *testing.T) {
315320
}
316321
}
317322

318-
func TestRunner_RetryLoop(t *testing.T) {
319-
retryCh := make(chan retryJob)
320-
out := make(chan Result)
323+
func TestRunner_Process_And_RetryLoop(t *testing.T) {
324+
var hits1, hits2 int32
325+
srv1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
326+
if atomic.AddInt32(&hits1, 1) != 4 {
327+
log.Println("serv1 429")
328+
w.WriteHeader(http.StatusTooManyRequests)
329+
return
330+
}
331+
log.Println("serv1 200")
332+
w.WriteHeader(http.StatusOK)
333+
}))
334+
defer srv1.Close()
335+
336+
srv2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
337+
if atomic.AddInt32(&hits2, 1) != 3 {
338+
log.Println("serv2 429")
339+
w.WriteHeader(http.StatusTooManyRequests)
340+
return
341+
}
342+
log.Println("serv2 200")
343+
w.WriteHeader(http.StatusOK)
344+
}))
345+
defer srv2.Close()
321346

322347
r, err := New(&Options{
323-
RetryDelay: 500,
348+
Threads: 1,
349+
Delay: 0,
324350
RetryRounds: 3,
351+
RetryDelay: 200, // Duration 권장
352+
Timeout: 2,
325353
})
326-
require.Nil(t, err, "could not create httpx runner")
354+
require.NoError(t, err)
327355

328-
var calls = map[string]int{}
329-
analyze := func(hp *httpx.HTTPX,
330-
protocol string,
331-
target httpx.Target,
332-
method, origInput string,
333-
scanopts *ScanOptions) Result {
334-
calls[method]++
335-
if strings.HasPrefix(method, "retry-") && calls[method] == 1 {
336-
return Result{StatusCode: http.StatusTooManyRequests}
337-
}
338-
return Result{StatusCode: http.StatusOK}
339-
}
356+
output := make(chan Result)
357+
retryCh := make(chan retryJob)
340358

341-
cancel, wait := r.retryLoop(context.Background(), retryCh, out, analyze)
359+
// ctx, timeout := context.WithTimeout(context.Background(), time.Duration(r.options.Timeout))
360+
// defer timeout()
361+
cancel, wait := r.retryLoop(context.Background(), retryCh, output, r.analyze)
342362

343-
seed := []retryJob{
344-
{method: "ok-a", when: time.Now().Add(-time.Millisecond), attempt: 1},
345-
{method: "retry-a", when: time.Now().Add(-time.Millisecond), attempt: 1},
346-
{method: "ok-b", when: time.Now().Add(-time.Millisecond), attempt: 1},
347-
{method: "retry-b", when: time.Now().Add(-time.Millisecond), attempt: 1},
348-
}
349-
for _, j := range seed {
350-
retryCh <- j
351-
}
363+
wg, _ := syncutil.New(syncutil.WithSize(r.options.Threads))
364+
so := r.scanopts.Clone()
365+
so.Methods = []string{"GET"}
366+
so.TLSProbe = false
367+
so.CSPProbe = false
352368

353-
want := 6
354-
got := make([]Result, 0, want)
355-
deadline := time.After(2 * time.Second)
369+
seed := map[string]string{
370+
"srv1": srv1.URL,
371+
"srv2": srv2.URL,
372+
}
356373

357-
for len(got) < want {
358-
select {
359-
case r := <-out:
360-
got = append(got, r)
361-
case <-deadline:
362-
t.Errorf("timed out waiting results: got=%d want=%d", len(got), want)
374+
var drainWG sync.WaitGroup
375+
drainWG.Add(1)
376+
var s1n429, s1n200, s2n429, s2n200 int
377+
go func(output chan Result) {
378+
defer drainWG.Done()
379+
for res := range output {
380+
switch res.StatusCode {
381+
case http.StatusTooManyRequests:
382+
if res.URL == srv1.URL {
383+
s1n429++
384+
} else {
385+
s2n429++
386+
}
387+
case http.StatusOK:
388+
if res.URL == srv1.URL {
389+
s1n200++
390+
} else {
391+
s2n200++
392+
}
393+
}
363394
}
395+
}(output)
396+
397+
for _, url := range seed {
398+
r.process(url, wg, r.hp, httpx.HTTP, so, output, retryCh)
364399
}
365400

401+
wg.Wait()
366402
wait()
367403
cancel()
368-
close(retryCh)
369404

370-
close(out)
371-
372-
var n429, n200 int
373-
for _, r := range got {
374-
switch r.StatusCode {
375-
case http.StatusTooManyRequests:
376-
n429++
377-
case http.StatusOK:
378-
n200++
379-
}
380-
}
405+
close(retryCh)
406+
close(output)
407+
drainWG.Wait()
381408

382-
require.GreaterOrEqual(t, n429, 2)
383-
require.Equal(t, 4, n200)
409+
require.Equal(t, 3, s1n429)
410+
require.Equal(t, 1, s1n200)
411+
require.Equal(t, 2, s2n429)
412+
require.Equal(t, 1, s2n200)
384413
}

0 commit comments

Comments
 (0)