From 59f40ad8251294cfed83cdfc4e6b591a06ab0eec Mon Sep 17 00:00:00 2001 From: Aykhan Shahsuvarov Date: Mon, 24 Mar 2025 16:54:09 +0400 Subject: [PATCH 1/4] =?UTF-8?q?=E2=9C=A8=20Add=20duration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 48 ++++++++++++---------- config.json | 1 + config.yaml | 1 + config/cli.go | 18 +++++--- config/config.go | 31 ++++++++++++-- main.go | 5 +++ requests/helper.go | 15 ++++--- requests/run.go | 100 ++++++++++++++++++++++++++++++++++++--------- types/duration.go | 26 ++++++------ types/timeout.go | 57 ++++++++++++++++++++++++++ 10 files changed, 232 insertions(+), 70 deletions(-) create mode 100644 types/timeout.go diff --git a/README.md b/README.md index d8fe9be..a53ba1c 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ ## Table of Contents - [Installation](#installation) - - [Using Docker (Recommended)](#using-docker-recommended) - - [Using Pre-built Binaries](#using-pre-built-binaries) - - [Building from Source](#building-from-source) + - [Using Docker (Recommended)](#using-docker-recommended) + - [Using Pre-built Binaries](#using-pre-built-binaries) + - [Building from Source](#building-from-source) - [Usage](#usage) - - [1. CLI Usage](#1-cli-usage) - - [2. Config File Usage](#2-config-file-usage) - - [2.1 JSON Example](#21-json-example) - - [2.2 YAML/YML Example](#22-yamlyml-example) - - [3. CLI & Config File Combination](#3-cli--config-file-combination) + - [1. CLI Usage](#1-cli-usage) + - [2. Config File Usage](#2-config-file-usage) + - [2.1 JSON Example](#21-json-example) + - [2.2 YAML/YML Example](#22-yamlyml-example) + - [3. CLI & Config File Combination](#3-cli--config-file-combination) - [Config Parameters Reference](#config-parameters-reference) ## Installation @@ -46,6 +46,7 @@ Download the latest binaries from the [releases](https://github.com/aykhans/dodo ### Building from Source To build Dodo from source, ensure you have [Go 1.24+](https://golang.org/dl/) installed. + ```sh go install -ldflags "-s -w" github.com/aykhans/dodo@latest ``` @@ -82,6 +83,7 @@ Send 1000 GET requests to https://example.com with 10 parallel dodos (threads) a "timeout": "800ms", "dodos": 10, "requests": 1000, + "duration": "10s", "params": [ // A random value will be selected from the list for first "key1" param on each request @@ -159,6 +161,7 @@ yes: false timeout: "800ms" dodos: 10 requests: 1000 +duration: "10s" params: # A random value will be selected from the list for first "key1" param on each request @@ -243,17 +246,18 @@ docker run --rm -i -v /path/to/config.json:/config.json aykhans/dodo -f /config. If `Headers`, `Params`, `Cookies`, `Body`, or `Proxy` fields have multiple values, each request will choose a random value from the list. -| Parameter | JSON config file | CLI Flag | CLI Short Flag | Type | Description | Default | -| --------------- | ---------------- | ------------ | -------------- | ------------------------------ | --------------------------------------------------------------- | ------- | -| Config file | - | -config-file | -f | String | Path to local config file or http(s) URL of the config file | - | -| Yes | yes | -yes | -y | Boolean | Answer yes to all questions | false | -| URL | url | -url | -u | String | URL to send the request to | - | -| Method | method | -method | -m | String | HTTP method | GET | -| Requests | requests | -requests | -r | UnsignedInteger | Total number of requests to send | 1000 | -| Dodos (Threads) | dodos | -dodos | -d | UnsignedInteger | Number of dodos (threads) to send requests in parallel | 1 | -| Timeout | timeout | -timeout | -t | Duration | Timeout for canceling each request | 10s | -| Params | params | -param | -p | [{String: String OR [String]}] | Request parameters | - | -| Headers | headers | -header | -H | [{String: String OR [String]}] | Request headers | - | -| Cookies | cookies | -cookie | -c | [{String: String OR [String]}] | Request cookies | - | -| Body | body | -body | -b | String OR [String] | Request body or list of request bodies | - | -| Proxy | proxies | -proxy | -x | String OR [String] | Proxy URL or list of proxy URLs | - | +| Parameter | config file | CLI Flag | CLI Short Flag | Type | Description | Default | +| --------------- | ----------- | ------------ | -------------- | ------------------------------ | ----------------------------------------------------------- | ------- | +| Config file | - | -config-file | -f | String | Path to local config file or http(s) URL of the config file | - | +| Yes | yes | -yes | -y | Boolean | Answer yes to all questions | false | +| URL | url | -url | -u | String | URL to send the request to | - | +| Method | method | -method | -m | String | HTTP method | GET | +| Dodos (Threads) | dodos | -dodos | -d | UnsignedInteger | Number of dodos (threads) to send requests in parallel | 1 | +| Requests | requests | -requests | -r | UnsignedInteger | Total number of requests to send | - | +| Duration | duration | -duration | -o | Time | Maximum duration for the test | - | +| Timeout | timeout | -timeout | -t | Time | Timeout for canceling each request | 10s | +| Params | params | -param | -p | [{String: String OR [String]}] | Request parameters | - | +| Headers | headers | -header | -H | [{String: String OR [String]}] | Request headers | - | +| Cookies | cookies | -cookie | -c | [{String: String OR [String]}] | Request cookies | - | +| Body | body | -body | -b | String OR [String] | Request body or list of request bodies | - | +| Proxy | proxies | -proxy | -x | String OR [String] | Proxy URL or list of proxy URLs | - | diff --git a/config.json b/config.json index 48fa3d1..53c7a0e 100644 --- a/config.json +++ b/config.json @@ -5,6 +5,7 @@ "timeout": "5s", "dodos": 8, "requests": 1000, + "duration": "10s", "params": [ { "key1": ["value1", "value2", "value3", "value4"] }, diff --git a/config.yaml b/config.yaml index 3273304..d8353b0 100644 --- a/config.yaml +++ b/config.yaml @@ -4,6 +4,7 @@ yes: false timeout: "5s" dodos: 8 requests: 1000 +duration: "10s" params: - key1: ["value1", "value2", "value3", "value4"] diff --git a/config/cli.go b/config/cli.go index 99db4f0..a09d050 100644 --- a/config/cli.go +++ b/config/cli.go @@ -16,8 +16,8 @@ const cliUsageText = `Usage: Examples: -Simple usage only with URL: - dodo -u https://example.com +Simple usage: + dodo -u https://example.com -o 1m Usage with config file: dodo -f /path/to/config/file/config.json @@ -25,7 +25,7 @@ Usage with config file: Usage with all flags: dodo -f /path/to/config/file/config.json \ -u https://example.com -m POST \ - -d 10 -r 1000 -t 3s \ + -d 10 -r 1000 -o 3m -t 3s \ -b "body1" -body "body2" \ -H "header1:value1" -header "header2:value2" \ -p "param1=value1" -param "param2=value2" \ @@ -39,8 +39,9 @@ Flags: -y, -yes bool Answer yes to all questions (default %v) -f, -config-file string Path to the local config file or http(s) URL of the config file -d, -dodos uint Number of dodos(threads) (default %d) - -r, -requests uint Number of total requests (default %d) - -t, -timeout Duration Timeout for each request (e.g. 400ms, 15s, 1m10s) (default %v) + -r, -requests uint Number of total requests + -o, -duration Time Maximum duration for the test (e.g. 30s, 1m, 5h) + -t, -timeout Time Timeout for each request (e.g. 400ms, 15s, 1m10s) (default %v) -u, -url string URL for stress testing -m, -method string HTTP Method for the request (default %s) -b, -body [string] Body for the request (e.g. "body text") @@ -55,7 +56,6 @@ func (config *Config) ReadCLI() (types.ConfigFile, error) { cliUsageText+"\n", DefaultYes, DefaultDodosCount, - DefaultRequestCount, DefaultTimeout, DefaultMethod, ) @@ -70,6 +70,7 @@ func (config *Config) ReadCLI() (types.ConfigFile, error) { dodosCount = uint(0) requestCount = uint(0) timeout time.Duration + duration time.Duration ) { @@ -94,6 +95,9 @@ func (config *Config) ReadCLI() (types.ConfigFile, error) { flag.UintVar(&requestCount, "requests", 0, "Number of total requests") flag.UintVar(&requestCount, "r", 0, "Number of total requests") + flag.DurationVar(&duration, "duration", 0, "Maximum duration of the test") + flag.DurationVar(&duration, "o", 0, "Maximum duration of the test") + flag.DurationVar(&timeout, "timeout", 0, "Timeout for each request (e.g. 400ms, 15s, 1m10s)") flag.DurationVar(&timeout, "t", 0, "Timeout for each request (e.g. 400ms, 15s, 1m10s)") @@ -139,6 +143,8 @@ func (config *Config) ReadCLI() (types.ConfigFile, error) { config.DodosCount = utils.ToPtr(dodosCount) case "requests", "r": config.RequestCount = utils.ToPtr(requestCount) + case "duration", "o": + config.Duration = &types.Duration{Duration: duration} case "timeout", "t": config.Timeout = &types.Timeout{Duration: timeout} case "yes", "y": diff --git a/config/config.go b/config/config.go index 00a052f..888730e 100644 --- a/config/config.go +++ b/config/config.go @@ -20,7 +20,8 @@ const ( DefaultMethod string = "GET" DefaultTimeout time.Duration = time.Second * 10 DefaultDodosCount uint = 1 - DefaultRequestCount uint = 1 + DefaultRequestCount uint = 0 + DefaultDuration time.Duration = 0 DefaultYes bool = false ) @@ -32,6 +33,7 @@ type RequestConfig struct { Timeout time.Duration DodosCount uint RequestCount uint + Duration time.Duration Yes bool Params types.Params Headers types.Headers @@ -47,6 +49,7 @@ func NewRequestConfig(conf *Config) *RequestConfig { Timeout: conf.Timeout.Duration, DodosCount: *conf.DodosCount, RequestCount: *conf.RequestCount, + Duration: conf.Duration.Duration, Yes: *conf.Yes, Params: conf.Params, Headers: conf.Headers, @@ -57,6 +60,9 @@ func NewRequestConfig(conf *Config) *RequestConfig { } func (rc *RequestConfig) GetValidDodosCountForRequests() uint { + if rc.RequestCount == 0 { + return rc.DodosCount + } return min(rc.DodosCount, rc.RequestCount) } @@ -95,7 +101,17 @@ func (rc *RequestConfig) Print() { t.AppendSeparator() t.AppendRow(table.Row{"Dodos", rc.DodosCount}) t.AppendSeparator() - t.AppendRow(table.Row{"Requests", rc.RequestCount}) + if rc.RequestCount > 0 { + t.AppendRow(table.Row{"Requests", rc.RequestCount}) + } else { + t.AppendRow(table.Row{"Requests"}) + } + t.AppendSeparator() + if rc.Duration > 0 { + t.AppendRow(table.Row{"Duration", rc.Duration}) + } else { + t.AppendRow(table.Row{"Duration"}) + } t.AppendSeparator() t.AppendRow(table.Row{"Params", rc.Params.String()}) t.AppendSeparator() @@ -116,6 +132,7 @@ type Config struct { Timeout *types.Timeout `json:"timeout" yaml:"timeout"` DodosCount *uint `json:"dodos" yaml:"dodos"` RequestCount *uint `json:"requests" yaml:"requests"` + Duration *types.Duration `json:"duration" yaml:"duration"` Yes *bool `json:"yes" yaml:"yes"` Params types.Params `json:"params" yaml:"params"` Headers types.Headers `json:"headers" yaml:"headers"` @@ -162,8 +179,8 @@ func (c *Config) Validate() []error { if utils.IsNilOrZero(c.DodosCount) { errs = append(errs, errors.New("dodos count must be greater than 0")) } - if utils.IsNilOrZero(c.RequestCount) { - errs = append(errs, errors.New("request count must be greater than 0")) + if utils.IsNilOrZero(c.Duration) && utils.IsNilOrZero(c.RequestCount) { + errs = append(errs, errors.New("you should provide at least one of duration or request count")) } for i, proxy := range c.Proxies { @@ -197,6 +214,9 @@ func (config *Config) MergeConfig(newConfig *Config) { if newConfig.RequestCount != nil { config.RequestCount = newConfig.RequestCount } + if newConfig.Duration != nil { + config.Duration = newConfig.Duration + } if newConfig.Yes != nil { config.Yes = newConfig.Yes } @@ -230,6 +250,9 @@ func (config *Config) SetDefaults() { if config.RequestCount == nil { config.RequestCount = utils.ToPtr(DefaultRequestCount) } + if config.Duration == nil { + config.Duration = &types.Duration{Duration: DefaultDuration} + } if config.Yes == nil { config.Yes = utils.ToPtr(DefaultYes) } diff --git a/main.go b/main.go index 5e99a86..36a0b2d 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "os" "os/signal" "syscall" + "time" "github.com/aykhans/dodo/config" "github.com/aykhans/dodo/requests" @@ -49,6 +50,10 @@ func main() { ctx, cancel := context.WithCancel(context.Background()) go listenForTermination(func() { cancel() }) + if requestConf.Duration > 0 { + time.AfterFunc(requestConf.Duration, func() { cancel() }) + } + responses, err := requests.Run(ctx, requestConf) if err != nil { if err == types.ErrInterrupt { diff --git a/requests/helper.go b/requests/helper.go index 5f9768a..47d4bfd 100644 --- a/requests/helper.go +++ b/requests/helper.go @@ -17,7 +17,7 @@ import ( func streamProgress( ctx context.Context, wg *sync.WaitGroup, - total int64, + total uint, message string, increase <-chan int64, ) { @@ -27,21 +27,26 @@ func streamProgress( pw.SetStyle(progress.StyleBlocks) pw.SetTrackerLength(40) pw.SetUpdateFrequency(time.Millisecond * 250) + if total == 0 { + pw.Style().Visibility.Percentage = false + } go pw.Render() dodosTracker := progress.Tracker{ Message: message, - Total: total, + Total: int64(total), } pw.AppendTracker(&dodosTracker) + for { select { case <-ctx.Done(): - if ctx.Err() != context.Canceled { + if err := ctx.Err(); err == context.Canceled || err == context.DeadlineExceeded { + dodosTracker.MarkAsDone() + } else { dodosTracker.MarkAsErrored() } + time.Sleep(time.Millisecond * 300) fmt.Printf("\r") - time.Sleep(time.Millisecond * 500) - pw.Stop() return case value := <-increase: diff --git a/requests/run.go b/requests/run.go index 94079f8..a076671 100644 --- a/requests/run.go +++ b/requests/run.go @@ -59,46 +59,59 @@ func releaseDodos( streamWG sync.WaitGroup requestCountPerDodo uint dodosCount uint = requestConfig.GetValidDodosCountForRequests() - dodosCountInt int = int(dodosCount) responses = make([][]*Response, dodosCount) increase = make(chan int64, requestConfig.RequestCount) ) - wg.Add(dodosCountInt) + wg.Add(int(dodosCount)) streamWG.Add(1) streamCtx, streamCtxCancel := context.WithCancel(context.Background()) - go streamProgress(streamCtx, &streamWG, int64(requestConfig.RequestCount), "Dodos Working🔥", increase) + go streamProgress(streamCtx, &streamWG, requestConfig.RequestCount, "Dodos Working🔥", increase) - for i := range dodosCount { - if i+1 == dodosCount { - requestCountPerDodo = requestConfig.RequestCount - (i * requestConfig.RequestCount / dodosCount) - } else { - requestCountPerDodo = ((i + 1) * requestConfig.RequestCount / dodosCount) - - (i * requestConfig.RequestCount / dodosCount) + if requestConfig.RequestCount == 0 { + for i := range dodosCount { + go sendRequest( + ctx, + newRequest(*requestConfig, clients, int64(i)), + requestConfig.Timeout, + &responses[i], + increase, + &wg, + ) } + } else { + for i := range dodosCount { + if i+1 == dodosCount { + requestCountPerDodo = requestConfig.RequestCount - (i * requestConfig.RequestCount / dodosCount) + } else { + requestCountPerDodo = ((i + 1) * requestConfig.RequestCount / dodosCount) - + (i * requestConfig.RequestCount / dodosCount) + } - go sendRequest( - ctx, - newRequest(*requestConfig, clients, int64(i)), - requestConfig.Timeout, - requestCountPerDodo, - &responses[i], - increase, - &wg, - ) + go sendRequestByCount( + ctx, + newRequest(*requestConfig, clients, int64(i)), + requestConfig.Timeout, + requestCountPerDodo, + &responses[i], + increase, + &wg, + ) + } } + wg.Wait() streamCtxCancel() streamWG.Wait() return utils.Flatten(responses) } -// sendRequest sends a specified number of HTTP requests concurrently with a given timeout. +// sendRequestByCount sends a specified number of HTTP requests concurrently with a given timeout. // It appends the responses to the provided responseData slice and sends the count of completed requests // to the increase channel. The function terminates early if the context is canceled or if a custom // interrupt error is encountered. -func sendRequest( +func sendRequestByCount( ctx context.Context, request *Request, timeout time.Duration, @@ -142,3 +155,50 @@ func sendRequest( }() } } + +// sendRequest continuously sends HTTP requests until the context is canceled. +// It records the response status code or error message along with the response time, +// and signals each completed request through the increase channel. +func sendRequest( + ctx context.Context, + request *Request, + timeout time.Duration, + responseData *[]*Response, + increase chan<- int64, + wg *sync.WaitGroup, +) { + defer wg.Done() + + for { + if ctx.Err() != nil { + return + } + + func() { + startTime := time.Now() + response, err := request.Send(ctx, timeout) + completedTime := time.Since(startTime) + if response != nil { + defer fasthttp.ReleaseResponse(response) + } + + if err != nil { + if err == types.ErrInterrupt { + return + } + *responseData = append(*responseData, &Response{ + Response: err.Error(), + Time: completedTime, + }) + increase <- 1 + return + } + + *responseData = append(*responseData, &Response{ + Response: strconv.Itoa(response.StatusCode()), + Time: completedTime, + }) + increase <- 1 + }() + } +} diff --git a/types/duration.go b/types/duration.go index 9d41e33..7eaa44b 100644 --- a/types/duration.go +++ b/types/duration.go @@ -6,52 +6,52 @@ import ( "time" ) -type Timeout struct { +type Duration struct { time.Duration } -func (timeout *Timeout) UnmarshalJSON(b []byte) error { +func (duration *Duration) UnmarshalJSON(b []byte) error { var v any if err := json.Unmarshal(b, &v); err != nil { return err } switch value := v.(type) { case float64: - timeout.Duration = time.Duration(value) + duration.Duration = time.Duration(value) return nil case string: var err error - timeout.Duration, err = time.ParseDuration(value) + duration.Duration, err = time.ParseDuration(value) if err != nil { - return errors.New("Timeout is invalid (e.g. 400ms, 1s, 5m, 1h)") + return errors.New("Duration is invalid (e.g. 400ms, 1s, 5m, 1h)") } return nil default: - return errors.New("Timeout is invalid (e.g. 400ms, 1s, 5m, 1h)") + return errors.New("Duration is invalid (e.g. 400ms, 1s, 5m, 1h)") } } -func (timeout Timeout) MarshalJSON() ([]byte, error) { - return json.Marshal(timeout.Duration.String()) +func (duration Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(duration.Duration.String()) } -func (timeout *Timeout) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (duration *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error { var v any if err := unmarshal(&v); err != nil { return err } switch value := v.(type) { case float64: - timeout.Duration = time.Duration(value) + duration.Duration = time.Duration(value) return nil case string: var err error - timeout.Duration, err = time.ParseDuration(value) + duration.Duration, err = time.ParseDuration(value) if err != nil { - return errors.New("Timeout is invalid (e.g. 400ms, 1s, 5m, 1h)") + return errors.New("Duration is invalid (e.g. 400ms, 1s, 5m, 1h)") } return nil default: - return errors.New("Timeout is invalid (e.g. 400ms, 1s, 5m, 1h)") + return errors.New("Duration is invalid (e.g. 400ms, 1s, 5m, 1h)") } } diff --git a/types/timeout.go b/types/timeout.go new file mode 100644 index 0000000..9d41e33 --- /dev/null +++ b/types/timeout.go @@ -0,0 +1,57 @@ +package types + +import ( + "encoding/json" + "errors" + "time" +) + +type Timeout struct { + time.Duration +} + +func (timeout *Timeout) UnmarshalJSON(b []byte) error { + var v any + if err := json.Unmarshal(b, &v); err != nil { + return err + } + switch value := v.(type) { + case float64: + timeout.Duration = time.Duration(value) + return nil + case string: + var err error + timeout.Duration, err = time.ParseDuration(value) + if err != nil { + return errors.New("Timeout is invalid (e.g. 400ms, 1s, 5m, 1h)") + } + return nil + default: + return errors.New("Timeout is invalid (e.g. 400ms, 1s, 5m, 1h)") + } +} + +func (timeout Timeout) MarshalJSON() ([]byte, error) { + return json.Marshal(timeout.Duration.String()) +} + +func (timeout *Timeout) UnmarshalYAML(unmarshal func(interface{}) error) error { + var v any + if err := unmarshal(&v); err != nil { + return err + } + switch value := v.(type) { + case float64: + timeout.Duration = time.Duration(value) + return nil + case string: + var err error + timeout.Duration, err = time.ParseDuration(value) + if err != nil { + return errors.New("Timeout is invalid (e.g. 400ms, 1s, 5m, 1h)") + } + return nil + default: + return errors.New("Timeout is invalid (e.g. 400ms, 1s, 5m, 1h)") + } +} From 3cc165cbf449156cb8428503caf823a01c3c17e6 Mon Sep 17 00:00:00 2001 From: Aykhan Shahsuvarov Date: Mon, 24 Mar 2025 16:55:23 +0400 Subject: [PATCH 2/4] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20bump=20version=20to=20?= =?UTF-8?q?0.6.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 888730e..a1a6131 100644 --- a/config/config.go +++ b/config/config.go @@ -15,7 +15,7 @@ import ( ) const ( - VERSION string = "0.6.1" + VERSION string = "0.6.2" DefaultUserAgent string = "Dodo/" + VERSION DefaultMethod string = "GET" DefaultTimeout time.Duration = time.Second * 10 From 69c4841a0580208ef0c39c6b5f4fec336ccedd5e Mon Sep 17 00:00:00 2001 From: Aykhan Shahsuvarov Date: Mon, 24 Mar 2025 17:02:06 +0400 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=93=9A=20Update=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +++++++------- config/cli.go | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a53ba1c..7833592 100644 --- a/README.md +++ b/README.md @@ -57,21 +57,21 @@ Dodo supports CLI arguments, configuration files (JSON/YAML), or a combination o ### 1. CLI Usage -Send 1000 GET requests to https://example.com with 10 parallel dodos (threads) and a timeout of 2 seconds: +Send 1000 GET requests to https://example.com with 10 parallel dodos (threads), each with a timeout of 2 seconds, within a maximum duration of 1 minute: ```sh -dodo -u https://example.com -m GET -d 10 -r 1000 -t 2s +dodo -u https://example.com -m GET -d 10 -r 1000 -o 1m -t 2s ``` With Docker: ```sh -docker run --rm -i aykhans/dodo -u https://example.com -m GET -d 10 -r 1000 -t 2s +docker run --rm -i aykhans/dodo -u https://example.com -m GET -d 10 -r 1000 -o 1m -t 2s ``` ### 2. Config File Usage -Send 1000 GET requests to https://example.com with 10 parallel dodos (threads) and a timeout of 800 milliseconds: +Send 1000 GET requests to https://example.com with 10 parallel dodos (threads), each with a timeout of 800 milliseconds, within a maximum duration of 250 seconds: #### 2.1 JSON Example @@ -83,7 +83,7 @@ Send 1000 GET requests to https://example.com with 10 parallel dodos (threads) a "timeout": "800ms", "dodos": 10, "requests": 1000, - "duration": "10s", + "duration": "250s", "params": [ // A random value will be selected from the list for first "key1" param on each request @@ -233,13 +233,13 @@ docker run --rm -i aykhans/dodo -f https://example.com/config.yaml CLI arguments override config file values: ```sh -dodo -f /path/to/config.yaml -u https://example.com -m GET -d 10 -r 1000 -t 5s +dodo -f /path/to/config.yaml -u https://example.com -m GET -d 10 -r 1000 -o 1m -t 5s ``` With Docker: ```sh -docker run --rm -i -v /path/to/config.json:/config.json aykhans/dodo -f /config.json -u https://example.com -m GET -d 10 -r 1000 -t 5s +docker run --rm -i -v /path/to/config.json:/config.json aykhans/dodo -f /config.json -u https://example.com -m GET -d 10 -r 1000 -o 1m -t 5s ``` ## Config Parameters Reference diff --git a/config/cli.go b/config/cli.go index a09d050..48ae09c 100644 --- a/config/cli.go +++ b/config/cli.go @@ -40,7 +40,7 @@ Flags: -f, -config-file string Path to the local config file or http(s) URL of the config file -d, -dodos uint Number of dodos(threads) (default %d) -r, -requests uint Number of total requests - -o, -duration Time Maximum duration for the test (e.g. 30s, 1m, 5h) + -o, -duration Time Maximum duration for the test (e.g. 30s, 1m, 5h) -t, -timeout Time Timeout for each request (e.g. 400ms, 15s, 1m10s) (default %v) -u, -url string URL for stress testing -m, -method string HTTP Method for the request (default %s) From 934cd0ad33b4f1ab505f2bd42a9b53f4db1cbbd3 Mon Sep 17 00:00:00 2001 From: Aykhan Shahsuvarov Date: Mon, 24 Mar 2025 17:02:58 +0400 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=93=9A=20Update=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7833592..ad58c57 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ yes: false timeout: "800ms" dodos: 10 requests: 1000 -duration: "10s" +duration: "250s" params: # A random value will be selected from the list for first "key1" param on each request