From f248c2af9696dd856b96c1e6e5b64d9b882dc915 Mon Sep 17 00:00:00 2001 From: Aykhan Shahsuvarov Date: Fri, 30 May 2025 10:40:20 +0400 Subject: [PATCH] Value generator initial commit --- go.mod | 1 + go.sum | 2 + requests/request.go | 116 ++++++++++++++++++++++++++++---------------- requests/run.go | 2 +- utils/slice.go | 10 ++-- 5 files changed, 81 insertions(+), 50 deletions(-) diff --git a/go.mod b/go.mod index 86b726e..29b3897 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.24.2 require ( github.com/jedib0t/go-pretty/v6 v6.6.7 github.com/valyala/fasthttp v1.62.0 + github.com/brianvoe/gofakeit/v7 v7.2.1 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 85194b4..01ce699 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +github.com/brianvoe/gofakeit/v7 v7.2.1 h1:AGojgaaCdgq4Adzrd2uWdbGNDyX6MWNhHdQBraNfOHI= +github.com/brianvoe/gofakeit/v7 v7.2.1/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= 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/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo= diff --git a/requests/request.go b/requests/request.go index 8624655..c255a26 100644 --- a/requests/request.go +++ b/requests/request.go @@ -1,14 +1,18 @@ package requests import ( + "bytes" "context" "math/rand" "net/url" + "strings" + "text/template" "time" "github.com/aykhans/dodo/config" "github.com/aykhans/dodo/types" "github.com/aykhans/dodo/utils" + "github.com/brianvoe/gofakeit/v7" "github.com/valyala/fasthttp" ) @@ -21,6 +25,11 @@ type Request struct { getRequest RequestGeneratorFunc } +type keyValueGenerator struct { + key func() string + value func() string +} + // Send sends the HTTP request using the fasthttp client with a specified timeout. // It returns the HTTP response or an error if the request fails or times out. func (r *Request) Send(ctx context.Context, timeout time.Duration) (*fasthttp.Response, error) { @@ -101,17 +110,10 @@ func getRequestGeneratorFunc( bodies []string, localRand *rand.Rand, ) RequestGeneratorFunc { - bodiesLen := len(bodies) - getBody := func() string { return "" } - if bodiesLen == 1 { - getBody = func() string { return bodies[0] } - } else if bodiesLen > 1 { - getBody = utils.RandomValueCycle(bodies, localRand) - } - getParams := getKeyValueGeneratorFunc(params, localRand) getHeaders := getKeyValueGeneratorFunc(headers, localRand) getCookies := getKeyValueGeneratorFunc(cookies, localRand) + getBody := getValueFunc(bodies, newFuncMap(localRand), localRand) return func() *fasthttp.Request { return newFasthttpRequest( @@ -199,45 +201,75 @@ func getKeyValueGeneratorFunc[ keyValueSlice []types.KeyValue[string, []string], localRand *rand.Rand, ) func() T { - getKeyValueSlice := []map[string]func() string{} - isRandom := false + keyValueGenerators := make([]keyValueGenerator, len(keyValueSlice)) - for _, kv := range keyValueSlice { - if valuesLen := len(kv.Value); valuesLen > 1 { - isRandom = true + funcMap := newFuncMap(localRand) + + for i, kv := range keyValueSlice { + keyValueGenerators[i] = keyValueGenerator{ + key: getKeyFunc(kv.Key, funcMap), + value: getValueFunc(kv.Value, funcMap, localRand), } - - getKeyValueSlice = append( - getKeyValueSlice, - map[string]func() string{ - kv.Key: utils.RandomValueCycle(kv.Value, localRand), - }, - ) } - if isRandom { - return func() T { - keyValues := make(T, len(getKeyValueSlice)) - for i, keyValue := range getKeyValueSlice { - for key, value := range keyValue { - keyValues[i] = types.KeyValue[string, string]{ - Key: key, - Value: value(), - } - } - } - return keyValues - } - } else { - keyValues := make(T, len(getKeyValueSlice)) - for i, keyValue := range getKeyValueSlice { - for key, value := range keyValue { - keyValues[i] = types.KeyValue[string, string]{ - Key: key, - Value: value(), - } + return func() T { + keyValues := make(T, len(keyValueGenerators)) + for i, keyValue := range keyValueGenerators { + keyValues[i] = types.KeyValue[string, string]{ + Key: keyValue.key(), + Value: keyValue.value(), } } - return func() T { return keyValues } + return keyValues + } +} + +func getKeyFunc(key string, funcMap template.FuncMap) func() string { + t, err := template.New("default").Funcs(funcMap).Parse(key) + if err != nil { + panic(err) + } + + return func() string { + var buf bytes.Buffer + _ = t.Execute(&buf, nil) + return buf.String() + } +} + +func getValueFunc( + values []string, + funcMap template.FuncMap, + localRand *rand.Rand, +) func() string { + templates := make([]*template.Template, len(values)) + + for i, value := range values { + t, err := template.New("default").Funcs(funcMap).Parse(value) + if err != nil { + panic(err) + } + templates[i] = t + } + + randomTemplateFunc := utils.RandomValueCycle(templates, localRand) + + return func() string { + if tmpl := randomTemplateFunc(); tmpl == nil { + return "" + } else { + var buf bytes.Buffer + _ = tmpl.Execute(&buf, nil) + return buf.String() + } + } +} + +func newFuncMap(localRand *rand.Rand) template.FuncMap { + localFaker := gofakeit.NewFaker(localRand, false) + + return template.FuncMap{ + "upper": strings.ToUpper, + "fakeit_Name": localFaker.Name, } } diff --git a/requests/run.go b/requests/run.go index 3441533..2095a2b 100644 --- a/requests/run.go +++ b/requests/run.go @@ -72,7 +72,7 @@ func releaseDodos( wg.Add(int(dodosCount)) streamWG.Add(1) - streamCtx, streamCtxCancel := context.WithCancel(context.Background()) + streamCtx, streamCtxCancel := context.WithCancel(ctx) go streamProgress(streamCtx, &streamWG, requestConfig.RequestCount, "Dodos Working🔥", increase) diff --git a/utils/slice.go b/utils/slice.go index b3d5f4e..0edeac9 100644 --- a/utils/slice.go +++ b/utils/slice.go @@ -19,19 +19,15 @@ func Flatten[T any](nested [][]*T) []*T { // reset and start cycling through the values in a random order again. // The returned function isn't thread-safe and should be used in a single-threaded context. func RandomValueCycle[T any](values []T, localRand *rand.Rand) func() T { - var ( - valuesLen = len(values) - currentIndex = localRand.Intn(valuesLen) - stopIndex = currentIndex - ) - - switch valuesLen { + switch valuesLen := len(values); valuesLen { case 0: var zero T return func() T { return zero } case 1: return func() T { return values[0] } default: + currentIndex := localRand.Intn(valuesLen) + stopIndex := currentIndex return func() T { value := values[currentIndex] currentIndex++