nuclei/v2/internal/progress/progress.go

185 lines
4.9 KiB
Go
Raw Normal View History

2020-07-04 21:00:11 +00:00
package progress
import (
"fmt"
"github.com/logrusorgru/aurora"
"github.com/vbauerster/mpb/v5"
"github.com/vbauerster/mpb/v5/cwriter"
2020-07-04 21:00:11 +00:00
"github.com/vbauerster/mpb/v5/decor"
"io"
2020-07-04 21:00:11 +00:00
"os"
"regexp"
"strconv"
2020-07-04 21:00:11 +00:00
"strings"
"sync"
)
2020-07-11 20:57:44 +00:00
// Encapsulates progress tracking.
2020-07-04 21:00:11 +00:00
type Progress struct {
progress *mpb.Progress
2020-07-25 21:13:58 +00:00
gbar *mpb.Bar
2020-07-26 22:00:06 +00:00
total int64
initialTotal int64
totalMutex *sync.Mutex
2020-07-09 19:20:00 +00:00
captureData *captureData
stdCaptureMutex *sync.Mutex
stdout *strings.Builder
stderr *strings.Builder
colorizer aurora.Aurora
termWidth int
2020-07-04 21:00:11 +00:00
}
2020-07-11 20:57:44 +00:00
// Creates and returns a new progress tracking object.
func NewProgress(noColor bool) *Progress {
w := cwriter.New(os.Stdout)
tw, err := w.GetWidth()
if err != nil {
panic("Couldn't determine available terminal width.")
}
2020-07-04 21:00:11 +00:00
p := &Progress{
progress: mpb.New(
mpb.WithOutput(os.Stderr),
mpb.PopCompletedMode(),
),
2020-07-26 22:00:06 +00:00
totalMutex: &sync.Mutex{},
2020-07-09 19:20:00 +00:00
stdCaptureMutex: &sync.Mutex{},
stdout: &strings.Builder{},
stderr: &strings.Builder{},
colorizer: aurora.NewAurora(!noColor),
termWidth: tw,
2020-07-04 21:00:11 +00:00
}
return p
}
2020-07-11 20:57:44 +00:00
// Creates and returns a progress bar that tracks all the requests progress.
// This is only useful when multiple templates are processed within the same run.
2020-07-26 13:10:03 +00:00
func (p *Progress) InitProgressbar(hostCount int64, templateCount int, requestCount int64) {
if p.gbar != nil {
panic("A global progressbar is already present.")
}
color := p.colorizer
barName := color.Sprintf(
2020-07-25 17:58:17 +00:00
color.Cyan("%d %s, %d %s"),
color.Bold(color.Cyan(templateCount)),
2020-07-25 17:58:17 +00:00
pluralize(int64(templateCount), "template", "templates"),
color.Bold(color.Cyan(hostCount)),
2020-07-25 17:58:17 +00:00
pluralize(hostCount, "host", "hosts"))
2020-07-11 20:57:44 +00:00
2020-07-26 13:10:03 +00:00
p.gbar = p.setupProgressbar("["+barName+"]", requestCount, 0)
2020-07-11 20:57:44 +00:00
}
func pluralize(count int64, singular, plural string) string {
if count > 1 {
return plural
}
return singular
}
2020-07-26 22:00:06 +00:00
// Update total progress request count
func (p *Progress) AddToTotal(delta int64) {
p.totalMutex.Lock()
p.total += delta
p.gbar.SetTotal(p.total, false)
p.totalMutex.Unlock()
}
2020-07-11 20:57:44 +00:00
// Update progress tracking information and increments the request counter by one unit.
2020-07-26 13:10:03 +00:00
func (p *Progress) Update() {
p.gbar.Increment()
2020-07-04 21:00:11 +00:00
}
2020-07-11 20:57:44 +00:00
// Drops the specified number of requests from the progress bar total.
// This may be the case when uncompleted requests are encountered and shouldn't be part of the total count.
2020-07-26 13:10:03 +00:00
func (p *Progress) Drop(count int64) {
// mimic dropping by incrementing the completed requests
p.gbar.IncrInt64(count)
2020-07-26 22:00:06 +00:00
}
2020-07-11 20:57:44 +00:00
// Ensures that a progress bar's total count is up-to-date if during an enumeration there were uncompleted requests and
// wait for all the progress bars to finish.
2020-07-04 21:00:11 +00:00
func (p *Progress) Wait() {
2020-07-26 22:00:06 +00:00
p.totalMutex.Lock()
2020-07-27 18:39:13 +00:00
if p.total == 0 {
p.gbar.Abort(true)
} else if p.initialTotal != p.total {
2020-07-26 22:00:06 +00:00
p.gbar.SetTotal(p.total, true)
}
p.totalMutex.Unlock()
2020-07-04 21:00:11 +00:00
p.progress.Wait()
}
2020-07-11 20:57:44 +00:00
// Creates and returns a progress bar.
func (p *Progress) setupProgressbar(name string, total int64, priority int) *mpb.Bar {
color := p.colorizer
2020-07-26 22:00:06 +00:00
p.total = total
p.initialTotal = total
2020-07-11 20:57:44 +00:00
return p.progress.AddBar(
total,
mpb.BarPriority(priority),
2020-07-11 20:57:44 +00:00
mpb.BarNoPop(),
mpb.BarRemoveOnComplete(),
mpb.PrependDecorators(
decor.Name(name, decor.WCSyncSpaceR),
decor.CountersNoUnit(color.BrightBlue(" %d/%d").String(), decor.WCSyncSpace),
2020-07-26 13:10:03 +00:00
decor.NewPercentage(color.Bold("%d").String(), decor.WCSyncSpace),
2020-07-11 20:57:44 +00:00
),
mpb.AppendDecorators(
2020-07-26 22:00:06 +00:00
decor.AverageSpeed(0, color.BrightYellow("%.2f").Bold().String()+color.BrightYellow("r/s").String(), decor.WCSyncSpace),
decor.Elapsed(decor.ET_STYLE_GO, decor.WCSyncSpace),
2020-07-11 20:57:44 +00:00
decor.AverageETA(decor.ET_STYLE_GO, decor.WCSyncSpace),
),
)
}
2020-07-04 21:00:11 +00:00
2020-07-11 20:57:44 +00:00
// Starts capturing stdout and stderr instead of producing visual output that may interfere with the progress bars.
2020-07-04 21:00:11 +00:00
func (p *Progress) StartStdCapture() {
2020-07-09 19:20:00 +00:00
p.stdCaptureMutex.Lock()
2020-07-04 21:00:11 +00:00
p.captureData = startStdCapture()
}
2020-07-11 20:57:44 +00:00
// Stops capturing stdout and stderr and store both output to be shown later.
func (p *Progress) StopStdCapture() {
2020-07-04 21:00:11 +00:00
stopStdCapture(p.captureData)
p.stdout.Write(p.captureData.DataStdOut.Bytes())
p.stderr.Write(p.captureData.DataStdErr.Bytes())
//
var r = regexp.MustCompile("(.{" + strconv.Itoa(p.termWidth) + "})")
multiline := r.ReplaceAllString(p.stdout.String(), "$1\n")
arr := strings.Split(multiline, "\n")
for _, msg := range arr {
if len(msg) > 0 {
p.progress.Add(0, makeLogBar(msg)).SetTotal(0, true)
}
}
p.stdout.Reset()
//
2020-07-09 19:20:00 +00:00
p.stdCaptureMutex.Unlock()
}
2020-07-11 20:57:44 +00:00
// Writes the captured stdout data to stdout, if any.
func (p *Progress) ShowStdOut() {
//if p.stdout.Len() > 0 {
// fmt.Fprint(os.Stdout, p.stdout.String())
//}
2020-07-04 21:00:11 +00:00
}
2020-07-11 20:57:44 +00:00
// Writes the captured stderr data to stderr, if any.
func (p *Progress) ShowStdErr() {
//if p.stderr.Len() > 0 {
// fmt.Fprint(os.Stderr, p.stderr.String())
//}
2020-07-04 21:00:11 +00:00
}
func makeLogBar(msg string) mpb.BarFiller {
return mpb.BarFillerFunc(func(w io.Writer, _ int, st decor.Statistics) {
fmt.Fprintf(w, msg)
})
}