更新时间:2023-06-28 GMT+08:00

自定义样例

自定义请求样例

自定义请求Go样例:

package main

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"strings"
	"sync"
)

var (
	trs  = sync.Map{}
	jars = sync.Map{}
)

func getTr(addr string) *http.Transport {
	if v, ok := trs.Load(addr); ok {
		return v.(*http.Transport)
	} else {
		tr := &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true,
			},
			MaxConnsPerHost:     5000,
			MaxIdleConnsPerHost: 5000,
		}
		v, _ = trs.LoadOrStore(addr, tr)
		return v.(*http.Transport)
	}
}

func getJar(goroutineId string) *cookiejar.Jar {
	if v, ok := jars.Load(goroutineId); ok {
		return v.(*cookiejar.Jar)
	} else {
		jar, _ := cookiejar.New(nil)
		v, _ = jars.LoadOrStore(goroutineId, jar)
		return v.(*cookiejar.Jar)
	}
}

func outgoingLength(req *http.Request) int64 {
	if req.Body == nil || req.Body == http.NoBody {
		return 0
	}
	if req.ContentLength != 0 {
		return req.ContentLength
	}
	return -1
}

func redirectBehavior(reqMethod string, resp *http.Response, req *http.Request) (redirectMethod string, shouldRedirect, includeBody bool) {
	switch resp.StatusCode {
	case 301, 302, 303:
		redirectMethod = reqMethod
		shouldRedirect = true
		includeBody = false

		if reqMethod != "GET" && reqMethod != "HEAD" {
			redirectMethod = "GET"
		}
	case 307, 308:
		redirectMethod = reqMethod
		shouldRedirect = true
		includeBody = true

		if resp.Header.Get("Location") == "" {
			shouldRedirect = false
			break
		}
		if req.GetBody == nil && outgoingLength(req) != 0 {
			shouldRedirect = false
		}
	}
	return redirectMethod, shouldRedirect, includeBody
}

func Do(params map[string]string) interface{} {
	parent := acquireResult(params[InnerVarName])
	sub := parent
	var (
		err           error
		redirectCount int
		req           *http.Request
		res           *http.Response
	)
	defer func() {
		if p := recover(); p != nil {
			sub.ResponseCode = 500
			sub.Success = false
			sub.FailureMessage = fmt.Sprintf("%v", sub)
		} else if err != nil {
			sub.ResponseCode = 500
			sub.Success = false
			sub.FailureMessage = err.Error()
		}
	}()

	redirect := params["redirect"]
	for {
		// process params
		url, method, body := params["url"], params["method"], params["body"]
		sub.Url, sub.Method, sub.RequestBody = url, method, body
		sub.SentBytes = len(body)

		// create request
		req, err = http.NewRequest(method, url, strings.NewReader(body))
		if err != nil {
			return parent
		}

		// load cookies
		jar := getJar(params[InnerVarGoroutineId])
		cookies := jar.Cookies(req.URL)
		for _, c := range cookies {
			req.AddCookie(c)
		}

		// write headers
		if params["headers"] != "" {
			headers := map[string][]string{}
			if err = json.Unmarshal([]byte(params["headers"]), &headers); err != nil {
				return parent
			}
			for k, vs := range headers {
				for _, v := range vs {
					req.Header.Add(k, v)
				}
			}
		}

		for k, v := range req.Header {
			sub.RequestHeader[k] = strings.Join(v, ",")
		}

		// do request
		sub.begin()
		res, err = getTr(fmt.Sprintf("%s://%s", req.URL.Scheme, req.URL.Host)).RoundTrip(req)
		sub.end()
		if err != nil {
			return parent
		}
		var b []byte
		b, err = ioutil.ReadAll(res.Body)
		if err != nil {
			_ = res.Body.Close()
			return parent
		}

		// store cookies
		jar.SetCookies(req.URL, res.Cookies())

		for k, v := range res.Header {
			sub.ResponseHeader[k] = strings.Join(v, ",")
		}

		sub.ResponseBody = string(b)
		sub.ReceivedBytes = len(b)
		sub.ResponseCode = res.StatusCode
		_ = res.Body.Close()
		if redirect != "on" {
			break
		}
		if redirectMethod, shouldRedirect, includeBody := redirectBehavior(method, res, req); shouldRedirect {
			redirectCount++
			if redirectCount > 10 {
				err = errors.New("stopped after 10 redirects")
				return parent
			}
			params["url"] = res.Header.Get("Location")
			params["method"] = redirectMethod
			if !includeBody {
				params["body"] = ""
			}
			parent = acquireResult(params[InnerVarName])
			sub = parent
			continue
		}
		break
	}
	return parent
}

自定义请求JSON样例:

{
  "name": "Http 1.0",
  "plugin": "http.so",
  "desc": "Http plugin supports http 1.0 test.",
  "funcs": [
    {
      "name": "Do",
      "type": "req",
      "desc": "This is the entry of http request.",
      "params": [
        {
          "name": "url",
          "default": "",
          "range": [],
          "desc": "The url of http request",
          "required": true
        },
        {
          "name": "headers",
          "default": "{\"Content-Type\": [\"text/plain;utf-8\"]}",
          "range": [],
          "desc": "The headers of http request",
          "required": true
        },
        {
          "name": "method",
          "default": "GET",
          "range": [
            "GET",
            "HEAD",
            "POST",
            "PUT",
            "PATCH",
            "DELETE",
            "CONNECT",
            "OPTIONS",
            "TRACE"
          ],
          "desc": "The method of http request",
          "required": true
        },
        {
          "name": "body",
          "default": "",
          "range": [],
          "desc": "The body of http request",
          "required": true
        },
        {
          "name": "redirect",
          "default": "on",
          "range": [
            "on",
            "off"
          ],
          "desc": "If enable auto redirect",
          "required": true
        }
      ]
    }
  ]
}

自定义函数样例

自定义函数Go样例:

package main

import (
	"fmt"
	"math/rand"
	"strconv"
)

func RandomNum(params map[string]string) (s string) {
	var err error
	defer func() {
		if r := recover(); r != nil {
			s = fmt.Sprintf("%v", r)
		} else if err != nil {
			s = err.Error()
		}
	}()

	min, err := strconv.ParseInt(params["min"], 10, 64)
	if err != nil {
		return
	}
	max, err := strconv.ParseInt(params["max"], 10, 64)
	if err != nil {
		return
	}
	if min > max {
		min, max = max, min
	}
	return strconv.FormatInt(min+rand.Int63n(max-min+1), 10)
}

var (
	charSet = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
)

func RandomText(params map[string]string) (s string) {
	var err error
	defer func() {
		if r := recover(); r != nil {
			s = fmt.Sprintf("%v", r)
		} else if err != nil {
			s = err.Error()
		}
	}()

	length, err := strconv.ParseInt(params["length"], 10, 64)
	if err != nil {
		return
	}
	if length == 0 {
		return ""
	}
	set := []rune(params["charset"])
	if len(set) == 0 {
		set = charSet
	}
	n := len(set)
	rs := make([]rune, length)
	for i := int64(0); i < length; i++ {
		rs[i] = set[rand.Intn(n)]
	}
	return string(rs)
}

自定义函数JSON样例:

{
  "name": "Random",
  "plugin": "random.so",
  "desc": "Random plugin supports generating random value.",
  "funcs": [
    {
      "name": "RandomNum",
      "type": "func",
      "desc": "RandomNum generates random number.",
      "params": [
        {
          "name": "min",
          "default": "0",
          "range": [],
          "desc": "The maximum of random number.",
          "required": true
        },
        {
          "name": "max",
          "default": "100",
          "range": [],
          "desc": "The maximum of random number.",
          "required": true
        }
      ]
    },
    {
      "name": "RandomText",
      "type": "func",
      "desc": "RandomText generates random text of a specified length.",
      "params": [
        {
          "name": "length",
          "default": "32",
          "range": [],
          "desc": "The length of random text.",
          "required": true
        },
        {
          "name": "charset",
          "default": "",
          "range": [],
          "desc": "Random text is generated randomly from the specified character set. if empty, default char set is 0-9 a-z A-Z",
          "required": false
        }
      ]
    }
  ]
}