Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
28 changes: 19 additions & 9 deletions runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ type Runner struct {
hm *hybrid.HybridMap
excludeCdn bool
stats clistats.StatisticsClient
ratelimiter ratelimit.Limiter
ratelimiter *ratelimit.Limiter
HostErrorsCache gcache.Cache[string, int]
browser *Browser
ditClassifier *dit.Classifier
Expand Down Expand Up @@ -416,11 +416,11 @@ func New(options *Options) (*Runner, error) {
runner.hm = hm

if options.RateLimitMinute > 0 {
runner.ratelimiter = *ratelimit.New(context.Background(), uint(options.RateLimitMinute), time.Minute)
runner.ratelimiter = ratelimit.New(context.Background(), uint(options.RateLimitMinute), time.Minute)
} else if options.RateLimit > 0 {
runner.ratelimiter = *ratelimit.New(context.Background(), uint(options.RateLimit), time.Second)
runner.ratelimiter = ratelimit.New(context.Background(), uint(options.RateLimit), time.Second)
} else {
runner.ratelimiter = *ratelimit.NewUnlimited(context.Background())
runner.ratelimiter = ratelimit.NewUnlimited(context.Background())
}

if options.HostMaxErrors >= 0 {
Expand Down Expand Up @@ -917,7 +917,9 @@ func (r *Runner) Close() {
// nolint:errcheck // ignore
r.hm.Close()
r.hp.Dialer.Close()
r.ratelimiter.Stop()
if r.ratelimiter != nil {
r.ratelimiter.Stop()
}

if r.options.HostMaxErrors >= 0 {
r.HostErrorsCache.Purge()
Expand Down Expand Up @@ -1927,7 +1929,9 @@ retry:
req.Body = nil
}

r.ratelimiter.Take()
if r.ratelimiter != nil {
r.ratelimiter.Take()
}

// with rawhttp we should say to the server to close the connection, otherwise it will remain open
if scanopts.Unsafe {
Expand Down Expand Up @@ -2217,7 +2221,9 @@ retry:
// check for virtual host
isvhost := false
if scanopts.VHost {
r.ratelimiter.Take()
if r.ratelimiter != nil {
r.ratelimiter.Take()
}
isvhost, _ = hp.IsVirtualHost(req, httpx.UnsafeOptions{})
if isvhost {
builder.WriteString(" [vhost]")
Expand All @@ -2238,7 +2244,9 @@ retry:
port = p
}
}
r.ratelimiter.Take()
if r.ratelimiter != nil {
r.ratelimiter.Take()
}
pipeline = hp.SupportPipeline(protocol, method, URL.Host, port)
if pipeline {
builder.WriteString(" [pipeline]")
Expand All @@ -2251,7 +2259,9 @@ retry:
var http2 bool
// if requested probes for http2
if scanopts.HTTP2Probe {
r.ratelimiter.Take()
if r.ratelimiter != nil {
r.ratelimiter.Take()
}
http2 = hp.SupportHTTP2(protocol, method, URL.String())
if http2 {
builder.WriteString(" [http2]")
Expand Down
63 changes: 63 additions & 0 deletions runner/runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1166,3 +1166,66 @@ func TestHandshakeThenCloseKeepsPlainResult(t *testing.T) {
require.EqualValues(t, 1, atomic.LoadInt64(&tlsHandshakes),
"the failed HTTPS request must complete its TLS handshake first")
}

func TestRunner_RateLimiter_Initialization(t *testing.T) {
t.Run("unlimited rate limiter initialization and concurrency", func(t *testing.T) {
const numGoroutines = 10
var wg sync.WaitGroup
wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func() {
defer wg.Done()
r, err := New(&Options{})
require.NoError(t, err)
require.NotNil(t, r.ratelimiter)
r.ratelimiter.Take()
r.Close()
}()
}
wg.Wait()
})

t.Run("per second rate limiter initialization and concurrency", func(t *testing.T) {
const numGoroutines = 10
var wg sync.WaitGroup
wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func() {
defer wg.Done()
r, err := New(&Options{RateLimit: 100})
require.NoError(t, err)
require.NotNil(t, r.ratelimiter)
r.ratelimiter.Take()
r.Close()
}()
}
wg.Wait()
})

t.Run("per minute rate limiter initialization and concurrency", func(t *testing.T) {
const numGoroutines = 10
var wg sync.WaitGroup
wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func() {
defer wg.Done()
r, err := New(&Options{RateLimitMinute: 600})
require.NoError(t, err)
require.NotNil(t, r.ratelimiter)
r.ratelimiter.Take()
r.Close()
}()
}
wg.Wait()
})

t.Run("close on nil ratelimiter does not panic", func(t *testing.T) {
r, err := New(&Options{})
require.NoError(t, err)
r.ratelimiter = nil
require.NotPanics(t, func() {
r.Close()
})
})
}

Loading