mirror of
https://github.com/aykhans/sarin.git
synced 2026-02-28 14:59:14 +00:00
add scripting js/lua
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/valyala/fasthttp"
|
||||
"go.aykhans.me/sarin/internal/script"
|
||||
"go.aykhans.me/sarin/internal/types"
|
||||
utilsSlice "go.aykhans.me/utils/slice"
|
||||
)
|
||||
@@ -26,6 +27,9 @@ type valuesData struct {
|
||||
// NewRequestGenerator creates a new RequestGenerator function that generates HTTP requests
|
||||
// with the specified configuration. The returned RequestGenerator is NOT safe for concurrent
|
||||
// use by multiple goroutines.
|
||||
//
|
||||
// Note: Scripts must be validated before calling this function (e.g., in NewSarin).
|
||||
// The caller is responsible for managing the scriptTransformer lifecycle.
|
||||
func NewRequestGenerator(
|
||||
methods []string,
|
||||
requestURL *url.URL,
|
||||
@@ -35,6 +39,7 @@ func NewRequestGenerator(
|
||||
bodies []string,
|
||||
values []string,
|
||||
fileCache *FileCache,
|
||||
scriptTransformer *script.Transformer,
|
||||
) (RequestGenerator, bool) {
|
||||
randSource := NewDefaultRandSource()
|
||||
//nolint:gosec // G404: Using non-cryptographic rand for load testing, not security
|
||||
@@ -53,6 +58,8 @@ func NewRequestGenerator(
|
||||
|
||||
valuesGenerator := NewValuesGeneratorFunc(values, templateFuncMap)
|
||||
|
||||
hasScripts := scriptTransformer != nil && !scriptTransformer.IsEmpty()
|
||||
|
||||
var (
|
||||
data valuesData
|
||||
path string
|
||||
@@ -98,13 +105,24 @@ func NewRequestGenerator(
|
||||
if requestURL.Scheme == "https" {
|
||||
req.URI().SetScheme("https")
|
||||
}
|
||||
|
||||
// Apply script transformations if any
|
||||
if hasScripts {
|
||||
reqData := script.RequestDataFromFastHTTP(req)
|
||||
if err = scriptTransformer.Transform(reqData); err != nil {
|
||||
return err
|
||||
}
|
||||
script.ApplyToFastHTTP(reqData, req)
|
||||
}
|
||||
|
||||
return nil
|
||||
}, isPathGeneratorDynamic ||
|
||||
isMethodGeneratorDynamic ||
|
||||
isParamsGeneratorDynamic ||
|
||||
isHeadersGeneratorDynamic ||
|
||||
isCookiesGeneratorDynamic ||
|
||||
isBodyGeneratorDynamic
|
||||
isBodyGeneratorDynamic ||
|
||||
hasScripts
|
||||
}
|
||||
|
||||
func NewMethodGeneratorFunc(localRand *rand.Rand, methods []string, templateFunctions template.FuncMap) (RequestGeneratorWithData, bool) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/valyala/fasthttp"
|
||||
"go.aykhans.me/sarin/internal/script"
|
||||
"go.aykhans.me/sarin/internal/types"
|
||||
)
|
||||
|
||||
@@ -52,11 +53,13 @@ type sarin struct {
|
||||
hostClients []*fasthttp.HostClient
|
||||
responses *SarinResponseData
|
||||
fileCache *FileCache
|
||||
scriptChain *script.Chain
|
||||
}
|
||||
|
||||
// NewSarin creates a new sarin instance for load testing.
|
||||
// It can return the following errors:
|
||||
// - types.ProxyDialError
|
||||
// - script loading errors
|
||||
func NewSarin(
|
||||
ctx context.Context,
|
||||
methods []string,
|
||||
@@ -75,6 +78,8 @@ func NewSarin(
|
||||
values []string,
|
||||
collectStats bool,
|
||||
dryRun bool,
|
||||
luaScripts []string,
|
||||
jsScripts []string,
|
||||
) (*sarin, error) {
|
||||
if workers == 0 {
|
||||
workers = 1
|
||||
@@ -85,6 +90,19 @@ func NewSarin(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Load script sources
|
||||
luaSources, err := script.LoadSources(ctx, luaScripts, script.EngineTypeLua)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsSources, err := script.LoadSources(ctx, jsScripts, script.EngineTypeJavaScript)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scriptChain := script.NewChain(luaSources, jsSources)
|
||||
|
||||
srn := &sarin{
|
||||
workers: workers,
|
||||
requestURL: requestURL,
|
||||
@@ -103,6 +121,7 @@ func NewSarin(
|
||||
dryRun: dryRun,
|
||||
hostClients: hostClients,
|
||||
fileCache: NewFileCache(time.Second * 10),
|
||||
scriptChain: scriptChain,
|
||||
}
|
||||
|
||||
if collectStats {
|
||||
@@ -193,7 +212,20 @@ func (q sarin) Worker(
|
||||
defer fasthttp.ReleaseRequest(req)
|
||||
defer fasthttp.ReleaseResponse(resp)
|
||||
|
||||
requestGenerator, isDynamic := NewRequestGenerator(q.methods, q.requestURL, q.params, q.headers, q.cookies, q.bodies, q.values, q.fileCache)
|
||||
// Create script transformer for this worker (engines are not thread-safe)
|
||||
// Scripts are pre-validated in NewSarin, so this should not fail
|
||||
var scriptTransformer *script.Transformer
|
||||
if !q.scriptChain.IsEmpty() {
|
||||
scriptTransformer, err := q.scriptChain.NewTransformer()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer scriptTransformer.Close()
|
||||
}
|
||||
|
||||
requestGenerator, isDynamic := NewRequestGenerator(
|
||||
q.methods, q.requestURL, q.params, q.headers, q.cookies, q.bodies, q.values, q.fileCache, scriptTransformer,
|
||||
)
|
||||
|
||||
if q.dryRun {
|
||||
switch {
|
||||
|
||||
Reference in New Issue
Block a user