change: move plugin to independent repo

This commit is contained in:
debugtalk
2022-03-10 18:38:41 +08:00
parent 072d5d8fb0
commit 116dbdb4d9
30 changed files with 31 additions and 2269 deletions

View File

@@ -9,7 +9,7 @@ import (
"github.com/httprunner/hrp/internal/boomer"
"github.com/httprunner/hrp/internal/ga"
pluginInternal "github.com/httprunner/hrp/plugin/go"
pluginInternal "github.com/httprunner/plugin/go"
)
func NewBoomer(spawnCount int, spawnRate float64) *HRPBoomer {

View File

@@ -1,6 +1,6 @@
package main
import "github.com/httprunner/hrp/plugin"
import "github.com/httprunner/plugin"
// register functions and build to plugin binary
func main() {

5
go.mod
View File

@@ -6,7 +6,7 @@ require (
github.com/andybalholm/brotli v1.0.4
github.com/denisbrodbeck/machineid v1.0.1
github.com/google/uuid v1.3.0
github.com/httprunner/hrp/plugin v0.0.0
github.com/httprunner/plugin v0.1.1
github.com/jinzhu/copier v0.3.2
github.com/jmespath/go-jmespath v0.4.0
github.com/json-iterator/go v1.1.12
@@ -18,8 +18,7 @@ require (
github.com/rs/zerolog v1.26.1
github.com/spf13/cobra v1.2.1
github.com/stretchr/testify v1.7.0
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
)
replace github.com/httprunner/hrp/plugin => ./plugin
// replace github.com/httprunner/plugin => ./plugin

28
go.sum
View File

@@ -66,6 +66,10 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
@@ -81,6 +85,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -193,6 +198,8 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/httprunner/plugin v0.1.1 h1:tiTRAPVZ6tW6g0AvWTcLeuEmLLXgH7tMuzqXr6X2Nko=
github.com/httprunner/plugin v0.1.1/go.mod h1:DShhD3X+JrA6mqaIHHOeNUAMFjY5EFdx+9EWhXMYa14=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
@@ -347,6 +354,7 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
@@ -433,8 +441,9 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -510,10 +519,13 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 h1:TyHqChC80pFkXWraUUf6RuB5IqFdQieMLwwCJokV2pc=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -521,8 +533,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -653,8 +666,9 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20220308174144-ae0e22291548 h1:J5ZNG1QIdstOl8aaUoFoQJfp04FKTsFV+jwkBHEchqs=
google.golang.org/genproto v0.0.0-20220308174144-ae0e22291548/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -675,8 +689,10 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=

View File

@@ -72,7 +72,7 @@ var demoPlugin = `package main
import (
"fmt"
"github.com/httprunner/hrp/plugin"
"github.com/httprunner/plugin"
)
func SumTwoInt(a, b int) int {

View File

@@ -70,7 +70,7 @@ func CreateScaffold(projectName string) error {
}
// download plugin dependency
if err := builtin.ExecCommand(exec.Command("go", "get", "github.com/httprunner/hrp/plugin"), pluginDir); err != nil {
if err := builtin.ExecCommand(exec.Command("go", "get", "github.com/httprunner/plugin"), pluginDir); err != nil {
return err
}

View File

@@ -13,8 +13,8 @@ import (
"github.com/rs/zerolog/log"
"github.com/httprunner/hrp/internal/builtin"
pluginInternal "github.com/httprunner/hrp/plugin/go"
pluginUtils "github.com/httprunner/hrp/plugin/utils"
pluginInternal "github.com/httprunner/plugin/go"
pluginUtils "github.com/httprunner/plugin/utils"
)
func newParser() *parser {

View File

@@ -1,202 +0,0 @@
# plugin
When you need to do some dynamic calculations or custom logic processing in testcases, you need to use the plugin function mechanism.
HttpRunner+ supports both [hashicorp/plugin] and [go plugin] to create and call custom functions.
## hashicorp/plugin
It is recommended to use [hashicorp/plugin] in most cases.
### create plugin functions
Firstly, you need to define your plugin functions. The functions can be very flexible, only the following restrictions should be complied with.
- function should return at most one value and one error.
- `Register()` and `Serve()` must be called to register plugin functions and start a plugin server process in `main()`.
Here is some plugin functions as example.
```go
package main
import (
"fmt"
"github.com/httprunner/hrp/plugin"
)
func SumTwoInt(a, b int) int {
return a + b
}
func SumInts(args ...int) int {
var sum int
for _, arg := range args {
sum += arg
}
return sum
}
func Sum(args ...interface{}) (interface{}, error) {
var sum float64
for _, arg := range args {
switch v := arg.(type) {
case int:
sum += float64(v)
case float64:
sum += v
default:
return nil, fmt.Errorf("unexpected type: %T", arg)
}
}
return sum, nil
}
func main() {
plugin.Register("sum_ints", SumInts)
plugin.Register("sum_two_int", SumTwoInt)
plugin.Register("sum", Sum)
plugin.Serve()
}
```
You can get more examples at [examples/plugin/]
### build plugin
Secondly, you can build your hashicorp plugin to binary file `debugtalk.bin`. The name of `debugtalk.bin` is by convention and should not be changed.
```bash
$ go build -o examples/debugtalk.bin examples/plugin/hashicorp.go examples/plugin/debugtalk.go
```
It is recommended to place the `debugtalk.bin` file in your project root folder, or you can put it in the parent folder of the target testcase file. HttpRunner+ will search `debugtalk.bin` upward recursively until current working directory or system root dir.
### use plugin functions
Then, you can call your defined plugin function in your `YAML/JSON` testcase at any position.
```json
{
"name": "get with params",
"variables": {
"a": "${sum_two_int(1,6)}",
"b": "${sum_ints(1,2,3)}",
"c": "${sum(1, 2.3, 4)}",
},
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "$c",
"foo2": "${max($a, $b)}"
},
"headers": {
"User-Agent": "HttpRunnerPlus"
}
}
}
```
### rpc vs. gRPC
HttpRunner+ has both supported `net/rpc` and `gRPC` in [hashicorp/plugin]. It is recommended to use `gRPC` and this is the default choice.
If you want to run plugin in `net/rpc` mode, you can set an environment variable `HRP_PLUGIN_TYPE=rpc`.
```bash
$ export HRP_PLUGIN_TYPE=rpc
$ hrp run examples/demo.json
$ hrp boom examples/demo.json
```
## go plugin
The golang official plugin is only supported on Linux, FreeBSD, and macOS. And this solution also has many drawbacks.
### create plugin functions
Firstly, you need to define your plugin functions. The functions can be very flexible, only the following restrictions should be complied with.
- plugin package name must be `main`.
- function names must be capitalized.
- function should return at most one value and one error.
Here is some plugin functions as example.
```go
package main
func SumTwoInt(a, b int) int {
return a + b
}
func SumInts(args ...int) int {
var sum int
for _, arg := range args {
sum += arg
}
return sum
}
func Sum(args ...interface{}) (interface{}, error) {
var sum float64
for _, arg := range args {
switch v := arg.(type) {
case int:
sum += float64(v)
case float64:
sum += v
default:
return nil, fmt.Errorf("unexpected type: %T", arg)
}
}
return sum, nil
}
```
You can get more examples at [examples/plugin/debugtalk.go]
### build plugin
Then you can build your go plugin with `-buildmode=plugin` flag to binary file `debugtalk.so`. The name of `debugtalk.so` is by convention and should not be changed.
```bash
$ go build -buildmode=plugin -o=examples/debugtalk.so examples/plugin/debugtalk.go
```
It is recommended to place the `debugtalk.so` file in your project root folder, or you can put it in the parent folder of the target testcase file. HttpRunner+ will search `debugtalk.so` upward recursively until current working directory or system root dir.
### use plugin functions
Then, you can call your defined plugin function in your `YAML/JSON` testcase at any position.
```json
{
"name": "get with params",
"variables": {
"a": "${SumTwoInt(1,6)}",
"b": "${SumInts(1,2,3)}",
"c": "${Sum(1, 2.3, 4)}",
},
"request": {
"method": "GET",
"url": "/get",
"params": {
"foo1": "$c",
"foo2": "${max($a, $b)}"
},
"headers": {
"User-Agent": "HttpRunnerPlus"
}
}
}
```
Notice: you should use the original function name.
[hashicorp/plugin]: https://github.com/hashicorp/go-plugin
[go plugin]: https://pkg.go.dev/plugin
[examples/plugin/]: ../examples/plugin/
[examples/plugin/debugtalk.go]: ../examples/plugin/debugtalk.go

View File

@@ -1,14 +0,0 @@
module github.com/httprunner/hrp/plugin
go 1.16
require (
github.com/hashicorp/go-hclog v1.1.0
github.com/hashicorp/go-plugin v1.4.3
github.com/json-iterator/go v1.1.12
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.26.1
github.com/stretchr/testify v1.7.0
google.golang.org/grpc v1.27.1
google.golang.org/protobuf v1.27.1
)

View File

@@ -1,133 +0,0 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.1.0 h1:QsGcniKx5/LuX2eYoeL+Np3UKYPNaN7YKpTh29h8rbw=
github.com/hashicorp/go-hclog v1.1.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM=
github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg=
github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -1,37 +0,0 @@
package pluginInternal
import (
"os"
"strings"
"github.com/hashicorp/go-plugin"
)
const PluginName = "debugtalk"
const RPCPluginName = PluginName + "_rpc"
const GRPCPluginName = PluginName + "_grpc"
// handshakeConfigs are used to just do a basic handshake between
// a plugin and host. If the handshake fails, a user friendly error is shown.
// This prevents users from executing bad plugins or executing a plugin
// directory. It is a UX feature, not a security feature.
var HandshakeConfig = plugin.HandshakeConfig{
ProtocolVersion: 1,
MagicCookieKey: "HttpRunnerPlus",
MagicCookieValue: PluginName,
}
const hrpPluginTypeEnvName = "HRP_PLUGIN_TYPE"
var hrpPluginType string
func init() {
hrpPluginType = strings.ToLower(os.Getenv(hrpPluginTypeEnvName))
if hrpPluginType == "" {
hrpPluginType = "grpc" // default
}
}
func IsRPCPluginType() bool {
return hrpPluginType == "rpc"
}

View File

@@ -1,72 +0,0 @@
package pluginInternal
import (
"fmt"
"plugin"
"reflect"
"runtime"
"github.com/rs/zerolog/log"
pluginUtils "github.com/httprunner/hrp/plugin/utils"
)
// GoPlugin implements golang official plugin
type GoPlugin struct {
*plugin.Plugin
cachedFunctions map[string]reflect.Value // cache loaded functions to improve performance
}
func (p *GoPlugin) Init(path string) error {
if runtime.GOOS == "windows" {
log.Warn().Msg("go plugin does not support windows")
return fmt.Errorf("go plugin does not support windows")
}
var err error
p.Plugin, err = plugin.Open(path)
if err != nil {
log.Error().Err(err).Str("path", path).Msg("load go plugin failed")
return err
}
p.cachedFunctions = make(map[string]reflect.Value)
log.Info().Str("path", path).Msg("load go plugin success")
return nil
}
func (p *GoPlugin) Has(funcName string) bool {
fn, ok := p.cachedFunctions[funcName]
if ok {
return fn.IsValid()
}
sym, err := p.Plugin.Lookup(funcName)
if err != nil {
p.cachedFunctions[funcName] = reflect.Value{} // mark as invalid
return false
}
fn = reflect.ValueOf(sym)
// check function type
if fn.Kind() != reflect.Func {
p.cachedFunctions[funcName] = reflect.Value{} // mark as invalid
return false
}
p.cachedFunctions[funcName] = fn
return true
}
func (p *GoPlugin) Call(funcName string, args ...interface{}) (interface{}, error) {
if !p.Has(funcName) {
return nil, fmt.Errorf("function %s not found", funcName)
}
fn := p.cachedFunctions[funcName]
return pluginUtils.CallFunc(fn, args...)
}
func (p *GoPlugin) Quit() error {
// no need to quit for go plugin
return nil
}

View File

@@ -1,90 +0,0 @@
// +build linux freebsd darwin
// go plugin doesn't support windows
package pluginInternal
import (
"fmt"
"os"
"os/exec"
"testing"
"github.com/stretchr/testify/assert"
)
func buildGoPlugin() {
fmt.Println("[setup] build go plugin")
// flag -race is necessary in order to be consistent with go test
cmd := exec.Command("go", "build", "-buildmode=plugin", "-race",
"-o=debugtalk.so", "../../examples/plugin/debugtalk.go")
if err := cmd.Run(); err != nil {
panic(err)
}
}
func removeGoPlugin() {
fmt.Println("[teardown] remove go plugin")
os.Remove("debugtalk.so")
}
func TestLocatePlugin(t *testing.T) {
buildGoPlugin()
defer removeGoPlugin()
_, err := locateFile("../", goPluginFile)
if !assert.Error(t, err) {
t.Fail()
}
_, err = locateFile("", goPluginFile)
if !assert.Error(t, err) {
t.Fail()
}
startPath := "debugtalk.so"
_, err = locateFile(startPath, goPluginFile)
if !assert.Nil(t, err) {
t.Fail()
}
startPath = "call.go"
_, err = locateFile(startPath, goPluginFile)
if !assert.Nil(t, err) {
t.Fail()
}
startPath = "."
_, err = locateFile(startPath, goPluginFile)
if !assert.Nil(t, err) {
t.Fail()
}
startPath = "/abc"
_, err = locateFile(startPath, goPluginFile)
if !assert.Error(t, err) {
t.Fail()
}
}
func TestCallPluginFunction(t *testing.T) {
buildGoPlugin()
defer removeGoPlugin()
plugin, err := Init("debugtalk.so", false)
if err != nil {
t.Fatal(err)
}
if !assert.True(t, plugin.Has("Concatenate")) {
t.Fail()
}
// call function without arguments
result, err := plugin.Call("Concatenate", "1", 2, "3.14")
if !assert.NoError(t, err) {
t.Fail()
}
if !assert.Equal(t, "123.14", result) {
t.Fail()
}
}

View File

@@ -1,111 +0,0 @@
package pluginInternal
import (
"context"
"github.com/hashicorp/go-plugin"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
"google.golang.org/grpc"
"github.com/httprunner/hrp/plugin/go/proto"
jsoniter "github.com/json-iterator/go"
)
// replace with third-party json library to improve performance
var json = jsoniter.ConfigCompatibleWithStandardLibrary
// functionGRPCClient runs on the host side, it implements FuncCaller interface
type functionGRPCClient struct {
client proto.DebugTalkClient
}
func (m *functionGRPCClient) GetNames() ([]string, error) {
log.Info().Msg("function GetNames called on host side")
resp, err := m.client.GetNames(context.Background(), &proto.Empty{})
if err != nil {
log.Error().Err(err).Msg("gRPC call GetNames() failed")
return nil, err
}
return resp.Names, err
}
func (m *functionGRPCClient) Call(funcName string, funcArgs ...interface{}) (interface{}, error) {
log.Info().Str("funcName", funcName).Interface("funcArgs", funcArgs).Msg("call function via gRPC")
funcArgBytes, err := json.Marshal(funcArgs)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal Call() funcArgs")
}
req := &proto.CallRequest{
Name: funcName,
Args: funcArgBytes,
}
response, err := m.client.Call(context.Background(), req)
if err != nil {
log.Error().Err(err).
Str("funcName", funcName).Interface("funcArgs", funcArgs).
Msg("gRPC Call() failed")
return nil, err
}
var resp interface{}
err = json.Unmarshal(response.Value, &resp)
if err != nil {
return nil, errors.Wrap(err, "failed to unmarshal Call() response")
}
return resp, nil
}
// Here is the gRPC server that functionGRPCClient talks to.
type functionGRPCServer struct {
proto.UnimplementedDebugTalkServer
Impl FuncCaller
}
func (m *functionGRPCServer) GetNames(ctx context.Context, req *proto.Empty) (*proto.GetNamesResponse, error) {
log.Info().Interface("req", req).Msg("gRPC GetNames() called on plugin side")
v, err := m.Impl.GetNames()
if err != nil {
log.Error().Err(err).Msg("gRPC GetNames() execution failed")
return nil, err
}
return &proto.GetNamesResponse{Names: v}, err
}
func (m *functionGRPCServer) Call(ctx context.Context, req *proto.CallRequest) (*proto.CallResponse, error) {
var funcArgs []interface{}
if err := json.Unmarshal(req.Args, &funcArgs); err != nil {
return nil, errors.Wrap(err, "failed to unmarshal Call() funcArgs")
}
log.Info().Interface("req", req).Msg("gRPC Call() called on plugin side")
v, err := m.Impl.Call(req.Name, funcArgs...)
if err != nil {
log.Error().Err(err).Interface("req", req).Msg("gRPC Call() execution failed")
return nil, err
}
value, err := json.Marshal(v)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal Call() response")
}
return &proto.CallResponse{Value: value}, err
}
// HRPPlugin implements hashicorp's plugin.GRPCPlugin.
type GRPCPlugin struct {
plugin.Plugin
Impl FuncCaller
}
func (p *GRPCPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
proto.RegisterDebugTalkServer(s, &functionGRPCServer{Impl: p.Impl})
return nil
}
func (p *GRPCPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
return &functionGRPCClient{client: proto.NewDebugTalkClient(c)}, nil
}

View File

@@ -1,113 +0,0 @@
package pluginInternal
import (
"fmt"
"os"
"os/exec"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
var client *plugin.Client
// HashicorpPlugin implements hashicorp/go-plugin
type HashicorpPlugin struct {
logOn bool // turn on plugin log
FuncCaller
cachedFunctions map[string]bool // cache loaded functions to improve performance
}
func (p *HashicorpPlugin) Init(path string) error {
var pluginName string
if IsRPCPluginType() {
pluginName = RPCPluginName
} else {
pluginName = GRPCPluginName
}
// logger
loggerOptions := &hclog.LoggerOptions{
Name: pluginName,
Output: os.Stdout,
}
if p.logOn {
loggerOptions.Level = hclog.Debug
} else {
loggerOptions.Level = hclog.Info
}
// cmd
cmd := exec.Command(path)
cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%s", hrpPluginTypeEnvName, hrpPluginType))
// launch the plugin process
client = plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: HandshakeConfig,
Plugins: map[string]plugin.Plugin{
RPCPluginName: &RPCPlugin{},
GRPCPluginName: &GRPCPlugin{},
},
Cmd: cmd,
Logger: hclog.New(loggerOptions),
AllowedProtocols: []plugin.Protocol{
plugin.ProtocolNetRPC,
plugin.ProtocolGRPC,
},
})
// Connect via RPC/gRPC
rpcClient, err := client.Client()
if err != nil {
return errors.Wrap(err, fmt.Sprintf("connect %s plugin failed", hrpPluginType))
}
// Request the plugin
raw, err := rpcClient.Dispense(pluginName)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("request %s plugin failed", hrpPluginType))
}
// We should have a Function now! This feels like a normal interface
// implementation but is in fact over an RPC connection.
p.FuncCaller = raw.(FuncCaller)
p.cachedFunctions = make(map[string]bool)
log.Info().Str("path", path).Msg("load hashicorp go plugin success")
return nil
}
func (p *HashicorpPlugin) Has(funcName string) bool {
flag, ok := p.cachedFunctions[funcName]
if ok {
return flag
}
funcNames, err := p.GetNames()
if err != nil {
return false
}
for _, name := range funcNames {
if name == funcName {
p.cachedFunctions[funcName] = true // cache as exists
return true
}
}
p.cachedFunctions[funcName] = false // cache as not exists
return false
}
func (p *HashicorpPlugin) Call(funcName string, args ...interface{}) (interface{}, error) {
return p.FuncCaller.Call(funcName, args...)
}
func (p *HashicorpPlugin) Quit() error {
// kill hashicorp plugin process
log.Info().Msg("quit hashicorp plugin process")
client.Kill()
return nil
}

View File

@@ -1,90 +0,0 @@
package pluginInternal
import (
"os"
"os/exec"
"testing"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/assert"
)
func buildHashicorpPlugin() {
log.Info().Msg("[init] build hashicorp go plugin")
cmd := exec.Command("go", "build",
"-o", "../../examples/debugtalk.bin",
"../../examples/plugin/hashicorp.go", "../../examples/plugin/debugtalk.go")
if err := cmd.Run(); err != nil {
panic(err)
}
}
func removeHashicorpPlugin() {
log.Info().Msg("[teardown] remove hashicorp plugin")
os.Remove("../../examples/debugtalk.bin")
}
func TestInitHashicorpPlugin(t *testing.T) {
buildHashicorpPlugin()
defer removeHashicorpPlugin()
plugin, err := Init("../../examples/debugtalk.bin", false)
if err != nil {
t.Fatal(err)
}
defer plugin.Quit()
if !assert.True(t, plugin.Has("sum_ints")) {
t.Fatal(err)
}
if !assert.True(t, plugin.Has("concatenate")) {
t.Fatal(err)
}
var v2 interface{}
v2, err = plugin.Call("sum_ints", 1, 2, 3, 4)
if err != nil {
t.Fatal(err)
}
if !assert.Equal(t, 10, v2) {
t.Fail()
}
v2, err = plugin.Call("sum_two_int", 1, 2)
if err != nil {
t.Fatal(err)
}
if !assert.Equal(t, 3, v2) {
t.Fail()
}
v2, err = plugin.Call("sum", 1, 2, 3.4, 5)
if err != nil {
t.Fatal(err)
}
if !assert.Equal(t, 11.4, v2) {
t.Fail()
}
var v3 interface{}
v3, err = plugin.Call("sum_two_string", "a", "b")
if err != nil {
t.Fatal(err)
}
if !assert.Equal(t, "ab", v3) {
t.Fail()
}
v3, err = plugin.Call("sum_strings", "a", "b", "c")
if err != nil {
t.Fatal(err)
}
if !assert.Equal(t, "abc", v3) {
t.Fail()
}
v3, err = plugin.Call("concatenate", "a", 2, "c", 3.4)
if err != nil {
t.Fatal(err)
}
if !assert.Equal(t, "a2c3.4", v3) {
t.Fail()
}
}

View File

@@ -1,83 +0,0 @@
package pluginInternal
import (
"fmt"
"os"
"path/filepath"
)
type pluginFile string
const (
goPluginFile pluginFile = PluginName + ".so" // built from go plugin
hashicorpGoPluginFile pluginFile = PluginName + ".bin" // built from hashicorp go plugin
hashicorpPyPluginFile pluginFile = PluginName + ".py"
)
func Init(path string, logOn bool) (IPlugin, error) {
if path == "" {
return nil, nil
}
var plugin IPlugin
// priority: hashicorp plugin > go plugin
// locate hashicorp plugin file
pluginPath, err := locateFile(path, hashicorpGoPluginFile)
if err == nil {
// found hashicorp go plugin file
plugin = &HashicorpPlugin{
logOn: logOn,
}
err = plugin.Init(pluginPath)
return plugin, err
}
// locate go plugin file
pluginPath, err = locateFile(path, goPluginFile)
if err == nil {
// found go plugin file
plugin = &GoPlugin{}
err = plugin.Init(pluginPath)
return plugin, err
}
// plugin not found
return nil, nil
}
// locateFile searches destFile upward recursively until current
// working directory or system root dir.
func locateFile(startPath string, destFile pluginFile) (string, error) {
stat, err := os.Stat(startPath)
if os.IsNotExist(err) {
return "", err
}
var startDir string
if stat.IsDir() {
startDir = startPath
} else {
startDir = filepath.Dir(startPath)
}
startDir, _ = filepath.Abs(startDir)
// convention over configuration
pluginPath := filepath.Join(startDir, string(destFile))
if _, err := os.Stat(pluginPath); err == nil {
return pluginPath, nil
}
// current working directory
cwd, _ := os.Getwd()
if startDir == cwd {
return "", fmt.Errorf("searched to CWD, plugin file not found")
}
// system root dir
parentDir, _ := filepath.Abs(filepath.Dir(startDir))
if parentDir == startDir {
return "", fmt.Errorf("searched to system root dir, plugin file not found")
}
return locateFile(parentDir, destFile)
}

View File

@@ -1,14 +0,0 @@
package pluginInternal
// FuncCaller is the interface that we're exposing as a plugin.
type FuncCaller interface {
GetNames() ([]string, error) // get all plugin function names list
Call(funcName string, args ...interface{}) (interface{}, error) // call plugin function
}
type IPlugin interface {
Init(path string) error // init plugin
Has(funcName string) bool // check if plugin has function
Call(funcName string, args ...interface{}) (interface{}, error) // call function
Quit() error // quit plugin
}

View File

@@ -1,340 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.19.4
// source: plugin/proto/debugtalk.proto
package proto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Empty struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *Empty) Reset() {
*x = Empty{}
if protoimpl.UnsafeEnabled {
mi := &file_plugin_proto_debugtalk_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Empty) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Empty) ProtoMessage() {}
func (x *Empty) ProtoReflect() protoreflect.Message {
mi := &file_plugin_proto_debugtalk_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
func (*Empty) Descriptor() ([]byte, []int) {
return file_plugin_proto_debugtalk_proto_rawDescGZIP(), []int{0}
}
type GetNamesResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Names []string `protobuf:"bytes,1,rep,name=names,proto3" json:"names,omitempty"`
}
func (x *GetNamesResponse) Reset() {
*x = GetNamesResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_plugin_proto_debugtalk_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetNamesResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetNamesResponse) ProtoMessage() {}
func (x *GetNamesResponse) ProtoReflect() protoreflect.Message {
mi := &file_plugin_proto_debugtalk_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetNamesResponse.ProtoReflect.Descriptor instead.
func (*GetNamesResponse) Descriptor() ([]byte, []int) {
return file_plugin_proto_debugtalk_proto_rawDescGZIP(), []int{1}
}
func (x *GetNamesResponse) GetNames() []string {
if x != nil {
return x.Names
}
return nil
}
type CallRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Args []byte `protobuf:"bytes,2,opt,name=args,proto3" json:"args,omitempty"` // []interface{}
}
func (x *CallRequest) Reset() {
*x = CallRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_plugin_proto_debugtalk_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CallRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CallRequest) ProtoMessage() {}
func (x *CallRequest) ProtoReflect() protoreflect.Message {
mi := &file_plugin_proto_debugtalk_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CallRequest.ProtoReflect.Descriptor instead.
func (*CallRequest) Descriptor() ([]byte, []int) {
return file_plugin_proto_debugtalk_proto_rawDescGZIP(), []int{2}
}
func (x *CallRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *CallRequest) GetArgs() []byte {
if x != nil {
return x.Args
}
return nil
}
type CallResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` // interface{}
}
func (x *CallResponse) Reset() {
*x = CallResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_plugin_proto_debugtalk_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CallResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CallResponse) ProtoMessage() {}
func (x *CallResponse) ProtoReflect() protoreflect.Message {
mi := &file_plugin_proto_debugtalk_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CallResponse.ProtoReflect.Descriptor instead.
func (*CallResponse) Descriptor() ([]byte, []int) {
return file_plugin_proto_debugtalk_proto_rawDescGZIP(), []int{3}
}
func (x *CallResponse) GetValue() []byte {
if x != nil {
return x.Value
}
return nil
}
var File_plugin_proto_debugtalk_proto protoreflect.FileDescriptor
var file_plugin_proto_debugtalk_proto_rawDesc = []byte{
0x0a, 0x1c, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x64,
0x65, 0x62, 0x75, 0x67, 0x74, 0x61, 0x6c, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x28,
0x0a, 0x10, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x09, 0x52, 0x05, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0x35, 0x0a, 0x0b, 0x43, 0x61, 0x6c, 0x6c,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x61,
0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x61, 0x72, 0x67, 0x73, 0x22,
0x24, 0x0a, 0x0c, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x6f, 0x0a, 0x09, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x61,
0x6c, 0x6b, 0x12, 0x31, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x0c,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x17, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x12, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x11, 0x5a, 0x0f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_plugin_proto_debugtalk_proto_rawDescOnce sync.Once
file_plugin_proto_debugtalk_proto_rawDescData = file_plugin_proto_debugtalk_proto_rawDesc
)
func file_plugin_proto_debugtalk_proto_rawDescGZIP() []byte {
file_plugin_proto_debugtalk_proto_rawDescOnce.Do(func() {
file_plugin_proto_debugtalk_proto_rawDescData = protoimpl.X.CompressGZIP(file_plugin_proto_debugtalk_proto_rawDescData)
})
return file_plugin_proto_debugtalk_proto_rawDescData
}
var file_plugin_proto_debugtalk_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_plugin_proto_debugtalk_proto_goTypes = []interface{}{
(*Empty)(nil), // 0: proto.Empty
(*GetNamesResponse)(nil), // 1: proto.GetNamesResponse
(*CallRequest)(nil), // 2: proto.CallRequest
(*CallResponse)(nil), // 3: proto.CallResponse
}
var file_plugin_proto_debugtalk_proto_depIdxs = []int32{
0, // 0: proto.DebugTalk.GetNames:input_type -> proto.Empty
2, // 1: proto.DebugTalk.Call:input_type -> proto.CallRequest
1, // 2: proto.DebugTalk.GetNames:output_type -> proto.GetNamesResponse
3, // 3: proto.DebugTalk.Call:output_type -> proto.CallResponse
2, // [2:4] is the sub-list for method output_type
0, // [0:2] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_plugin_proto_debugtalk_proto_init() }
func file_plugin_proto_debugtalk_proto_init() {
if File_plugin_proto_debugtalk_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_plugin_proto_debugtalk_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Empty); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_plugin_proto_debugtalk_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetNamesResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_plugin_proto_debugtalk_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CallRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_plugin_proto_debugtalk_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CallResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_plugin_proto_debugtalk_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_plugin_proto_debugtalk_proto_goTypes,
DependencyIndexes: file_plugin_proto_debugtalk_proto_depIdxs,
MessageInfos: file_plugin_proto_debugtalk_proto_msgTypes,
}.Build()
File_plugin_proto_debugtalk_proto = out.File
file_plugin_proto_debugtalk_proto_rawDesc = nil
file_plugin_proto_debugtalk_proto_goTypes = nil
file_plugin_proto_debugtalk_proto_depIdxs = nil
}

View File

@@ -1,141 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.19.4
// source: plugin/proto/debugtalk.proto
package proto
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// DebugTalkClient is the client API for DebugTalk service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type DebugTalkClient interface {
GetNames(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetNamesResponse, error)
Call(ctx context.Context, in *CallRequest, opts ...grpc.CallOption) (*CallResponse, error)
}
type debugTalkClient struct {
cc grpc.ClientConnInterface
}
func NewDebugTalkClient(cc grpc.ClientConnInterface) DebugTalkClient {
return &debugTalkClient{cc}
}
func (c *debugTalkClient) GetNames(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*GetNamesResponse, error) {
out := new(GetNamesResponse)
err := c.cc.Invoke(ctx, "/proto.DebugTalk/GetNames", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *debugTalkClient) Call(ctx context.Context, in *CallRequest, opts ...grpc.CallOption) (*CallResponse, error) {
out := new(CallResponse)
err := c.cc.Invoke(ctx, "/proto.DebugTalk/Call", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// DebugTalkServer is the server API for DebugTalk service.
// All implementations must embed UnimplementedDebugTalkServer
// for forward compatibility
type DebugTalkServer interface {
GetNames(context.Context, *Empty) (*GetNamesResponse, error)
Call(context.Context, *CallRequest) (*CallResponse, error)
mustEmbedUnimplementedDebugTalkServer()
}
// UnimplementedDebugTalkServer must be embedded to have forward compatible implementations.
type UnimplementedDebugTalkServer struct {
}
func (UnimplementedDebugTalkServer) GetNames(context.Context, *Empty) (*GetNamesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetNames not implemented")
}
func (UnimplementedDebugTalkServer) Call(context.Context, *CallRequest) (*CallResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Call not implemented")
}
func (UnimplementedDebugTalkServer) mustEmbedUnimplementedDebugTalkServer() {}
// UnsafeDebugTalkServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to DebugTalkServer will
// result in compilation errors.
type UnsafeDebugTalkServer interface {
mustEmbedUnimplementedDebugTalkServer()
}
func RegisterDebugTalkServer(s grpc.ServiceRegistrar, srv DebugTalkServer) {
s.RegisterService(&DebugTalk_ServiceDesc, srv)
}
func _DebugTalk_GetNames_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DebugTalkServer).GetNames(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.DebugTalk/GetNames",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DebugTalkServer).GetNames(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
func _DebugTalk_Call_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CallRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(DebugTalkServer).Call(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/proto.DebugTalk/Call",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DebugTalkServer).Call(ctx, req.(*CallRequest))
}
return interceptor(ctx, in, info, handler)
}
// DebugTalk_ServiceDesc is the grpc.ServiceDesc for DebugTalk service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var DebugTalk_ServiceDesc = grpc.ServiceDesc{
ServiceName: "proto.DebugTalk",
HandlerType: (*DebugTalkServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetNames",
Handler: _DebugTalk_GetNames_Handler,
},
{
MethodName: "Call",
Handler: _DebugTalk_Call_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "plugin/proto/debugtalk.proto",
}

View File

@@ -1,97 +0,0 @@
package pluginInternal
import (
"encoding/gob"
"net/rpc"
"github.com/hashicorp/go-plugin"
"github.com/rs/zerolog/log"
)
func init() {
gob.Register(new(funcData))
}
// funcData is used to transfer between plugin and host via RPC.
type funcData struct {
Name string // function name
Args []interface{} // function arguments
}
// functionRPCClient runs on the host side, it implements FuncCaller interface
type functionRPCClient struct {
client *rpc.Client
}
func (g *functionRPCClient) GetNames() ([]string, error) {
var resp []string
err := g.client.Call("Plugin.GetNames", new(interface{}), &resp)
if err != nil {
log.Error().Err(err).Msg("rpc call GetNames() failed")
return nil, err
}
return resp, nil
}
// host -> plugin
func (g *functionRPCClient) Call(funcName string, funcArgs ...interface{}) (interface{}, error) {
log.Info().Str("funcName", funcName).Interface("funcArgs", funcArgs).Msg("call function via RPC")
f := funcData{
Name: funcName,
Args: funcArgs,
}
var args interface{} = f
var resp interface{}
err := g.client.Call("Plugin.Call", &args, &resp)
if err != nil {
log.Error().Err(err).
Str("funcName", funcName).Interface("funcArgs", funcArgs).
Msg("rpc Call() failed")
return nil, err
}
return resp, nil
}
// functionRPCServer runs on the plugin side, executing the user custom function.
type functionRPCServer struct {
Impl FuncCaller
}
// plugin execution
func (s *functionRPCServer) GetNames(args interface{}, resp *[]string) error {
log.Info().Interface("args", args).Msg("rpc GetNames() called on plugin side")
var err error
*resp, err = s.Impl.GetNames()
if err != nil {
log.Error().Err(err).Msg("rpc GetNames() execution failed")
return err
}
return nil
}
// plugin execution
func (s *functionRPCServer) Call(args interface{}, resp *interface{}) error {
log.Info().Interface("args", args).Msg("rpc Call() called on plugin side")
f := args.(*funcData)
var err error
*resp, err = s.Impl.Call(f.Name, f.Args...)
if err != nil {
log.Error().Err(err).Interface("args", args).Msg("rpc Call() execution failed")
return err
}
return nil
}
// RPCPlugin implements hashicorp's plugin.Plugin.
type RPCPlugin struct {
Impl FuncCaller
}
func (p *RPCPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
return &functionRPCServer{Impl: p.Impl}, nil
}
func (RPCPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
return &functionRPCClient{client: c}, nil
}

View File

@@ -1,106 +0,0 @@
package plugin
import (
"fmt"
"os"
"reflect"
hclog "github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/rs/zerolog/log"
pluginInternal "github.com/httprunner/hrp/plugin/go"
pluginUtils "github.com/httprunner/hrp/plugin/utils"
)
// functionsMap stores plugin functions
type functionsMap map[string]reflect.Value
// functionPlugin implements the FuncCaller interface
type functionPlugin struct {
logger hclog.Logger
functions functionsMap
}
func (p *functionPlugin) GetNames() ([]string, error) {
var names []string
for name := range p.functions {
names = append(names, name)
}
return names, nil
}
func (p *functionPlugin) Call(funcName string, args ...interface{}) (interface{}, error) {
p.logger.Info("call function", "funcName", funcName, "args", args)
fn, ok := p.functions[funcName]
if !ok {
return nil, fmt.Errorf("function %s not found", funcName)
}
return pluginUtils.CallFunc(fn, args...)
}
var functions = make(functionsMap)
// Register registers a plugin function.
// Every plugin function must be registered before Serve() is called.
func Register(funcName string, fn interface{}) {
if _, ok := functions[funcName]; ok {
return
}
functions[funcName] = reflect.ValueOf(fn)
}
// serveRPC starts a plugin server process in RPC mode.
func serveRPC() {
log.Info().Msg("start plugin server in RPC mode")
funcPlugin := &functionPlugin{
logger: hclog.New(&hclog.LoggerOptions{
Name: pluginInternal.RPCPluginName,
Output: os.Stdout,
Level: hclog.Info,
}),
functions: functions,
}
var pluginMap = map[string]plugin.Plugin{
pluginInternal.RPCPluginName: &pluginInternal.RPCPlugin{Impl: funcPlugin},
}
// start RPC server
plugin.Serve(&plugin.ServeConfig{
HandshakeConfig: pluginInternal.HandshakeConfig,
Plugins: pluginMap,
})
}
// serveGRPC starts a plugin server process in gRPC mode.
func serveGRPC() {
log.Info().Msg("start plugin server in gRPC mode")
funcPlugin := &functionPlugin{
logger: hclog.New(&hclog.LoggerOptions{
Name: pluginInternal.GRPCPluginName,
Output: os.Stdout,
Level: hclog.Info,
}),
functions: functions,
}
var pluginMap = map[string]plugin.Plugin{
pluginInternal.GRPCPluginName: &pluginInternal.GRPCPlugin{Impl: funcPlugin},
}
// start gRPC server
plugin.Serve(&plugin.ServeConfig{
HandshakeConfig: pluginInternal.HandshakeConfig,
Plugins: pluginMap,
GRPCServer: plugin.DefaultGRPCServer,
})
}
// default to run plugin in gRPC mode
func Serve() {
if pluginInternal.IsRPCPluginType() {
serveRPC()
} else {
// default
serveGRPC()
}
}

View File

@@ -1,62 +0,0 @@
# Updating the Protocol
If you update the protocol buffers file, you can regenerate the file using the following command from the project root directory. You do not need to run this if you're just using the plugin.
## For Go
### Install dependencies
ref: https://www.grpc.io/docs/languages/go/quickstart/
Install the protocol compiler plugins for Go using the following commands:
```bash
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
```
Update your PATH so that the protoc compiler can find the plugins:
```bash
$ export PATH="$PATH:$(go env GOPATH)/bin"
```
### Generate gRPC code
```bash
$ protoc --go_out=. --go-grpc_out=. plugin/proto/debugtalk.proto
```
This will generate two go files in `plugin/go/proto` folder:
- debugtalk.pb.go
- debugtalk_grpc.pb.go
## For Python
### Install dependencies
ref: https://www.grpc.io/docs/languages/python/quickstart/
Install gRPC:
```bash
$ pip3 install grpcio
```
Install gRPC tools:
```bash
$ pip3 install grpcio-tools
```
### Generate gRPC code
```bash
$ python3 -m grpc_tools.protoc -I plugin/proto --python_out=plugin/python/ --grpc_python_out=plugin/python/ plugin/proto/debugtalk.proto
```
This will generate two python files in `plugin/python` folder:
- debugtalk_pb2.py
- debugtalk_pb2_grpc.py

View File

@@ -1,24 +0,0 @@
syntax = "proto3";
package proto;
option go_package = "plugin/go/proto";
message Empty {}
message GetNamesResponse {
repeated string names = 1;
}
message CallRequest {
string name = 1;
bytes args = 2; // []interface{}
}
message CallResponse {
bytes value = 1; // interface{}
}
service DebugTalk {
rpc GetNames(Empty) returns (GetNamesResponse);
rpc Call(CallRequest) returns (CallResponse);
}

View File

@@ -1,68 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: debugtalk.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0f\x64\x65\x62ugtalk.proto\x12\x05proto\"\x07\n\x05\x45mpty\"!\n\x10GetNamesResponse\x12\r\n\x05names\x18\x01 \x03(\t\")\n\x0b\x43\x61llRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04\x61rgs\x18\x02 \x01(\x0c\"\x1d\n\x0c\x43\x61llResponse\x12\r\n\x05value\x18\x01 \x01(\x0c\x32o\n\tDebugTalk\x12\x31\n\x08GetNames\x12\x0c.proto.Empty\x1a\x17.proto.GetNamesResponse\x12/\n\x04\x43\x61ll\x12\x12.proto.CallRequest\x1a\x13.proto.CallResponseB\x11Z\x0fplugin/go/protob\x06proto3')
_EMPTY = DESCRIPTOR.message_types_by_name['Empty']
_GETNAMESRESPONSE = DESCRIPTOR.message_types_by_name['GetNamesResponse']
_CALLREQUEST = DESCRIPTOR.message_types_by_name['CallRequest']
_CALLRESPONSE = DESCRIPTOR.message_types_by_name['CallResponse']
Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), {
'DESCRIPTOR' : _EMPTY,
'__module__' : 'debugtalk_pb2'
# @@protoc_insertion_point(class_scope:proto.Empty)
})
_sym_db.RegisterMessage(Empty)
GetNamesResponse = _reflection.GeneratedProtocolMessageType('GetNamesResponse', (_message.Message,), {
'DESCRIPTOR' : _GETNAMESRESPONSE,
'__module__' : 'debugtalk_pb2'
# @@protoc_insertion_point(class_scope:proto.GetNamesResponse)
})
_sym_db.RegisterMessage(GetNamesResponse)
CallRequest = _reflection.GeneratedProtocolMessageType('CallRequest', (_message.Message,), {
'DESCRIPTOR' : _CALLREQUEST,
'__module__' : 'debugtalk_pb2'
# @@protoc_insertion_point(class_scope:proto.CallRequest)
})
_sym_db.RegisterMessage(CallRequest)
CallResponse = _reflection.GeneratedProtocolMessageType('CallResponse', (_message.Message,), {
'DESCRIPTOR' : _CALLRESPONSE,
'__module__' : 'debugtalk_pb2'
# @@protoc_insertion_point(class_scope:proto.CallResponse)
})
_sym_db.RegisterMessage(CallResponse)
_DEBUGTALK = DESCRIPTOR.services_by_name['DebugTalk']
if _descriptor._USE_C_DESCRIPTORS == False:
DESCRIPTOR._options = None
DESCRIPTOR._serialized_options = b'Z\017plugin/go/proto'
_EMPTY._serialized_start=26
_EMPTY._serialized_end=33
_GETNAMESRESPONSE._serialized_start=35
_GETNAMESRESPONSE._serialized_end=68
_CALLREQUEST._serialized_start=70
_CALLREQUEST._serialized_end=111
_CALLRESPONSE._serialized_start=113
_CALLRESPONSE._serialized_end=142
_DEBUGTALK._serialized_start=144
_DEBUGTALK._serialized_end=255
# @@protoc_insertion_point(module_scope)

View File

@@ -1,99 +0,0 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import debugtalk_pb2 as debugtalk__pb2
class DebugTalkStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.GetNames = channel.unary_unary(
'/proto.DebugTalk/GetNames',
request_serializer=debugtalk__pb2.Empty.SerializeToString,
response_deserializer=debugtalk__pb2.GetNamesResponse.FromString,
)
self.Call = channel.unary_unary(
'/proto.DebugTalk/Call',
request_serializer=debugtalk__pb2.CallRequest.SerializeToString,
response_deserializer=debugtalk__pb2.CallResponse.FromString,
)
class DebugTalkServicer(object):
"""Missing associated documentation comment in .proto file."""
def GetNames(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def Call(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_DebugTalkServicer_to_server(servicer, server):
rpc_method_handlers = {
'GetNames': grpc.unary_unary_rpc_method_handler(
servicer.GetNames,
request_deserializer=debugtalk__pb2.Empty.FromString,
response_serializer=debugtalk__pb2.GetNamesResponse.SerializeToString,
),
'Call': grpc.unary_unary_rpc_method_handler(
servicer.Call,
request_deserializer=debugtalk__pb2.CallRequest.FromString,
response_serializer=debugtalk__pb2.CallResponse.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'proto.DebugTalk', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class DebugTalk(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def GetNames(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/proto.DebugTalk/GetNames',
debugtalk__pb2.Empty.SerializeToString,
debugtalk__pb2.GetNamesResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
@staticmethod
def Call(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/proto.DebugTalk/Call',
debugtalk__pb2.CallRequest.SerializeToString,
debugtalk__pb2.CallResponse.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)

View File

@@ -1,46 +0,0 @@
from concurrent import futures
import sys
import time
import grpc
import debugtalk_pb2
import debugtalk_pb2_grpc
from grpc_health.v1.health import HealthServicer
from grpc_health.v1 import health_pb2, health_pb2_grpc
class DebugTalkServicer(debugtalk_pb2_grpc.DebugTalkServicer):
"""Implementation of DebugTalk service."""
def GetNames(self, request, context):
result = debugtalk_pb2.GetNamesResponse()
return result
def Call(self, request, context):
return
def serve():
# We need to build a health service to work with go-plugin
health = HealthServicer()
health.set("plugin", health_pb2.HealthCheckResponse.ServingStatus.Value('SERVING'))
# Start the server.
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
debugtalk_pb2_grpc.add_DebugTalkServicer_to_server(DebugTalkServicer(), server)
health_pb2_grpc.add_HealthServicer_to_server(health, server)
server.add_insecure_port('127.0.0.1:1234')
server.start()
# Output information
print("1|1|tcp|127.0.0.1:1234|grpc")
sys.stdout.flush()
try:
while True:
time.sleep(60 * 60 * 24)
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
serve()

View File

@@ -1,103 +0,0 @@
package pluginUtils
import (
"fmt"
"reflect"
"github.com/rs/zerolog/log"
)
// CallFunc calls function with arguments
func CallFunc(fn reflect.Value, args ...interface{}) (interface{}, error) {
argumentsValue, err := convertArgs(fn, args...)
if err != nil {
log.Error().Err(err).Msg("convert arguments failed")
return nil, err
}
return call(fn, argumentsValue)
}
func convertArgs(fn reflect.Value, args ...interface{}) ([]reflect.Value, error) {
fnArgsNum := fn.Type().NumIn()
// function arguments should match exactly if function's last argument is not slice
if len(args) != fnArgsNum && (fnArgsNum == 0 || fn.Type().In(fnArgsNum-1).Kind() != reflect.Slice) {
return nil, fmt.Errorf("function expect %d arguments, but got %d", fnArgsNum, len(args))
}
argumentsValue := make([]reflect.Value, len(args))
for index := 0; index < len(args); index++ {
argument := args[index]
if argument == nil {
argumentsValue[index] = reflect.Zero(fn.Type().In(index))
continue
}
argumentValue := reflect.ValueOf(argument)
actualArgumentType := reflect.TypeOf(argument)
var expectArgumentType reflect.Type
if (index == fnArgsNum-1 && fn.Type().In(fnArgsNum-1).Kind() == reflect.Slice) || index > fnArgsNum-1 {
// last fn argument is slice
expectArgumentType = fn.Type().In(fnArgsNum - 1).Elem() // slice element type
// last argument is also slice, e.g. []int
if actualArgumentType.Kind() == reflect.Slice {
if actualArgumentType.Elem() != expectArgumentType {
err := fmt.Errorf("function argument %d's slice element type is not match, expect %v, actual %v",
index, expectArgumentType, actualArgumentType)
return nil, err
}
argumentsValue[index] = argumentValue
continue
}
} else {
expectArgumentType = fn.Type().In(index)
}
// type match
if expectArgumentType == actualArgumentType {
argumentsValue[index] = argumentValue
continue
}
// type not match, check if convertible
if !actualArgumentType.ConvertibleTo(expectArgumentType) {
// function argument type not match and not convertible
err := fmt.Errorf("function argument %d's type is neither match nor convertible, expect %v, actual %v",
index, expectArgumentType, actualArgumentType)
return nil, err
}
// convert argument to expect type
argumentsValue[index] = argumentValue.Convert(expectArgumentType)
}
return argumentsValue, nil
}
func call(fn reflect.Value, args []reflect.Value) (interface{}, error) {
resultValues := fn.Call(args)
if resultValues == nil {
// no returns
return nil, nil
} else if len(resultValues) == 2 {
// return two arguments: interface{}, error
if resultValues[1].Interface() != nil {
return resultValues[0].Interface(), resultValues[1].Interface().(error)
} else {
return resultValues[0].Interface(), nil
}
} else if len(resultValues) == 1 {
// return one argument
if err, ok := resultValues[0].Interface().(error); ok {
// return error
return nil, err
} else {
// return interface{}
return resultValues[0].Interface(), nil
}
} else {
// return more than 2 arguments, unexpected
err := fmt.Errorf("function should return at most 2 values")
return nil, err
}
}

View File

@@ -1,208 +0,0 @@
package pluginUtils
import (
"errors"
"fmt"
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
type data struct {
f interface{}
args []interface{}
expVal interface{}
expErr error
}
func TestCallFuncBasic(t *testing.T) {
params := []data{
// zero argument, zero return
{f: func() {}, args: []interface{}{}, expVal: nil, expErr: nil},
// zero argument, return one value
{f: func() int { return 1 }, args: []interface{}{}, expVal: 1, expErr: nil},
{f: func() string { return "a" }, args: []interface{}{}, expVal: "a", expErr: nil},
{f: func() interface{} { return 1.23 }, args: []interface{}{}, expVal: 1.23, expErr: nil},
// zero argument, return error
{f: func() error { return errors.New("xxx") }, args: []interface{}{}, expVal: nil, expErr: errors.New("xxx")},
// zero argument, return one value and error
{f: func() (int, error) { return 1, errors.New("xxx") }, args: []interface{}{}, expVal: 1, expErr: errors.New("xxx")},
{f: func() (interface{}, error) { return 1.23, errors.New("xxx") }, args: []interface{}{}, expVal: 1.23, expErr: errors.New("xxx")},
// one argument, zero return
{f: func(n int) {}, args: []interface{}{1}, expVal: nil, expErr: nil},
// one argument, return one value
{f: func(n int) int { return n * n }, args: []interface{}{2}, expVal: 4},
{f: func(c string) string { return c + c }, args: []interface{}{"p"}, expVal: "pp"},
{f: func(arg interface{}) interface{} { return fmt.Sprintf("%v", arg) }, args: []interface{}{1.23}, expVal: "1.23"},
// one argument, return one value and error
{f: func(arg interface{}) (interface{}, error) { return 1.23, errors.New("xxx") }, args: []interface{}{"a"}, expVal: 1.23, expErr: errors.New("xxx")},
// two arguments in same type
{f: func(a, b int) int { return a * b }, args: []interface{}{2, 3}, expVal: 6},
// two arguments in different type
{
f: func(n int, c string) string {
var s string
for i := 0; i < n; i++ {
s += c
}
return s
},
args: []interface{}{3, "p"},
expVal: "ppp",
},
// variable arguments list: ...int, ...interface{}
{
f: func(n ...int) int {
var sum int
for _, arg := range n {
sum += arg
}
return sum
},
args: []interface{}{1, 2, 3},
expVal: 6,
},
{
f: func(args ...interface{}) (interface{}, error) {
var result string
for _, arg := range args {
result += fmt.Sprintf("%v", arg)
}
return result, nil
},
args: []interface{}{1, 2.3, "4.5", "p"},
expVal: "12.34.5p",
},
{
f: func(a, b int8, n ...int) int {
var sum int
for _, arg := range n {
sum += arg
}
sum += int(a) + int(b)
return sum
},
args: []interface{}{1, 2, 3, 4.5},
expVal: 10,
},
{
f: func(a, b int8, n ...int) int {
sum := int(a) + int(b)
for _, arg := range n {
sum += arg
}
return sum
},
args: []interface{}{1, 2},
expVal: 3,
},
{
f: func(a []int, n ...int) int {
var sum int
for _, arg := range a {
sum += arg
}
for _, arg := range n {
sum += arg
}
return sum
},
args: []interface{}{[]int{1, 2}, 3, 4},
expVal: 10,
},
}
for _, p := range params {
fn := reflect.ValueOf(p.f)
val, err := CallFunc(fn, p.args...)
if !assert.Equal(t, p.expErr, err) {
t.Fatal(err)
}
if !assert.Equal(t, p.expVal, val) {
t.Fatal()
}
}
}
func TestCallFuncComplex(t *testing.T) {
params := []data{
// arguments include slice
{
f: func(a int, n []int, b int) int {
sum := a
for _, arg := range n {
sum += arg
}
sum += b
return sum
},
args: []interface{}{1, []int{2, 3}, 4},
expVal: 10,
},
// last argument is slice
{
f: func(n []int) int {
var sum int
for _, arg := range n {
sum += arg
}
return sum
},
args: []interface{}{[]int{1, 2, 3}},
expVal: 6,
},
{
f: func(a, b int, n []int) int {
sum := a + b
for _, arg := range n {
sum += arg
}
return sum
},
args: []interface{}{1, 2, []int{3, 4}},
expVal: 10,
},
}
for _, p := range params {
fn := reflect.ValueOf(p.f)
val, err := CallFunc(fn, p.args...)
if !assert.Equal(t, p.expErr, err) {
t.Fatal(err)
}
if !assert.Equal(t, p.expVal, val) {
t.Fatal()
}
}
}
func TestCallFuncAbnormal(t *testing.T) {
params := []data{
// return more than 2 values
{
f: func() (int, int, error) { return 1, 2, nil },
args: []interface{}{},
expVal: nil,
expErr: fmt.Errorf("function should return at most 2 values"),
},
}
for _, p := range params {
fn := reflect.ValueOf(p.f)
val, err := CallFunc(fn, p.args...)
if !assert.Equal(t, p.expErr, err) {
t.Fatal(err)
}
if !assert.Equal(t, p.expVal, val) {
t.Fatal()
}
}
}

View File

@@ -32,7 +32,7 @@ import (
"github.com/httprunner/hrp/internal/builtin"
"github.com/httprunner/hrp/internal/ga"
"github.com/httprunner/hrp/internal/json"
pluginInternal "github.com/httprunner/hrp/plugin/go"
pluginInternal "github.com/httprunner/plugin/go"
)
const (