forked from tsenart/vegeta
/
rapid.go
139 lines (100 loc) · 3.16 KB
/
rapid.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package main
import (
// "bytes"
"flag"
"fmt"
vegeta "github.com/senaduka/vegeta/lib"
"log"
"net/http"
"strings"
"time"
"strconv"
)
type rateList []uint64
// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func (i *rateList) String() string {
return fmt.Sprint(*i)
}
// Set is the method to set the flag value, part of the flag.Value interface.
// Set's argument is a string to be parsed to set the flag.
// It's a comma-separated list, so we split it.
func (i *rateList) Set(value string) error {
for _, singleRate := range strings.Split(value, ",") {
oneRate, err := strconv.ParseUint(singleRate, 10, 64)
if err != nil {
return err
}
*i = append(*i, oneRate)
}
return nil
}
// attack validates the attack arguments, sets up the
// required resources, launches the attack and writes the results
func rapidAttack(rate uint64, duration time.Duration, targets *vegeta.Targets , ordering, output string, header http.Header, previousResults vegeta.Results ) (vegeta.Results, error) {
if rate == 0 {
return nil, fmt.Errorf(errRatePrefix + "can't be zero")
}
if duration == 0 {
return nil, fmt.Errorf(errDurationPrefix + "can't be zero")
}
targets.SetHeader(header)
switch ordering {
case "random":
targets.Shuffle(time.Now().UnixNano())
case "sequential":
break
default:
return nil, fmt.Errorf(errOrderingPrefix+"`%s` is invalid", ordering)
}
log.Printf("Vegeta is attacking %d targets in %s order for %s with %d requests/sec...\n", len(*targets), ordering, duration, rate)
results := vegeta.Attack(*targets, rate, duration)
log.Println("Done!")
return append(previousResults, results...), nil
}
func writeResults(results vegeta.Results , output string) error {
out, err := file(output, true)
if err != nil {
return fmt.Errorf(errOutputFilePrefix+"(%s): %s", output, err)
}
defer out.Close()
log.Printf("Writing results to '%s'...", output)
if err := results.Encode(out); err != nil {
return err
}
return nil
}
func rapidCmd(args []string) command {
fs := flag.NewFlagSet("rapid", flag.ExitOnError)
targetsf := fs.String("targets", "stdin", "Targets file")
ordering := fs.String("ordering", "random", "Attack ordering [sequential, random]")
duration := fs.Duration("duration", 10*time.Second, "Duration of the attack on every rate")
output := fs.String("output", "stdout", "Output file")
hdrs := headers{Header: make(http.Header)}
fs.Var(hdrs, "header", "Targets request header")
var rateFlag rateList = []uint64{}
fs.Var(&rateFlag, "rates", "One or more rates, comma separated in requests per second")
fs.Parse(args)
in, err := file(*targetsf, false)
if err != nil {
return nil
}
defer in.Close()
targets, err := vegeta.NewTargetsFrom(in)
if err != nil {
return nil
}
return func() error {
results := make(vegeta.Results,0)
var err error = nil
for _, rate := range rateFlag {
if results, err = rapidAttack(rate, *duration, &targets, *ordering, *output, hdrs.Header, results); err != nil {
return err
}
}
if err = writeResults(results, *output); err != nil {
return err
}
return nil
}
}