mirror of
https://github.com/aykhans/sarin.git
synced 2026-02-28 14:59:14 +00:00
Build request data through RequestData struct instead of fasthttp directly
Refactor request generators to populate a script.RequestData intermediate struct, then apply it to fasthttp.Request in one step. This eliminates the round-trip conversion (fasthttp → RequestData → fasthttp) when scripts are enabled. Remove the URL field from RequestData and the now-unused fasthttp conversion functions from chain.go.
This commit is contained in:
@@ -17,7 +17,7 @@ import (
|
|||||||
|
|
||||||
type RequestGenerator func(*fasthttp.Request) error
|
type RequestGenerator func(*fasthttp.Request) error
|
||||||
|
|
||||||
type RequestGeneratorWithData func(*fasthttp.Request, any) error
|
type requestDataGenerator func(*script.RequestData, any) error
|
||||||
|
|
||||||
type valuesData struct {
|
type valuesData struct {
|
||||||
Values map[string]string
|
Values map[string]string
|
||||||
@@ -59,13 +59,22 @@ func NewRequestGenerator(
|
|||||||
|
|
||||||
hasScripts := scriptTransformer != nil && !scriptTransformer.IsEmpty()
|
hasScripts := scriptTransformer != nil && !scriptTransformer.IsEmpty()
|
||||||
|
|
||||||
|
host := requestURL.Host
|
||||||
|
scheme := requestURL.Scheme
|
||||||
|
|
||||||
|
reqData := &script.RequestData{
|
||||||
|
Headers: make(map[string][]string),
|
||||||
|
Params: make(map[string][]string),
|
||||||
|
Cookies: make(map[string][]string),
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
data valuesData
|
data valuesData
|
||||||
path string
|
path string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
return func(req *fasthttp.Request) error {
|
return func(req *fasthttp.Request) error {
|
||||||
req.Header.SetHost(requestURL.Host)
|
resetRequestData(reqData)
|
||||||
|
|
||||||
data, err = valuesGenerator()
|
data, err = valuesGenerator()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -76,44 +85,39 @@ func NewRequestGenerator(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
req.SetRequestURI(path)
|
reqData.Path = path
|
||||||
|
|
||||||
if err = methodGenerator(req, data); err != nil {
|
if err = methodGenerator(reqData, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyTemplateFuncMapData.ClearFormDataContenType()
|
bodyTemplateFuncMapData.ClearFormDataContenType()
|
||||||
if err = bodyGenerator(req, data); err != nil {
|
if err = bodyGenerator(reqData, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = headersGenerator(req, data); err != nil {
|
if err = headersGenerator(reqData, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if bodyTemplateFuncMapData.GetFormDataContenType() != "" {
|
if bodyTemplateFuncMapData.GetFormDataContenType() != "" {
|
||||||
req.Header.Add("Content-Type", bodyTemplateFuncMapData.GetFormDataContenType())
|
reqData.Headers["Content-Type"] = append(reqData.Headers["Content-Type"], bodyTemplateFuncMapData.GetFormDataContenType())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = paramsGenerator(req, data); err != nil {
|
if err = paramsGenerator(reqData, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = cookiesGenerator(req, data); err != nil {
|
if err = cookiesGenerator(reqData, data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if requestURL.Scheme == "https" {
|
|
||||||
req.URI().SetScheme("https")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply script transformations if any
|
|
||||||
if hasScripts {
|
if hasScripts {
|
||||||
reqData := script.RequestDataFromFastHTTP(req)
|
|
||||||
if err = scriptTransformer.Transform(reqData); err != nil {
|
if err = scriptTransformer.Transform(reqData); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
script.ApplyToFastHTTP(reqData, req)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyRequestDataToFastHTTP(reqData, req, host, scheme)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}, isPathGeneratorDynamic ||
|
}, isPathGeneratorDynamic ||
|
||||||
isMethodGeneratorDynamic ||
|
isMethodGeneratorDynamic ||
|
||||||
@@ -124,50 +128,92 @@ func NewRequestGenerator(
|
|||||||
hasScripts
|
hasScripts
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMethodGeneratorFunc(localRand *rand.Rand, methods []string, templateFunctions template.FuncMap) (RequestGeneratorWithData, bool) {
|
func resetRequestData(reqData *script.RequestData) {
|
||||||
|
reqData.Method = ""
|
||||||
|
reqData.Path = ""
|
||||||
|
reqData.Body = ""
|
||||||
|
clear(reqData.Headers)
|
||||||
|
clear(reqData.Params)
|
||||||
|
clear(reqData.Cookies)
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyRequestDataToFastHTTP(reqData *script.RequestData, req *fasthttp.Request, host, scheme string) {
|
||||||
|
req.Header.SetHost(host)
|
||||||
|
req.SetRequestURI(reqData.Path)
|
||||||
|
req.Header.SetMethod(reqData.Method)
|
||||||
|
req.SetBody([]byte(reqData.Body))
|
||||||
|
|
||||||
|
for k, values := range reqData.Headers {
|
||||||
|
for _, v := range values {
|
||||||
|
req.Header.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, values := range reqData.Params {
|
||||||
|
for _, v := range values {
|
||||||
|
req.URI().QueryArgs().Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(reqData.Cookies) > 0 {
|
||||||
|
cookieStrings := make([]string, 0, len(reqData.Cookies))
|
||||||
|
for k, values := range reqData.Cookies {
|
||||||
|
for _, v := range values {
|
||||||
|
cookieStrings = append(cookieStrings, k+"="+v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.Header.Add("Cookie", strings.Join(cookieStrings, "; "))
|
||||||
|
}
|
||||||
|
|
||||||
|
if scheme == "https" {
|
||||||
|
req.URI().SetScheme("https")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMethodGeneratorFunc(localRand *rand.Rand, methods []string, templateFunctions template.FuncMap) (requestDataGenerator, bool) {
|
||||||
methodGenerator, isDynamic := buildStringSliceGenerator(localRand, methods, templateFunctions)
|
methodGenerator, isDynamic := buildStringSliceGenerator(localRand, methods, templateFunctions)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
method string
|
method string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
return func(req *fasthttp.Request, data any) error {
|
return func(reqData *script.RequestData, data any) error {
|
||||||
method, err = methodGenerator()(data)
|
method, err = methodGenerator()(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.SetMethod(method)
|
reqData.Method = method
|
||||||
return nil
|
return nil
|
||||||
}, isDynamic
|
}, isDynamic
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBodyGeneratorFunc(localRand *rand.Rand, bodies []string, templateFunctions template.FuncMap) (RequestGeneratorWithData, bool) {
|
func NewBodyGeneratorFunc(localRand *rand.Rand, bodies []string, templateFunctions template.FuncMap) (requestDataGenerator, bool) {
|
||||||
bodyGenerator, isDynamic := buildStringSliceGenerator(localRand, bodies, templateFunctions)
|
bodyGenerator, isDynamic := buildStringSliceGenerator(localRand, bodies, templateFunctions)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
body string
|
body string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
return func(req *fasthttp.Request, data any) error {
|
return func(reqData *script.RequestData, data any) error {
|
||||||
body, err = bodyGenerator()(data)
|
body, err = bodyGenerator()(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.SetBody([]byte(body))
|
reqData.Body = body
|
||||||
return nil
|
return nil
|
||||||
}, isDynamic
|
}, isDynamic
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewParamsGeneratorFunc(localRand *rand.Rand, params types.Params, templateFunctions template.FuncMap) (RequestGeneratorWithData, bool) {
|
func NewParamsGeneratorFunc(localRand *rand.Rand, params types.Params, templateFunctions template.FuncMap) (requestDataGenerator, bool) {
|
||||||
generators, isDynamic := buildKeyValueGenerators(localRand, params, templateFunctions)
|
generators, isDynamic := buildKeyValueGenerators(localRand, params, templateFunctions)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
key, value string
|
key, value string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
return func(req *fasthttp.Request, data any) error {
|
return func(reqData *script.RequestData, data any) error {
|
||||||
for _, gen := range generators {
|
for _, gen := range generators {
|
||||||
key, err = gen.Key(data)
|
key, err = gen.Key(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -179,20 +225,20 @@ func NewParamsGeneratorFunc(localRand *rand.Rand, params types.Params, templateF
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.URI().QueryArgs().Add(key, value)
|
reqData.Params[key] = append(reqData.Params[key], value)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, isDynamic
|
}, isDynamic
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHeadersGeneratorFunc(localRand *rand.Rand, headers types.Headers, templateFunctions template.FuncMap) (RequestGeneratorWithData, bool) {
|
func NewHeadersGeneratorFunc(localRand *rand.Rand, headers types.Headers, templateFunctions template.FuncMap) (requestDataGenerator, bool) {
|
||||||
generators, isDynamic := buildKeyValueGenerators(localRand, headers, templateFunctions)
|
generators, isDynamic := buildKeyValueGenerators(localRand, headers, templateFunctions)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
key, value string
|
key, value string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
return func(req *fasthttp.Request, data any) error {
|
return func(reqData *script.RequestData, data any) error {
|
||||||
for _, gen := range generators {
|
for _, gen := range generators {
|
||||||
key, err = gen.Key(data)
|
key, err = gen.Key(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -204,41 +250,33 @@ func NewHeadersGeneratorFunc(localRand *rand.Rand, headers types.Headers, templa
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add(key, value)
|
reqData.Headers[key] = append(reqData.Headers[key], value)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, isDynamic
|
}, isDynamic
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCookiesGeneratorFunc(localRand *rand.Rand, cookies types.Cookies, templateFunctions template.FuncMap) (RequestGeneratorWithData, bool) {
|
func NewCookiesGeneratorFunc(localRand *rand.Rand, cookies types.Cookies, templateFunctions template.FuncMap) (requestDataGenerator, bool) {
|
||||||
generators, isDynamic := buildKeyValueGenerators(localRand, cookies, templateFunctions)
|
generators, isDynamic := buildKeyValueGenerators(localRand, cookies, templateFunctions)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
key, value string
|
key, value string
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if len(generators) > 0 {
|
return func(reqData *script.RequestData, data any) error {
|
||||||
return func(req *fasthttp.Request, data any) error {
|
for _, gen := range generators {
|
||||||
cookieStrings := make([]string, 0, len(generators))
|
key, err = gen.Key(data)
|
||||||
for _, gen := range generators {
|
if err != nil {
|
||||||
key, err = gen.Key(data)
|
return err
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
value, err = gen.Value()(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cookieStrings = append(cookieStrings, key+"="+value)
|
|
||||||
}
|
}
|
||||||
req.Header.Add("Cookie", strings.Join(cookieStrings, "; "))
|
|
||||||
return nil
|
|
||||||
}, isDynamic
|
|
||||||
}
|
|
||||||
|
|
||||||
return func(req *fasthttp.Request, data any) error {
|
value, err = gen.Value()(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
reqData.Cookies[key] = append(reqData.Cookies[key], value)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}, isDynamic
|
}, isDynamic
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package script
|
package script
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/valyala/fasthttp"
|
|
||||||
"go.aykhans.me/sarin/internal/types"
|
"go.aykhans.me/sarin/internal/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -106,83 +105,3 @@ func (t *Transformer) Close() {
|
|||||||
func (t *Transformer) IsEmpty() bool {
|
func (t *Transformer) IsEmpty() bool {
|
||||||
return len(t.luaEngines) == 0 && len(t.jsEngines) == 0
|
return len(t.luaEngines) == 0 && len(t.jsEngines) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestDataFromFastHTTP extracts RequestData from a fasthttp.Request.
|
|
||||||
func RequestDataFromFastHTTP(req *fasthttp.Request) *RequestData {
|
|
||||||
data := &RequestData{
|
|
||||||
Method: string(req.Header.Method()),
|
|
||||||
URL: string(req.URI().FullURI()),
|
|
||||||
Path: string(req.URI().Path()),
|
|
||||||
Body: string(req.Body()),
|
|
||||||
Headers: make(map[string][]string),
|
|
||||||
Params: make(map[string][]string),
|
|
||||||
Cookies: make(map[string][]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract headers (supports multiple values per key)
|
|
||||||
req.Header.All()(func(key, value []byte) bool {
|
|
||||||
k := string(key)
|
|
||||||
data.Headers[k] = append(data.Headers[k], string(value))
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
// Extract query params (supports multiple values per key)
|
|
||||||
req.URI().QueryArgs().All()(func(key, value []byte) bool {
|
|
||||||
k := string(key)
|
|
||||||
data.Params[k] = append(data.Params[k], string(value))
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
// Extract cookies (supports multiple values per key)
|
|
||||||
req.Header.Cookies()(func(key, value []byte) bool {
|
|
||||||
k := string(key)
|
|
||||||
data.Cookies[k] = append(data.Cookies[k], string(value))
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
// ApplyToFastHTTP applies the modified RequestData back to a fasthttp.Request.
|
|
||||||
func ApplyToFastHTTP(data *RequestData, req *fasthttp.Request) {
|
|
||||||
// Method
|
|
||||||
req.Header.SetMethod(data.Method)
|
|
||||||
|
|
||||||
// Path (preserve scheme and host)
|
|
||||||
req.URI().SetPath(data.Path)
|
|
||||||
|
|
||||||
// Body
|
|
||||||
req.SetBody([]byte(data.Body))
|
|
||||||
|
|
||||||
// Clear and set headers (supports multiple values per key)
|
|
||||||
req.Header.All()(func(key, _ []byte) bool {
|
|
||||||
keyStr := string(key)
|
|
||||||
if keyStr != "Host" {
|
|
||||||
req.Header.Del(keyStr)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
for k, values := range data.Headers {
|
|
||||||
if k != "Host" { // Don't overwrite Host
|
|
||||||
for _, v := range values {
|
|
||||||
req.Header.Add(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear and set query params (supports multiple values per key)
|
|
||||||
req.URI().QueryArgs().Reset()
|
|
||||||
for k, values := range data.Params {
|
|
||||||
for _, v := range values {
|
|
||||||
req.URI().QueryArgs().Add(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear and set cookies (supports multiple values per key)
|
|
||||||
req.Header.DelAllCookies()
|
|
||||||
for k, values := range data.Cookies {
|
|
||||||
for _, v := range values {
|
|
||||||
req.Header.SetCookie(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -86,7 +86,6 @@ func (e *JsEngine) requestDataToObject(req *RequestData) goja.Value {
|
|||||||
obj := e.runtime.NewObject()
|
obj := e.runtime.NewObject()
|
||||||
|
|
||||||
_ = obj.Set("method", req.Method)
|
_ = obj.Set("method", req.Method)
|
||||||
_ = obj.Set("url", req.URL)
|
|
||||||
_ = obj.Set("path", req.Path)
|
_ = obj.Set("path", req.Path)
|
||||||
_ = obj.Set("body", req.Body)
|
_ = obj.Set("body", req.Body)
|
||||||
|
|
||||||
@@ -130,11 +129,6 @@ func (e *JsEngine) objectToRequestData(val goja.Value, req *RequestData) error {
|
|||||||
req.Method = v.String()
|
req.Method = v.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL
|
|
||||||
if v := obj.Get("url"); v != nil && !goja.IsUndefined(v) {
|
|
||||||
req.URL = v.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path
|
// Path
|
||||||
if v := obj.Get("path"); v != nil && !goja.IsUndefined(v) {
|
if v := obj.Get("path"); v != nil && !goja.IsUndefined(v) {
|
||||||
req.Path = v.String()
|
req.Path = v.String()
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ func (e *LuaEngine) requestDataToTable(req *RequestData) *lua.LTable {
|
|||||||
t := L.NewTable()
|
t := L.NewTable()
|
||||||
|
|
||||||
t.RawSetString("method", lua.LString(req.Method))
|
t.RawSetString("method", lua.LString(req.Method))
|
||||||
t.RawSetString("url", lua.LString(req.URL))
|
|
||||||
t.RawSetString("path", lua.LString(req.Path))
|
t.RawSetString("path", lua.LString(req.Path))
|
||||||
t.RawSetString("body", lua.LString(req.Body))
|
t.RawSetString("body", lua.LString(req.Body))
|
||||||
|
|
||||||
@@ -137,11 +136,6 @@ func (e *LuaEngine) tableToRequestData(t *lua.LTable, req *RequestData) {
|
|||||||
req.Method = string(v.(lua.LString))
|
req.Method = string(v.(lua.LString))
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL
|
|
||||||
if v := t.RawGetString("url"); v.Type() == lua.LTString {
|
|
||||||
req.URL = string(v.(lua.LString))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path
|
// Path
|
||||||
if v := t.RawGetString("path"); v.Type() == lua.LTString {
|
if v := t.RawGetString("path"); v.Type() == lua.LTString {
|
||||||
req.Path = string(v.(lua.LString))
|
req.Path = string(v.(lua.LString))
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
// Headers, Params, and Cookies use []string values to support multiple values per key.
|
// Headers, Params, and Cookies use []string values to support multiple values per key.
|
||||||
type RequestData struct {
|
type RequestData struct {
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
URL string `json:"url"`
|
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Headers map[string][]string `json:"headers"`
|
Headers map[string][]string `json:"headers"`
|
||||||
Params map[string][]string `json:"params"`
|
Params map[string][]string `json:"params"`
|
||||||
|
|||||||
Reference in New Issue
Block a user