From 186683fce5560942e31c4d5d9bdc441e2f7912c7 Mon Sep 17 00:00:00 2001 From: Aykhan Shahsuvarov Date: Wed, 18 Dec 2024 20:58:14 +0400 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=94=A8Cobra=20package=20replaced=20wi?= =?UTF-8?q?th=20standard=20flag=20package?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 9 ++- custom_errors/formaters.go | 4 - go.mod | 3 - go.sum | 9 --- main.go | 12 ++- readers/cli.go | 156 +++++++++++++++++++++++++------------ types/option.go | 2 + 7 files changed, 123 insertions(+), 72 deletions(-) diff --git a/config/config.go b/config/config.go index 393d83e..b0883f4 100644 --- a/config/config.go +++ b/config/config.go @@ -18,7 +18,7 @@ const ( DefaultMethod string = "GET" DefaultTimeout uint32 = 10000 // Milliseconds (10 seconds) DefaultDodosCount uint = 1 - DefaultRequestCount uint = 1000 + DefaultRequestCount uint = 1 MaxDodosCountForProxies uint = 20 // Max dodos count for proxy check ) @@ -209,13 +209,13 @@ func (config *JSONConfig) MergeConfigs(newConfig *JSONConfig) { type CLIConfig struct { *Config - Yes bool `json:"yes" validate:"omitempty"` + Yes Option[bool] `json:"yes" validate:"omitempty"` ConfigFile string `validation_name:"config-file" validate:"omitempty,filepath"` } func NewCLIConfig( config *Config, - yes bool, + yes Option[bool], configFile string, ) *CLIConfig { return &CLIConfig{ @@ -228,4 +228,7 @@ func (config *CLIConfig) MergeConfigs(newConfig *CLIConfig) { if newConfig.ConfigFile != "" { config.ConfigFile = newConfig.ConfigFile } + if !newConfig.Yes.IsNone() { + config.Yes = newConfig.Yes + } } diff --git a/custom_errors/formaters.go b/custom_errors/formaters.go index c877a7c..99c5252 100644 --- a/custom_errors/formaters.go +++ b/custom_errors/formaters.go @@ -19,10 +19,6 @@ func OSErrorFormater(err error) error { return ErrInvalidFile } -func CobraErrorFormater(err error) error { - return err -} - func shortenNamespace(namespace string) string { return namespace[strings.Index(namespace, ".")+1:] } diff --git a/go.mod b/go.mod index 693eb13..0122c13 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,6 @@ go 1.23.2 require ( github.com/go-playground/validator/v10 v10.23.0 github.com/jedib0t/go-pretty/v6 v6.6.5 - github.com/spf13/cobra v1.8.1 - github.com/spf13/pflag v1.0.5 github.com/valyala/fasthttp v1.58.0 golang.org/x/net v0.32.0 ) @@ -16,7 +14,6 @@ require ( github.com/gabriel-vasile/mimetype v1.4.4 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect diff --git a/go.sum b/go.sum index 73bfc97..8d29e52 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,5 @@ github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= @@ -13,8 +12,6 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jedib0t/go-pretty/v6 v6.6.5 h1:9PgMJOVBedpgYLI56jQRJYqngxYAAzfEUua+3NgSqAo= github.com/jedib0t/go-pretty/v6 v6.6.5/go.mod h1:Uq/HrbhuFty5WSVNfjpQQe47x16RwVGXIveNGEyGtHs= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= @@ -28,11 +25,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -51,6 +43,5 @@ golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index a03cd3b..714a2ff 100644 --- a/main.go +++ b/main.go @@ -27,9 +27,13 @@ func main() { ) cliConf, err := readers.CLIConfigReader() - if err != nil || cliConf == nil { + if err != nil { + utils.PrintAndExit(err.Error()) + } + if cliConf == nil { os.Exit(0) } + if err := validator.StructPartial(cliConf, "ConfigFile"); err != nil { utils.PrintErrAndExit( customerrors.ValidationErrorsFormater( @@ -82,11 +86,11 @@ func main() { Cookies: jsonConf.Cookies, Proxies: jsonConf.Proxies, Body: jsonConf.Body, - Yes: cliConf.Yes, - NoProxyCheck: conf.NoProxyCheck.ValueOrPanic(), + Yes: cliConf.Yes.ValueOr(false), + NoProxyCheck: conf.NoProxyCheck.ValueOr(false), } requestConf.Print() - if !cliConf.Yes { + if !cliConf.Yes.ValueOr(false) { response := readers.CLIYesOrNoReader("Do you want to continue?", true) if !response { utils.PrintAndExit("Exiting...") diff --git a/readers/cli.go b/readers/cli.go index 6ed52a4..2b0db66 100644 --- a/readers/cli.go +++ b/readers/cli.go @@ -1,70 +1,128 @@ package readers import ( + "flag" "fmt" + "math" + "strings" "github.com/aykhans/dodo/config" - "github.com/aykhans/dodo/custom_errors" . "github.com/aykhans/dodo/types" "github.com/aykhans/dodo/utils" - "github.com/spf13/cobra" - "github.com/spf13/pflag" ) +const usageText = `Usage: + dodo [flags] + +Examples: + +Simple usage only with URL: + dodo -u https://example.com + +Simple usage with config file: + dodo -c /path/to/config/file/config.json + +Usage with all flags: + dodo -c /path/to/config/file/config.json -u https://example.com -m POST -d 10 -r 1000 -t 2000 --no-proxy-check -y + +Flags: + -h, --help help for dodo + -v, --version version for dodo + -c, --config-file string Path to the config file + -d, --dodos-count uint Number of dodos(threads) (default %d) + -m, --method string HTTP Method (default %s) + -r, --request-count uint Number of total requests (default %d) + -t, --timeout uint32 Timeout for each request in milliseconds (default %d) + -u, --url string URL for stress testing + --no-proxy-check bool Do not check for proxies (default false) + -y, --yes bool Answer yes to all questions (default false)` + func CLIConfigReader() (*config.CLIConfig, error) { - var ( - returnNil = false - cliConfig = config.NewCLIConfig(config.NewConfig("", 0, 0, 0, nil), false, "") - dodosCount uint - requestCount uint - timeout uint32 - noProxyCheck bool - rootCmd = &cobra.Command{ - Use: "dodo [flags]", - Example: ` Simple usage only with URL: - dodo -u https://example.com - - Simple usage with config file: - dodo -c /path/to/config/file/config.json - - Usage with all flags: - dodo -c /path/to/config/file/config.json -u https://example.com -m POST -d 10 -r 1000 -t 2000 --no-proxy-check -y`, - Run: func(cmd *cobra.Command, args []string) {}, - SilenceErrors: true, - SilenceUsage: true, - Version: config.VERSION, - } - ) - - rootCmd.Flags().StringVarP(&cliConfig.ConfigFile, "config-file", "c", "", "Path to the config file") - rootCmd.Flags().BoolVarP(&cliConfig.Yes, "yes", "y", false, "Answer yes to all questions") - rootCmd.Flags().StringVarP(&cliConfig.Method, "method", "m", "", fmt.Sprintf("HTTP Method (default %s)", config.DefaultMethod)) - rootCmd.Flags().StringVarP(&cliConfig.URL, "url", "u", "", "URL for stress testing") - rootCmd.Flags().UintVarP(&dodosCount, "dodos-count", "d", config.DefaultDodosCount, "Number of dodos(threads)") - rootCmd.Flags().UintVarP(&requestCount, "request-count", "r", config.DefaultRequestCount, "Number of total requests") - rootCmd.Flags().Uint32VarP(&timeout, "timeout", "t", config.DefaultTimeout, "Timeout for each request in milliseconds") - rootCmd.Flags().BoolVar(&noProxyCheck, "no-proxy-check", false, "Do not check for proxies") - if err := rootCmd.Execute(); err != nil { - utils.PrintErr(err) - rootCmd.Println(rootCmd.UsageString()) - return nil, customerrors.CobraErrorFormater(err) + flag.Usage = func() { + fmt.Printf( + usageText+"\n", + config.DefaultDodosCount, + config.DefaultMethod, + config.DefaultRequestCount, + config.DefaultTimeout, + ) } - rootCmd.Flags().Visit(func(f *pflag.Flag) { + + var ( + cliConfig = config.NewCLIConfig(config.NewConfig("", 0, 0, 0, nil), NewOption(false), "") + configFile = "" + yes = false + method = "" + url = "" + dodosCount uint = 0 + requestsCount uint = 0 + timeout uint = 0 + noProxyCheck bool = false + ) + { + flag.Bool("version", false, "Prints the version of the program") + flag.Bool("v", false, "Prints the version of the program") + + flag.StringVar(&configFile, "config-file", "", "Path to the configuration file") + flag.StringVar(&configFile, "c", "", "Path to the configuration file") + + flag.BoolVar(&yes, "yes", false, "Answer yes to all questions") + flag.BoolVar(&yes, "y", false, "Answer yes to all questions") + + flag.StringVar(&method, "method", "", "HTTP Method") + flag.StringVar(&method, "m", "", "HTTP Method") + + flag.StringVar(&url, "url", "", "URL to send the request") + flag.StringVar(&url, "u", "", "URL to send the request") + + flag.UintVar(&dodosCount, "dodos-count", 0, "Number of dodos(threads)") + flag.UintVar(&dodosCount, "d", 0, "Number of dodos(threads)") + + flag.UintVar(&requestsCount, "requests-count", 0, "Number of total requests") + flag.UintVar(&requestsCount, "r", 0, "Number of total requests") + + flag.UintVar(&timeout, "timeout", 0, "Timeout for each request in milliseconds") + flag.UintVar(&timeout, "t", 0, "Timeout for each request in milliseconds") + + flag.BoolVar(&noProxyCheck, "no-proxy-check", false, "Do not check for active proxies") + } + + flag.Parse() + + args := flag.Args() + if len(args) > 0 { + return nil, fmt.Errorf("unexpected arguments: %v", strings.Join(args, ", ")) + } + + returnNil := false + flag.Visit(func(f *flag.Flag) { switch f.Name { - case "help": + case "version", "v": + fmt.Printf("dodo version %s\n", config.VERSION) returnNil = true - case "version": - returnNil = true - case "dodos-count": + case "config-file", "c": + cliConfig.ConfigFile = configFile + case "yes", "y": + cliConfig.Yes.SetValue(yes) + case "method", "m": + cliConfig.Method = method + case "url", "u": + cliConfig.URL = url + case "dodos-count", "d": cliConfig.DodosCount = dodosCount - case "request-count": - cliConfig.RequestCount = requestCount - case "timeout": - cliConfig.Timeout = timeout + case "requests-count", "r": + cliConfig.RequestCount = requestsCount + case "timeout", "t": + if timeout > math.MaxUint32 { + utils.PrintfC(utils.Colors.Yellow, "timeout value is too large, setting to %d\n", math.MaxUint32) + timeout = math.MaxUint32 + } + cliConfig.Timeout = uint32(timeout) case "no-proxy-check": - cliConfig.NoProxyCheck = NewOption(noProxyCheck) + cliConfig.NoProxyCheck.SetValue(noProxyCheck) } }) + if returnNil { return nil, nil } diff --git a/types/option.go b/types/option.go index 55dc6ee..c642651 100644 --- a/types/option.go +++ b/types/option.go @@ -14,6 +14,8 @@ type Option[T NonNilT] interface { ValueOrErr() (T, error) ValueOr(def T) T ValueOrPanic() T + SetValue(value T) + SetNone() UnmarshalJSON(data []byte) error } From 6fceb0094b4d2902bd5f8f163716bf66104927de Mon Sep 17 00:00:00 2001 From: Aykhan Shahsuvarov Date: Wed, 18 Dec 2024 23:31:13 +0400 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=94=96=20Bump=20version=20to=200.5.4?= 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 b0883f4..09689b7 100644 --- a/config/config.go +++ b/config/config.go @@ -12,7 +12,7 @@ import ( ) const ( - VERSION string = "0.5.3" + VERSION string = "0.5.4" DefaultUserAgent string = "Dodo/" + VERSION ProxyCheckURL string = "https://www.google.com" DefaultMethod string = "GET" From 61b9ce943b2907ac8e6ee297cc92f4e15b2093d9 Mon Sep 17 00:00:00 2001 From: Aykhan Shahsuvarov Date: Wed, 18 Dec 2024 23:38:52 +0400 Subject: [PATCH 3/3] =?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 | 8 ++++---- config.json | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 043a627..8d160be 100644 --- a/README.md +++ b/README.md @@ -59,13 +59,13 @@ You can find an example config structure in the [config.json](https://github.com "method": "GET", "url": "https://example.com", "no_proxy_check": false, - "timeout": 2000, - "dodos_count": 10, - "request_count": 1000, + "timeout": 10000, + "dodos_count": 1, + "request_count": 1, "params": {}, "headers": {}, "cookies": {}, - "body": [""], + "body": [], "proxies": [ { "url": "http://example.com:8080", diff --git a/config.json b/config.json index 743bec4..ad5e1f9 100644 --- a/config.json +++ b/config.json @@ -3,12 +3,12 @@ "url": "https://example.com", "no_proxy_check": false, "timeout": 10000, - "dodos_count": 50, - "request_count": 1000, + "dodos_count": 1, + "request_count": 1, "params": {}, "headers": {}, "cookies": {}, - "body": [""], + "body": [], "proxies": [ { "url": "http://example.com:8080",