mirror of
https://github.com/aykhans/dodo.git
synced 2025-07-02 00:16:20 +00:00
🔨 Restructure entire project logic
- Moved readers to the config package - Added an option to read remote config files - Moved the validation package to the config package and removed the validator dependency - Moved the customerrors package to the config package - Replaced fatih/color with jedib0t/go-pretty/v6/text - Removed proxy check functionality - Added param, header, cookie, body, and proxy flags to the CLI - Allowed multiple values for the same key in params, headers, and cookies
This commit is contained in:
175
config/cli.go
Normal file
175
config/cli.go
Normal file
@ -0,0 +1,175 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aykhans/dodo/types"
|
||||
"github.com/aykhans/dodo/utils"
|
||||
)
|
||||
|
||||
const cliUsageText = `Usage:
|
||||
dodo [flags]
|
||||
|
||||
Examples:
|
||||
|
||||
Simple usage only with URL:
|
||||
dodo -u https://example.com
|
||||
|
||||
Usage with config file:
|
||||
dodo -f /path/to/config/file/config.json
|
||||
|
||||
Usage with all flags:
|
||||
dodo -f /path/to/config/file/config.json \
|
||||
-u https://example.com -m POST \
|
||||
-d 10 -r 1000 -t 3s \
|
||||
-b "body1" -body "body2" \
|
||||
-H "header1: value1" -header "header2: value2" \
|
||||
-p "param1=value1" -param "param2=value2" \
|
||||
-c "cookie1=value1" -cookie "cookie2=value2" \
|
||||
-x "http://proxy.example.com:8080" -proxy "socks5://proxy2.example.com:8080" \
|
||||
-y
|
||||
|
||||
Flags:
|
||||
-h, -help help for dodo
|
||||
-v, -version version for dodo
|
||||
-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)
|
||||
-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")
|
||||
-p, -param [string] Parameter for the request (e.g. "key1=value1")
|
||||
-H, -header [string] Header for the request (e.g. "key1: value1")
|
||||
-c, -cookie [string] Cookie for the request (e.g. "key1=value1")
|
||||
-x, -proxy [string] Proxy for the request (e.g. "http://proxy.example.com:8080")`
|
||||
|
||||
func (config *Config) ReadCLI() (types.ConfigFile, error) {
|
||||
flag.Usage = func() {
|
||||
fmt.Printf(
|
||||
cliUsageText+"\n",
|
||||
DefaultYes,
|
||||
DefaultDodosCount,
|
||||
DefaultRequestCount,
|
||||
DefaultTimeout,
|
||||
DefaultMethod,
|
||||
)
|
||||
}
|
||||
|
||||
var (
|
||||
version = false
|
||||
configFile = ""
|
||||
yes = false
|
||||
method = ""
|
||||
url types.RequestURL
|
||||
dodosCount = uint(0)
|
||||
requestCount = uint(0)
|
||||
timeout time.Duration
|
||||
)
|
||||
|
||||
{
|
||||
flag.BoolVar(&version, "version", false, "Prints the version of the program")
|
||||
flag.BoolVar(&version, "v", false, "Prints the version of the program")
|
||||
|
||||
flag.StringVar(&configFile, "config-file", "", "Path to the configuration file")
|
||||
flag.StringVar(&configFile, "f", "", "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.Var(&url, "url", "URL to send the request")
|
||||
flag.Var(&url, "u", "URL to send the request")
|
||||
|
||||
flag.UintVar(&dodosCount, "dodos", 0, "Number of dodos(threads)")
|
||||
flag.UintVar(&dodosCount, "d", 0, "Number of dodos(threads)")
|
||||
|
||||
flag.UintVar(&requestCount, "requests", 0, "Number of total requests")
|
||||
flag.UintVar(&requestCount, "r", 0, "Number of total requests")
|
||||
|
||||
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)")
|
||||
|
||||
flag.Var(&config.Params, "param", "URL parameter to send with the request")
|
||||
flag.Var(&config.Params, "p", "URL parameter to send with the request")
|
||||
|
||||
flag.Var(&config.Headers, "header", "Header to send with the request")
|
||||
flag.Var(&config.Headers, "H", "Header to send with the request")
|
||||
|
||||
flag.Var(&config.Cookies, "cookie", "Cookie to send with the request")
|
||||
flag.Var(&config.Cookies, "c", "Cookie to send with the request")
|
||||
|
||||
flag.Var(&config.Body, "body", "Body to send with the request")
|
||||
flag.Var(&config.Body, "b", "Body to send with the request")
|
||||
|
||||
flag.Var(&config.Proxies, "proxy", "Proxy to use for the request")
|
||||
flag.Var(&config.Proxies, "x", "Proxy to use for the request")
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if len(os.Args) <= 1 {
|
||||
flag.CommandLine.Usage()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if args := flag.Args(); len(args) > 0 {
|
||||
return types.ConfigFile(configFile), fmt.Errorf("unexpected arguments: %v", strings.Join(args, ", "))
|
||||
}
|
||||
|
||||
if version {
|
||||
fmt.Printf("dodo version %s\n", VERSION)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
flag.Visit(func(f *flag.Flag) {
|
||||
switch f.Name {
|
||||
case "method", "m":
|
||||
config.Method = utils.ToPtr(method)
|
||||
case "url", "u":
|
||||
config.URL = utils.ToPtr(url)
|
||||
case "dodos", "d":
|
||||
config.DodosCount = utils.ToPtr(dodosCount)
|
||||
case "requests", "r":
|
||||
config.RequestCount = utils.ToPtr(requestCount)
|
||||
case "timeout", "t":
|
||||
config.Timeout = &types.Timeout{Duration: timeout}
|
||||
case "yes", "y":
|
||||
config.Yes = utils.ToPtr(yes)
|
||||
}
|
||||
})
|
||||
|
||||
return types.ConfigFile(configFile), nil
|
||||
}
|
||||
|
||||
// CLIYesOrNoReader reads a yes or no answer from the command line.
|
||||
// It prompts the user with the given message and default value,
|
||||
// and returns true if the user answers "y" or "Y", and false otherwise.
|
||||
// If there is an error while reading the input, it returns false.
|
||||
// If the user simply presses enter without providing any input,
|
||||
// it returns the default value specified by the `dft` parameter.
|
||||
func CLIYesOrNoReader(message string, dft bool) bool {
|
||||
var answer string
|
||||
defaultMessage := "Y/n"
|
||||
if !dft {
|
||||
defaultMessage = "y/N"
|
||||
}
|
||||
fmt.Printf("%s [%s]: ", message, defaultMessage)
|
||||
if _, err := fmt.Scanln(&answer); err != nil {
|
||||
if err.Error() == "unexpected newline" {
|
||||
return dft
|
||||
}
|
||||
return false
|
||||
}
|
||||
if answer == "" {
|
||||
return dft
|
||||
}
|
||||
return answer == "y" || answer == "Y"
|
||||
}
|
309
config/config.go
309
config/config.go
@ -1,43 +1,73 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
. "github.com/aykhans/dodo/types"
|
||||
"github.com/aykhans/dodo/types"
|
||||
"github.com/aykhans/dodo/utils"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
)
|
||||
|
||||
const (
|
||||
VERSION string = "0.5.7"
|
||||
DefaultUserAgent string = "Dodo/" + VERSION
|
||||
ProxyCheckURL string = "https://www.google.com"
|
||||
DefaultMethod string = "GET"
|
||||
DefaultTimeout uint32 = 10000 // Milliseconds (10 seconds)
|
||||
DefaultDodosCount uint = 1
|
||||
DefaultRequestCount uint = 1
|
||||
MaxDodosCountForProxies uint = 20 // Max dodos count for proxy check
|
||||
VERSION string = "0.6.0"
|
||||
DefaultUserAgent string = "Dodo/" + VERSION
|
||||
DefaultMethod string = "GET"
|
||||
DefaultTimeout time.Duration = time.Second * 10
|
||||
DefaultDodosCount uint = 1
|
||||
DefaultRequestCount uint = 1
|
||||
DefaultYes bool = false
|
||||
)
|
||||
|
||||
var SupportedProxySchemes []string = []string{"http", "socks5", "socks5h"}
|
||||
|
||||
type RequestConfig struct {
|
||||
Method string
|
||||
URL *url.URL
|
||||
Timeout time.Duration
|
||||
DodosCount uint
|
||||
RequestCount uint
|
||||
Params map[string][]string
|
||||
Headers map[string][]string
|
||||
Cookies map[string][]string
|
||||
Proxies []Proxy
|
||||
Body []string
|
||||
Yes bool
|
||||
NoProxyCheck bool
|
||||
Method string `json:"method"`
|
||||
URL url.URL `json:"url"`
|
||||
Timeout time.Duration `json:"timeout"`
|
||||
DodosCount uint `json:"dodos"`
|
||||
RequestCount uint `json:"requests"`
|
||||
Yes bool `json:"yes"`
|
||||
Params types.Params `json:"params"`
|
||||
Headers types.Headers `json:"headers"`
|
||||
Cookies types.Cookies `json:"cookies"`
|
||||
Body types.Body `json:"body"`
|
||||
Proxies types.Proxies `json:"proxies"`
|
||||
}
|
||||
|
||||
func (config *RequestConfig) Print() {
|
||||
func NewRequestConfig(conf *Config) *RequestConfig {
|
||||
return &RequestConfig{
|
||||
Method: *conf.Method,
|
||||
URL: conf.URL.URL,
|
||||
Timeout: conf.Timeout.Duration,
|
||||
DodosCount: *conf.DodosCount,
|
||||
RequestCount: *conf.RequestCount,
|
||||
Yes: *conf.Yes,
|
||||
Params: conf.Params,
|
||||
Headers: conf.Headers,
|
||||
Cookies: conf.Cookies,
|
||||
Body: conf.Body,
|
||||
Proxies: conf.Proxies,
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *RequestConfig) GetValidDodosCountForRequests() uint {
|
||||
return min(rc.DodosCount, rc.RequestCount)
|
||||
}
|
||||
|
||||
func (rc *RequestConfig) GetMaxConns(minConns uint) uint {
|
||||
maxConns := max(
|
||||
minConns, rc.GetValidDodosCountForRequests(),
|
||||
)
|
||||
return ((maxConns * 50 / 100) + maxConns)
|
||||
}
|
||||
|
||||
func (rc *RequestConfig) Print() {
|
||||
t := table.NewWriter()
|
||||
t.SetOutputMirror(os.Stdout)
|
||||
t.SetStyle(table.StyleLight)
|
||||
@ -56,151 +86,118 @@ func (config *RequestConfig) Print() {
|
||||
WidthMax: 50},
|
||||
})
|
||||
|
||||
newHeaders := make(map[string][]string)
|
||||
newHeaders["User-Agent"] = []string{DefaultUserAgent}
|
||||
for k, v := range config.Headers {
|
||||
newHeaders[k] = v
|
||||
}
|
||||
|
||||
t.AppendHeader(table.Row{"Request Configuration"})
|
||||
t.AppendRow(table.Row{"Method", config.Method})
|
||||
t.AppendRow(table.Row{"URL", rc.URL.String()})
|
||||
t.AppendSeparator()
|
||||
t.AppendRow(table.Row{"URL", config.URL})
|
||||
t.AppendRow(table.Row{"Method", rc.Method})
|
||||
t.AppendSeparator()
|
||||
t.AppendRow(table.Row{"Timeout", config.Timeout})
|
||||
t.AppendRow(table.Row{"Timeout", rc.Timeout})
|
||||
t.AppendSeparator()
|
||||
t.AppendRow(table.Row{"Dodos", config.DodosCount})
|
||||
t.AppendRow(table.Row{"Dodos", rc.DodosCount})
|
||||
t.AppendSeparator()
|
||||
t.AppendRow(table.Row{"Requests", config.RequestCount})
|
||||
t.AppendRow(table.Row{"Requests", rc.RequestCount})
|
||||
t.AppendSeparator()
|
||||
t.AppendRow(table.Row{"Params", string(utils.PrettyJSONMarshal(config.Params, 3, "", " "))})
|
||||
t.AppendRow(table.Row{"Params", rc.Params.String()})
|
||||
t.AppendSeparator()
|
||||
t.AppendRow(table.Row{"Headers", string(utils.PrettyJSONMarshal(newHeaders, 3, "", " "))})
|
||||
t.AppendRow(table.Row{"Headers", rc.Headers.String()})
|
||||
t.AppendSeparator()
|
||||
t.AppendRow(table.Row{"Cookies", string(utils.PrettyJSONMarshal(config.Cookies, 3, "", " "))})
|
||||
t.AppendRow(table.Row{"Cookies", rc.Cookies.String()})
|
||||
t.AppendSeparator()
|
||||
t.AppendRow(table.Row{"Proxies", string(utils.PrettyJSONMarshal(config.Proxies, 3, "", " "))})
|
||||
t.AppendRow(table.Row{"Proxy", rc.Proxies.String()})
|
||||
t.AppendSeparator()
|
||||
t.AppendRow(table.Row{"Proxy Check", !config.NoProxyCheck})
|
||||
t.AppendSeparator()
|
||||
t.AppendRow(table.Row{"Body", string(utils.PrettyJSONMarshal(config.Body, 3, "", " "))})
|
||||
t.AppendRow(table.Row{"Body", rc.Body.String()})
|
||||
|
||||
t.Render()
|
||||
}
|
||||
|
||||
func (config *RequestConfig) GetValidDodosCountForRequests() uint {
|
||||
return min(config.DodosCount, config.RequestCount)
|
||||
}
|
||||
|
||||
func (config *RequestConfig) GetValidDodosCountForProxies() uint {
|
||||
return min(config.DodosCount, uint(len(config.Proxies)), MaxDodosCountForProxies)
|
||||
}
|
||||
|
||||
func (config *RequestConfig) GetMaxConns(minConns uint) uint {
|
||||
maxConns := max(
|
||||
minConns, config.GetValidDodosCountForRequests(),
|
||||
)
|
||||
return ((maxConns * 50 / 100) + maxConns)
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Method string `json:"method" validate:"http_method"` // custom validations: http_method
|
||||
URL string `json:"url" validate:"http_url,required"`
|
||||
Timeout uint32 `json:"timeout" validate:"gte=1,lte=100000"`
|
||||
DodosCount uint `json:"dodos" validate:"gte=1"`
|
||||
RequestCount uint `json:"requests" validation_name:"request-count" validate:"gte=1"`
|
||||
NoProxyCheck Option[bool] `json:"no_proxy_check"`
|
||||
Method *string `json:"method"`
|
||||
URL *types.RequestURL `json:"url"`
|
||||
Timeout *types.Timeout `json:"timeout"`
|
||||
DodosCount *uint `json:"dodos"`
|
||||
RequestCount *uint `json:"requests"`
|
||||
Yes *bool `json:"yes"`
|
||||
Params types.Params `json:"params"`
|
||||
Headers types.Headers `json:"headers"`
|
||||
Cookies types.Cookies `json:"cookies"`
|
||||
Body types.Body `json:"body"`
|
||||
Proxies types.Proxies `json:"proxy"`
|
||||
}
|
||||
|
||||
func NewConfig(
|
||||
method string,
|
||||
timeout uint32,
|
||||
dodosCount uint,
|
||||
requestCount uint,
|
||||
noProxyCheck Option[bool],
|
||||
) *Config {
|
||||
if noProxyCheck == nil {
|
||||
noProxyCheck = NewNoneOption[bool]()
|
||||
}
|
||||
|
||||
return &Config{
|
||||
Method: method,
|
||||
Timeout: timeout,
|
||||
DodosCount: dodosCount,
|
||||
RequestCount: requestCount,
|
||||
NoProxyCheck: noProxyCheck,
|
||||
}
|
||||
func NewConfig() *Config {
|
||||
return &Config{}
|
||||
}
|
||||
|
||||
func (config *Config) MergeConfigs(newConfig *Config) {
|
||||
if newConfig.Method != "" {
|
||||
func (c *Config) Validate() []error {
|
||||
var errs []error
|
||||
if utils.IsNilOrZero(c.URL) {
|
||||
errs = append(errs, errors.New("request URL is required"))
|
||||
}
|
||||
if c.URL.Scheme == "" {
|
||||
c.URL.Scheme = "http"
|
||||
}
|
||||
if c.URL.Scheme != "http" && c.URL.Scheme != "https" {
|
||||
errs = append(errs, errors.New("request URL scheme must be http or https"))
|
||||
}
|
||||
urlParams := types.Params{}
|
||||
for key, values := range c.URL.Query() {
|
||||
for _, value := range values {
|
||||
urlParams = append(urlParams, types.KeyValue[string, []string]{
|
||||
Key: key,
|
||||
Value: []string{value},
|
||||
})
|
||||
}
|
||||
}
|
||||
c.Params = append(urlParams, c.Params...)
|
||||
c.URL.RawQuery = ""
|
||||
|
||||
if utils.IsNilOrZero(c.Method) {
|
||||
errs = append(errs, errors.New("request method is required"))
|
||||
}
|
||||
if utils.IsNilOrZero(c.Timeout) {
|
||||
errs = append(errs, errors.New("request timeout must be greater than 0"))
|
||||
}
|
||||
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"))
|
||||
}
|
||||
|
||||
for i, proxy := range c.Proxies {
|
||||
if proxy.String() == "" {
|
||||
errs = append(errs, fmt.Errorf("proxies[%d]: proxy cannot be empty", i))
|
||||
} else if schema := proxy.Scheme; !slices.Contains(SupportedProxySchemes, schema) {
|
||||
errs = append(errs,
|
||||
fmt.Errorf("proxies[%d]: proxy has unsupported scheme \"%s\" (supported schemes: %s)",
|
||||
i, proxy.String(), strings.Join(SupportedProxySchemes, ", "),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func (config *Config) MergeConfig(newConfig *Config) {
|
||||
if newConfig.Method != nil {
|
||||
config.Method = newConfig.Method
|
||||
}
|
||||
if newConfig.URL != "" {
|
||||
if newConfig.URL != nil {
|
||||
config.URL = newConfig.URL
|
||||
}
|
||||
if newConfig.Timeout != 0 {
|
||||
if newConfig.Timeout != nil {
|
||||
config.Timeout = newConfig.Timeout
|
||||
}
|
||||
if newConfig.DodosCount != 0 {
|
||||
if newConfig.DodosCount != nil {
|
||||
config.DodosCount = newConfig.DodosCount
|
||||
}
|
||||
if newConfig.RequestCount != 0 {
|
||||
if newConfig.RequestCount != nil {
|
||||
config.RequestCount = newConfig.RequestCount
|
||||
}
|
||||
if !newConfig.NoProxyCheck.IsNone() {
|
||||
config.NoProxyCheck = newConfig.NoProxyCheck
|
||||
if newConfig.Yes != nil {
|
||||
config.Yes = newConfig.Yes
|
||||
}
|
||||
}
|
||||
|
||||
func (config *Config) SetDefaults() {
|
||||
if config.Method == "" {
|
||||
config.Method = DefaultMethod
|
||||
}
|
||||
if config.Timeout == 0 {
|
||||
config.Timeout = DefaultTimeout
|
||||
}
|
||||
if config.DodosCount == 0 {
|
||||
config.DodosCount = DefaultDodosCount
|
||||
}
|
||||
if config.RequestCount == 0 {
|
||||
config.RequestCount = DefaultRequestCount
|
||||
}
|
||||
if config.NoProxyCheck.IsNone() {
|
||||
config.NoProxyCheck = NewOption(false)
|
||||
}
|
||||
}
|
||||
|
||||
type Proxy struct {
|
||||
URL string `json:"url" validate:"required,proxy_url"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type JSONConfig struct {
|
||||
*Config
|
||||
Params map[string][]string `json:"params"`
|
||||
Headers map[string][]string `json:"headers"`
|
||||
Cookies map[string][]string `json:"cookies"`
|
||||
Proxies []Proxy `json:"proxies" validate:"dive"`
|
||||
Body []string `json:"body"`
|
||||
}
|
||||
|
||||
func NewJSONConfig(
|
||||
config *Config,
|
||||
params map[string][]string,
|
||||
headers map[string][]string,
|
||||
cookies map[string][]string,
|
||||
proxies []Proxy,
|
||||
body []string,
|
||||
) *JSONConfig {
|
||||
return &JSONConfig{
|
||||
config, params, headers, cookies, proxies, body,
|
||||
}
|
||||
}
|
||||
|
||||
func (config *JSONConfig) MergeConfigs(newConfig *JSONConfig) {
|
||||
config.Config.MergeConfigs(newConfig.Config)
|
||||
if len(newConfig.Params) != 0 {
|
||||
config.Params = newConfig.Params
|
||||
}
|
||||
@ -218,28 +215,20 @@ func (config *JSONConfig) MergeConfigs(newConfig *JSONConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
type CLIConfig struct {
|
||||
*Config
|
||||
Yes Option[bool] `json:"yes" validate:"omitempty"`
|
||||
ConfigFile string `validation_name:"config-file" validate:"omitempty,filepath"`
|
||||
}
|
||||
|
||||
func NewCLIConfig(
|
||||
config *Config,
|
||||
yes Option[bool],
|
||||
configFile string,
|
||||
) *CLIConfig {
|
||||
return &CLIConfig{
|
||||
config, yes, configFile,
|
||||
}
|
||||
}
|
||||
|
||||
func (config *CLIConfig) MergeConfigs(newConfig *CLIConfig) {
|
||||
config.Config.MergeConfigs(newConfig.Config)
|
||||
if newConfig.ConfigFile != "" {
|
||||
config.ConfigFile = newConfig.ConfigFile
|
||||
}
|
||||
if !newConfig.Yes.IsNone() {
|
||||
config.Yes = newConfig.Yes
|
||||
func (config *Config) SetDefaults() {
|
||||
if config.Method == nil {
|
||||
config.Method = utils.ToPtr(DefaultMethod)
|
||||
}
|
||||
if config.Timeout == nil {
|
||||
config.Timeout = &types.Timeout{Duration: DefaultTimeout}
|
||||
}
|
||||
if config.DodosCount == nil {
|
||||
config.DodosCount = utils.ToPtr(DefaultDodosCount)
|
||||
}
|
||||
if config.RequestCount == nil {
|
||||
config.RequestCount = utils.ToPtr(DefaultRequestCount)
|
||||
}
|
||||
if config.Yes == nil {
|
||||
config.Yes = utils.ToPtr(DefaultYes)
|
||||
}
|
||||
}
|
||||
|
60
config/file.go
Normal file
60
config/file.go
Normal file
@ -0,0 +1,60 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/aykhans/dodo/types"
|
||||
)
|
||||
|
||||
func (config *Config) ReadFile(filePath types.ConfigFile) error {
|
||||
var (
|
||||
data []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if filePath.LocationType() == types.FileLocationTypeRemoteHTTP {
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
resp, err := client.Get(filePath.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch config file from %s", filePath)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err = io.ReadAll(io.Reader(resp.Body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read config file from %s", filePath)
|
||||
}
|
||||
} else {
|
||||
data, err = os.ReadFile(filePath.String())
|
||||
if err != nil {
|
||||
return errors.New("failed to read config file from " + filePath.String())
|
||||
}
|
||||
}
|
||||
|
||||
return parseJSONConfig(data, config)
|
||||
}
|
||||
|
||||
func parseJSONConfig(data []byte, config *Config) error {
|
||||
err := json.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
switch parsedErr := err.(type) {
|
||||
case *json.SyntaxError:
|
||||
return fmt.Errorf("JSON Config file: invalid syntax at byte offset %d", parsedErr.Offset)
|
||||
case *json.UnmarshalTypeError:
|
||||
return fmt.Errorf("JSON Config file: invalid type %v for field %s, expected %v", parsedErr.Value, parsedErr.Field, parsedErr.Type)
|
||||
default:
|
||||
return fmt.Errorf("JSON Config file: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user