diff --git a/go.mod b/go.mod
index b6ce0425b4ed..0a4669f8d5d9 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,6 @@ require (
github.com/golang/protobuf v1.5.3
github.com/gorilla/websocket v1.5.0
github.com/grafana/xk6-browser v1.2.1
- github.com/grafana/xk6-grpc v0.1.4-0.20230919144024-6ed5daf33509
github.com/grafana/xk6-output-prometheus-remote v0.3.1
github.com/grafana/xk6-redis v0.2.0
github.com/grafana/xk6-timers v0.1.2
@@ -30,6 +29,7 @@ require (
github.com/mccutchen/go-httpbin v1.1.2-0.20190116014521-c5cb2f4802fa
github.com/mstoykov/atlas v0.0.0-20220811071828-388f114305dd
github.com/mstoykov/envconfig v1.4.1-0.20220114105314-765c6d8c76f1
+ github.com/mstoykov/k6-taskqueue-lib v0.1.0
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d
github.com/serenize/snaker v0.0.0-20201027110005-a7ad2135616e
github.com/sirupsen/logrus v1.9.3
@@ -77,7 +77,6 @@ require (
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
- github.com/mstoykov/k6-taskqueue-lib v0.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
diff --git a/go.sum b/go.sum
index aaf8c8246015..e1a605418c0e 100644
--- a/go.sum
+++ b/go.sum
@@ -95,8 +95,6 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafana/xk6-browser v1.2.1 h1:O2fuHHvmmhXvWTPXzD+jsnt1XkVgVjx0+Lj1hsGIWMM=
github.com/grafana/xk6-browser v1.2.1/go.mod h1:D3k9/MQHnNKfyzU3fh32pHlrh3GY2LAlkY4wYt/Vn4Y=
-github.com/grafana/xk6-grpc v0.1.4-0.20230919144024-6ed5daf33509 h1:9ujE4S5cA3WDhRJnwNuUDtfk3w9FeWx6PaZ+lb3o46M=
-github.com/grafana/xk6-grpc v0.1.4-0.20230919144024-6ed5daf33509/go.mod h1:sFTwAsHAtp2f1PNiq0wPjJ7HrAIKploI7Y5mOYo+zIQ=
github.com/grafana/xk6-output-prometheus-remote v0.3.1 h1:X23rQzlJD8dXWB31DkxR4uPnuRFo8L0Y0H22fSG9xl0=
github.com/grafana/xk6-output-prometheus-remote v0.3.1/go.mod h1:0JLAm4ONsNUlNoxJXAwOCfA6GtDwTPs557OplAvE+3o=
github.com/grafana/xk6-redis v0.2.0 h1:iXmAKVlAxafZ/h8ptuXTFhGu63IFsyDI8QjUgWm66BU=
diff --git a/js/jsmodules.go b/js/jsmodules.go
index b19a1e74aeb5..b5328b05ea5f 100644
--- a/js/jsmodules.go
+++ b/js/jsmodules.go
@@ -17,7 +17,6 @@ import (
"go.k6.io/k6/js/modules/k6/ws"
"github.com/grafana/xk6-browser/browser"
- expGrpc "github.com/grafana/xk6-grpc/grpc"
"github.com/grafana/xk6-redis/redis"
"github.com/grafana/xk6-timers/timers"
"github.com/grafana/xk6-webcrypto/webcrypto"
@@ -35,7 +34,7 @@ func getInternalJSModules() map[string]interface{} {
"k6/experimental/redis": redis.New(),
"k6/experimental/webcrypto": webcrypto.New(),
"k6/experimental/websockets": &expws.RootModule{},
- "k6/experimental/grpc": expGrpc.New(),
+ "k6/experimental/grpc": grpc.New(), // TODO: make warning
"k6/experimental/timers": timers.New(),
"k6/experimental/tracing": tracing.New(),
"k6/experimental/browser": browser.New(),
diff --git a/js/modules/k6/grpc/client.go b/js/modules/k6/grpc/client.go
index 5c350e6de090..8b2b37e5e7e3 100644
--- a/js/modules/k6/grpc/client.go
+++ b/js/modules/k6/grpc/client.go
@@ -14,8 +14,6 @@ import (
"go.k6.io/k6/js/common"
"go.k6.io/k6/js/modules"
"go.k6.io/k6/lib/netext/grpcext"
- "go.k6.io/k6/lib/types"
- "go.k6.io/k6/metrics"
"github.com/dop251/goja"
"github.com/jhump/protoreflect/desc"
@@ -213,7 +211,7 @@ func (c *Client) Connect(addr string, params goja.Value) (bool, error) {
return false, common.NewInitContextError("connecting to a gRPC server in the init context is not supported")
}
- p, err := newConnectParams(c.vu.Runtime(), params)
+ p, err := newConnectParams(c.vu, params)
if err != nil {
return false, fmt.Errorf("invalid grpc.connect() parameters: %w", err)
}
@@ -299,9 +297,14 @@ func (c *Client) Invoke(
return nil, fmt.Errorf("method %q not found in file descriptors", method)
}
- p, err := c.parseInvokeParams(params)
+ p, err := newCallParams(c.vu, params)
if err != nil {
- return nil, fmt.Errorf("invalid grpc.invoke() parameters: %w", err)
+ return nil, fmt.Errorf("invalid GRPC's client.invoke() parameters: %w", err)
+ }
+
+ // k6 GRPC Invoke's default timeout is 2 minutes
+ if p.Timeout == time.Duration(0) {
+ p.Timeout = 2 * time.Minute
}
if req == nil {
@@ -315,17 +318,7 @@ func (c *Client) Invoke(
ctx, cancel := context.WithTimeout(c.vu.Context(), p.Timeout)
defer cancel()
- if state.Options.SystemTags.Has(metrics.TagURL) {
- p.TagsAndMeta.SetSystemTagOrMeta(metrics.TagURL, fmt.Sprintf("%s%s", c.addr, method))
- }
- parts := strings.Split(method[1:], "/")
- p.TagsAndMeta.SetSystemTagOrMetaIfEnabled(state.Options.SystemTags, metrics.TagService, parts[0])
- p.TagsAndMeta.SetSystemTagOrMetaIfEnabled(state.Options.SystemTags, metrics.TagMethod, parts[1])
-
- // Only set the name system tag if the user didn't explicitly set it beforehand
- if _, ok := p.TagsAndMeta.Tags.Get("name"); !ok {
- p.TagsAndMeta.SetSystemTagOrMetaIfEnabled(state.Options.SystemTags, metrics.TagName, method)
- }
+ p.SetSystemTags(state, c.addr, method)
reqmsg := grpcext.Request{
MethodDescriptor: methodDesc,
@@ -394,6 +387,7 @@ func (c *Client) convertToMethodInfo(fdset *descriptorpb.FileDescriptorSet) ([]M
appendMethodInfo(fd, sd, md)
}
}
+
messages := fd.Messages()
stack := make([]protoreflect.MessageDescriptor, 0, messages.Len())
@@ -427,218 +421,49 @@ func (c *Client) convertToMethodInfo(fdset *descriptorpb.FileDescriptorSet) ([]M
return rtn, nil
}
-type invokeParams struct {
- Metadata metadata.MD
- TagsAndMeta metrics.TagsAndMeta
- Timeout time.Duration
-}
-
-func (c *Client) parseInvokeParams(paramsVal goja.Value) (*invokeParams, error) {
- result := &invokeParams{
- Timeout: 1 * time.Minute,
- TagsAndMeta: c.vu.State().Tags.GetCurrentValues(),
- Metadata: metadata.New(nil),
- }
- if paramsVal == nil || goja.IsUndefined(paramsVal) || goja.IsNull(paramsVal) {
- return result, nil
- }
- rt := c.vu.Runtime()
- params := paramsVal.ToObject(rt)
- for _, k := range params.Keys() {
- switch k {
- case "metadata":
- md, err := newMetadata(params.Get(k))
- if err != nil {
- return result, fmt.Errorf("invalid metadata param: %w", err)
- }
-
- result.Metadata = md
- case "tags":
- if err := common.ApplyCustomUserTags(rt, &result.TagsAndMeta, params.Get(k)); err != nil {
- return result, fmt.Errorf("metric tags: %w", err)
- }
- case "timeout":
- var err error
- v := params.Get(k).Export()
- result.Timeout, err = types.GetDurationValue(v)
- if err != nil {
- return result, fmt.Errorf("invalid timeout value: %w", err)
- }
- case "headers":
- return result, errors.New("headers param is not supported anymore. Please, use metadata param instead")
- default:
- return result, fmt.Errorf("unknown param: %q", k)
- }
- }
- return result, nil
-}
-
-// newMetadata constructs a metadata.MD from the input value.
-func newMetadata(input goja.Value) (metadata.MD, error) {
- md := metadata.New(nil)
-
- if common.IsNullish(input) {
- return md, nil
- }
-
- v := input.Export()
+func walkFileDescriptors(seen map[string]struct{}, fd *desc.FileDescriptor) []*descriptorpb.FileDescriptorProto {
+ fds := []*descriptorpb.FileDescriptorProto{}
- raw, ok := v.(map[string]interface{})
- if !ok {
- return md, errors.New("must be an object with key-value pairs")
+ if _, ok := seen[fd.GetName()]; ok {
+ return fds
}
+ seen[fd.GetName()] = struct{}{}
+ fds = append(fds, fd.AsFileDescriptorProto())
- for hk, kv := range raw {
- var val string
- // The gRPC spec defines that Binary-valued keys end in -bin
- // https://grpc.io/docs/what-is-grpc/core-concepts/#metadata
- if strings.HasSuffix(hk, "-bin") {
- var binVal []byte
- if binVal, ok = kv.([]byte); !ok {
- return md, fmt.Errorf("%q value must be binary", hk)
- }
-
- // https://github.com/grpc/grpc-go/blob/v1.57.0/Documentation/grpc-metadata.md#storing-binary-data-in-metadata
- val = string(binVal)
- } else if val, ok = kv.(string); !ok {
- return md, fmt.Errorf("%q value must be a string", hk)
- }
-
- md.Append(hk, val)
+ for _, dep := range fd.GetDependencies() {
+ deps := walkFileDescriptors(seen, dep)
+ fds = append(fds, deps...)
}
- return md, nil
-}
-
-type connectParams struct {
- IsPlaintext bool
- ReflectionMetadata metadata.MD
- UseReflectionProtocol bool
- Timeout time.Duration
- MaxReceiveSize int64
- MaxSendSize int64
- TLS map[string]interface{}
+ return fds
}
-func newConnectParams(rt *goja.Runtime, input goja.Value) (connectParams, error) { //nolint:funlen,gocognit,cyclop
- params := connectParams{
- IsPlaintext: false,
- UseReflectionProtocol: false,
- ReflectionMetadata: metadata.New(nil),
- Timeout: time.Minute,
- MaxReceiveSize: 0,
- MaxSendSize: 0,
+// sanitizeMethodName
+func sanitizeMethodName(name string) string {
+ if name == "" {
+ return name
}
- if common.IsNullish(input) {
- return params, nil
+ if !strings.HasPrefix(name, "/") {
+ name = "/" + name
}
- raw := input.ToObject(rt)
-
- for _, k := range raw.Keys() {
- v := raw.Get(k).Export()
-
- switch k {
- case "plaintext":
- var ok bool
- params.IsPlaintext, ok = v.(bool)
- if !ok {
- return params, fmt.Errorf("invalid plaintext value: '%#v', it needs to be boolean", v)
- }
- case "timeout":
- var err error
- params.Timeout, err = types.GetDurationValue(v)
- if err != nil {
- return params, fmt.Errorf("invalid timeout value: %w", err)
- }
- case "reflect":
- var ok bool
- params.UseReflectionProtocol, ok = v.(bool)
- if !ok {
- return params, fmt.Errorf("invalid reflect value: '%#v', it needs to be boolean", v)
- }
- case "reflectMetadata":
- md, err := newMetadata(raw.Get(k))
- if err != nil {
- return params, fmt.Errorf("invalid reflectMetadata param: %w", err)
- }
- params.ReflectionMetadata = md
- case "maxReceiveSize":
- var ok bool
- params.MaxReceiveSize, ok = v.(int64)
- if !ok {
- return params, fmt.Errorf("invalid maxReceiveSize value: '%#v', it needs to be an integer", v)
- }
- if params.MaxReceiveSize < 0 {
- return params, fmt.Errorf("invalid maxReceiveSize value: '%#v, it needs to be a positive integer", v)
- }
- case "maxSendSize":
- var ok bool
- params.MaxSendSize, ok = v.(int64)
- if !ok {
- return params, fmt.Errorf("invalid maxSendSize value: '%#v', it needs to be an integer", v)
- }
- if params.MaxSendSize < 0 {
- return params, fmt.Errorf("invalid maxSendSize value: '%#v, it needs to be a positive integer", v)
- }
- case "tls":
- var ok bool
- params.TLS, ok = v.(map[string]interface{})
-
- if !ok {
- return params, fmt.Errorf("invalid tls value: '%#v', expected (optional) keys: cert, key, password, and cacerts", v)
- }
- // optional map keys below
- if cert, certok := params.TLS["cert"]; certok {
- if _, ok = cert.(string); !ok {
- return params, fmt.Errorf("invalid tls cert value: '%#v', it needs to be a PEM formatted string", v)
- }
- }
- if key, keyok := params.TLS["key"]; keyok {
- if _, ok = key.(string); !ok {
- return params, fmt.Errorf("invalid tls key value: '%#v', it needs to be a PEM formatted string", v)
- }
- }
- if pass, passok := params.TLS["password"]; passok {
- if _, ok = pass.(string); !ok {
- return params, fmt.Errorf("invalid tls password value: '%#v', it needs to be a string", v)
- }
- }
- if cacerts, cacertsok := params.TLS["cacerts"]; cacertsok {
- var cacertsArray []interface{}
- if cacertsArray, ok = cacerts.([]interface{}); ok {
- for _, cacertsArrayEntry := range cacertsArray {
- if _, ok = cacertsArrayEntry.(string); !ok {
- return params, fmt.Errorf("invalid tls cacerts value: '%#v',"+
- " it needs to be a string or an array of PEM formatted strings", v)
- }
- }
- } else if _, ok = cacerts.(string); !ok {
- return params, fmt.Errorf("invalid tls cacerts value: '%#v',"+
- " it needs to be a string or an array of PEM formatted strings", v)
- }
- }
- default:
- return params, fmt.Errorf("unknown connect param: %q", k)
- }
- }
- return params, nil
+ return name
}
-func walkFileDescriptors(seen map[string]struct{}, fd *desc.FileDescriptor) []*descriptorpb.FileDescriptorProto {
- fds := []*descriptorpb.FileDescriptorProto{}
+// getMethodDescriptor sanitize it, and gets GRPC method descriptor or an error if not found
+func (c *Client) getMethodDescriptor(method string) (protoreflect.MethodDescriptor, error) {
+ method = sanitizeMethodName(method)
- if _, ok := seen[fd.GetName()]; ok {
- return fds
+ if method == "" {
+ return nil, errors.New("method to invoke cannot be empty")
}
- seen[fd.GetName()] = struct{}{}
- fds = append(fds, fd.AsFileDescriptorProto())
- for _, dep := range fd.GetDependencies() {
- deps := walkFileDescriptors(seen, dep)
- fds = append(fds, deps...)
+ methodDesc := c.mds[method]
+
+ if methodDesc == nil {
+ return nil, fmt.Errorf("method %q not found in file descriptors", method)
}
- return fds
+ return methodDesc, nil
}
diff --git a/js/modules/k6/grpc/client_test.go b/js/modules/k6/grpc/client_test.go
index f1212a3fbece..25eb60e3e7a2 100644
--- a/js/modules/k6/grpc/client_test.go
+++ b/js/modules/k6/grpc/client_test.go
@@ -9,29 +9,31 @@ import (
"strings"
"testing"
+ k6grpc "go.k6.io/k6/js/modules/k6/grpc"
+ "go.k6.io/k6/lib/netext/grpcext"
+ "go.k6.io/k6/lib/testutils/httpmultibin"
+ grpcanytesting "go.k6.io/k6/lib/testutils/httpmultibin/grpc_any_testing"
+ "go.k6.io/k6/lib/testutils/httpmultibin/grpc_testing"
+ "go.k6.io/k6/lib/testutils/httpmultibin/grpc_wrappers_testing"
+ "go.k6.io/k6/metrics"
+
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/codes"
"google.golang.org/grpc/reflection"
+ "google.golang.org/grpc/status"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
"google.golang.org/protobuf/types/known/wrapperspb"
- "gopkg.in/guregu/null.v3"
"github.com/golang/protobuf/ptypes/any"
+ _struct "github.com/golang/protobuf/ptypes/struct"
"github.com/golang/protobuf/ptypes/wrappers"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
+ v1alphagrpc "google.golang.org/grpc/reflection/grpc_reflection_v1alpha"
grpcstats "google.golang.org/grpc/stats"
- "google.golang.org/grpc/status"
-
- k6grpc "go.k6.io/k6/js/modules/k6/grpc"
- "go.k6.io/k6/lib/netext/grpcext"
- "go.k6.io/k6/lib/testutils/httpmultibin"
- grpcanytesting "go.k6.io/k6/lib/testutils/httpmultibin/grpc_any_testing"
- "go.k6.io/k6/lib/testutils/httpmultibin/grpc_testing"
- "go.k6.io/k6/lib/testutils/httpmultibin/grpc_wrappers_testing"
- "go.k6.io/k6/metrics"
+ "gopkg.in/guregu/null.v3"
)
func TestClient(t *testing.T) {
@@ -638,7 +640,51 @@ func TestClient(t *testing.T) {
{
name: "ReflectInvoke",
setup: func(tb *httpmultibin.HTTPMultiBin) {
+ // this register both reflection APIs v1 and v1alpha
reflection.Register(tb.ServerGRPC)
+
+ tb.GRPCStub.EmptyCallFunc = func(ctx context.Context, _ *grpc_testing.Empty) (*grpc_testing.Empty, error) {
+ return &grpc_testing.Empty{}, nil
+ }
+ },
+ initString: codeBlock{
+ code: `var client = new grpc.Client();`,
+ },
+ vuString: codeBlock{
+ code: `
+ client.connect("GRPCBIN_ADDR", {reflect: true})
+ client.invoke("grpc.testing.TestService/EmptyCall", {})
+ `,
+ },
+ },
+ {
+ name: "ReflectV1Alpha_Invoke",
+ setup: func(tb *httpmultibin.HTTPMultiBin) {
+ // this register only v1alpha (this could be removed with removal v1alpha from grpc-go)
+ s := tb.ServerGRPC
+ svr := reflection.NewServer(reflection.ServerOptions{Services: s})
+ v1alphagrpc.RegisterServerReflectionServer(s, svr)
+
+ tb.GRPCStub.EmptyCallFunc = func(ctx context.Context, _ *grpc_testing.Empty) (*grpc_testing.Empty, error) {
+ return &grpc_testing.Empty{}, nil
+ }
+ },
+ initString: codeBlock{
+ code: `var client = new grpc.Client();`,
+ },
+ vuString: codeBlock{
+ code: `
+ client.connect("GRPCBIN_ADDR", {reflect: true})
+ client.invoke("grpc.testing.TestService/EmptyCall", {})
+ `,
+ },
+ },
+ {
+ name: "ReflectV1Invoke",
+ setup: func(tb *httpmultibin.HTTPMultiBin) {
+ // this register only reflection APIs v1
+ reflection.RegisterV1(tb.ServerGRPC)
+
tb.GRPCStub.EmptyCallFunc = func(ctx context.Context, _ *grpc_testing.Empty) (*grpc_testing.Empty, error) {
return &grpc_testing.Empty{}, nil
}
@@ -852,6 +898,63 @@ func TestClient(t *testing.T) {
`,
},
},
+ {
+ name: "ValueReflection",
+ setup: func(hb *httpmultibin.HTTPMultiBin) {
+ reflection.Register(hb.ServerGRPC)
+
+ srv := grpc_wrappers_testing.Register(hb.ServerGRPC)
+
+ srv.TestValueImplementation = func(_ context.Context, in *_struct.Value) (*_struct.Value, error) {
+ if in.GetNumberValue() == 12 {
+ return &_struct.Value{
+ Kind: &_struct.Value_NumberValue{
+ NumberValue: 42,
+ },
+ }, nil
+ }
+
+ if in.GetStringValue() != "" {
+ return &_struct.Value{
+ Kind: &_struct.Value_StringValue{
+ StringValue: "hey " + in.GetStringValue(),
+ },
+ }, nil
+ }
+
+ return &_struct.Value{
+ Kind: &_struct.Value_StringValue{
+ StringValue: "I don't know what to answer",
+ },
+ }, nil
+ }
+ },
+ initString: codeBlock{
+ code: `
+ const client = new grpc.Client();
+ `,
+ },
+ vuString: codeBlock{
+ code: `
+ client.connect("GRPCBIN_ADDR", {reflect: true});
+
+ let respString = client.invoke("grpc.wrappers.testing.Service/TestValue", "John")
+ if (respString.message !== "hey John") {
+ throw new Error("expected to get 'hey John', but got a " + respString.message)
+ }
+
+ let respNumber = client.invoke("grpc.wrappers.testing.Service/TestValue", 12)
+ if (respNumber.message !== 42) {
+ throw new Error("expected to get '42', but got a " + respNumber.message)
+ }
+
+ let respBool = client.invoke("grpc.wrappers.testing.Service/TestValue", false)
+ if (respBool.message !== "I don't know what to answer") {
+ throw new Error("expected to get 'I don't know what to answer', but got a " + respBool.message)
+ }
+ `,
+ },
+ },
}
for _, tt := range tests {
@@ -870,7 +973,6 @@ func TestClient(t *testing.T) {
assertResponse(t, tt.initString, err, val, ts)
ts.ToVUContext()
-
val, err = ts.Run(tt.vuString.code)
assertResponse(t, tt.vuString, err, val, ts)
})
@@ -893,6 +995,7 @@ func TestClient_TlsParameters(t *testing.T) {
clientAuthBad := "-----BEGIN CERTIFICATE-----\\nMIIB2TCCAX6gAwIBAgIUJIZKiR78AH2ioZ+Jae/sElgH85kwCgYIKoZIzj0EAwIw\\nEDEOMAwGA1UEAwwFTXkgQ0EwHhcNMjMwNzA3MTAyNjQ2WhcNMjQwNzA2MTAyNjQ2\\nWjARMQ8wDQYDVQQDDAZjbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASj\\nOziUDGBuCi7QwIGfMzoNj4phzJkJ4w9h7SOHEsCFCSZ7x1i6MGxXvX5Ol3j/W93S\\ntSJlCPvvxGXVawAQHJ4Ho4G0MIGxMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD\\nAgWgMCwGCWCGSAGG+EIBDQQfFh1Mb2NhbCBUZXN0IENsaWVudCBDZXJ0aWZpY2F0\\nZTAdBgNVHQ4EFgQUrbSXtZnDxJwTmesLyjxuMy9JtQswHwYDVR0jBBgwFoAUpLpA\\nQPJYBb7wSQGCrKElEfj1+9YwDgYDVR0PAQH/BAQDAgXgMBMGA1UdJQQMMAoGCCsG\\nAQUFBwMEMAoGCCqGSM49BAMCA0kAMEYCIQDcHrzug3V3WvUU+tEKhG1C4cPG5rPJ\\n/y3oOoM0roOnsgIhAP23UmiC6Qdgj+MOhXWSaNt3exWvlxdKmLm2edkxaTs+\\n-----END CERTIFICATE-----"
trivialKeyPassword := "abc123"
+ trivialWrongKeyPassword := "abc321"
tests := []testcase{
{
@@ -943,7 +1046,7 @@ func TestClient_TlsParameters(t *testing.T) {
tb.ServerHTTP2.TLS.ClientCAs = clientCAPool
},
initString: codeBlock{code: "var client = new grpc.Client();"},
- vuString: codeBlock{code: fmt.Sprintf(`client.connect("GRPCBIN_ADDR", { tls: { cacerts: "%s", cert: "%s", key: "%s" }});`, localHostCert, clientAuth, clientAuthKey)},
+ vuString: codeBlock{code: fmt.Sprintf(`client.connect("GRPCBIN_ADDR", { tls: { cacerts: ["%s"], cert: "%s", key: "%s" }});`, localHostCert, clientAuth, clientAuthKey)},
},
{
name: "ConnectTlsEncryptedKey",
@@ -960,9 +1063,10 @@ func TestClient_TlsParameters(t *testing.T) {
name: "ConnectTlsEncryptedKeyDecryptionFailed",
initString: codeBlock{code: "var client = new grpc.Client();"},
vuString: codeBlock{
- code: fmt.Sprintf(`client.connect("GRPCBIN_ADDR", { timeout: '5s', tls: { cert: "%s", key: "%s", password: "abc321" }});`,
+ code: fmt.Sprintf(`client.connect("GRPCBIN_ADDR", { timeout: '5s', tls: { cert: "%s", key: "%s", password: "%s" }});`,
clientAuth,
clientAuthKeyEncrypted,
+ trivialWrongKeyPassword,
),
err: "x509: decryption password incorrect",
},
@@ -977,7 +1081,7 @@ func TestClient_TlsParameters(t *testing.T) {
},
initString: codeBlock{code: `var client = new grpc.Client();`},
vuString: codeBlock{
- code: fmt.Sprintf(`client.connect("GRPCBIN_ADDR", { tls: { cacerts: ["%s"], cert: "%s", key: "%s" }});`,
+ code: fmt.Sprintf(`client.connect("GRPCBIN_ADDR", { timeout: '2s', tls: { cacerts: ["%s"], cert: "%s", key: "%s" }});`,
localHostCert,
clientAuthBad,
clientAuthKey),
@@ -995,7 +1099,7 @@ func TestClient_TlsParameters(t *testing.T) {
initString: codeBlock{code: `var client = new grpc.Client();`},
vuString: codeBlock{
code: fmt.Sprintf(`
- client.connect("GRPCBIN_ADDR", { tls: { cacerts: ["%s"], cert: "%s", key: "%s", password: "%s" }});
+ client.connect("GRPCBIN_ADDR", { timeout: '2s', tls: { cacerts: ["%s"], cert: "%s", key: "%s", password: "%s" }});
`,
localHostCert,
clientAuthBad,
@@ -1030,6 +1134,33 @@ func TestClient_TlsParameters(t *testing.T) {
clientAuthKey),
},
},
+ {
+ name: "ConnectTlsBadAuthInvokeFails",
+ setup: func(tb *httpmultibin.HTTPMultiBin) {
+ clientCAPool := x509.NewCertPool()
+ clientCAPool.AppendCertsFromPEM(clientAuthCA)
+ tb.ServerHTTP2.TLS.ClientAuth = tls.RequireAndVerifyClientCert
+ tb.ServerHTTP2.TLS.ClientCAs = clientCAPool
+ tb.GRPCStub.EmptyCallFunc = func(context.Context, *grpc_testing.Empty) (*grpc_testing.Empty, error) {
+ return &grpc_testing.Empty{}, nil
+ }
+ },
+ initString: codeBlock{code: `
+ var client = new grpc.Client();
+ client.load([], "../../../../lib/testutils/httpmultibin/grpc_testing/test.proto");`},
+ vuString: codeBlock{
+ code: fmt.Sprintf(`
+ client.connect("GRPCBIN_ADDR", { timeout: '2s', tls: { cacerts: ["%s"], cert: "%s", key: "%s" }});
+ var resp = client.invoke("grpc.testing.TestService/EmptyCall", {})
+ if (resp.status !== grpc.StatusOK) {
+ throw new Error("unexpected error: " + JSON.stringify(resp.error) + "or status: " + resp.status)
+ }`,
+ localHostCert,
+ clientAuthBad,
+ clientAuthKey),
+ err: "remote error: tls: bad certificate",
+ },
+ },
}
for _, tt := range tests {
@@ -1048,7 +1179,6 @@ func TestClient_TlsParameters(t *testing.T) {
assertResponse(t, tt.initString, err, val, ts)
ts.ToVUContext()
-
val, err = ts.Run(tt.vuString.code)
assertResponse(t, tt.vuString, err, val, ts)
})
@@ -1128,35 +1258,6 @@ func TestDebugStat(t *testing.T) {
}
}
-func TestClientLoadProto(t *testing.T) {
- t.Parallel()
-
- ts := newTestState(t)
-
- code := `
- var client = new grpc.Client();
- client.load([], "../../../../lib/testutils/httpmultibin/grpc_testing/nested_types.proto");`
-
- _, err := ts.VU.Runtime().RunString(ts.httpBin.Replacer.Replace(code))
- assert.Nil(t, err, "It was not expected that there would be an error, but it got: %v", err)
-
- expectedTypes := []string{
- "grpc.testing.Outer",
- "grpc.testing.Outer.MiddleAA",
- "grpc.testing.Outer.MiddleAA.Inner",
- "grpc.testing.Outer.MiddleBB",
- "grpc.testing.Outer.MiddleBB.Inner",
- "grpc.testing.MeldOuter",
- }
-
- for _, expected := range expectedTypes {
- found, err := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(expected))
-
- assert.NotNil(t, found, "Expected to find the message type %s, but an error occurred", expected)
- assert.Nil(t, err, "It was not expected that there would be an error, but it got: %v", err)
- }
-}
-
func TestClientConnectionReflectMetadata(t *testing.T) {
t.Parallel()
@@ -1199,3 +1300,37 @@ func TestClientConnectionReflectMetadata(t *testing.T) {
assert.True(t, foundReflectionCall, "expected to find a reflection call in the logs, but didn't")
}
+
+func TestClientLoadProto(t *testing.T) {
+ t.Parallel()
+
+ ts := newTestState(t)
+
+ tt := testcase{
+ name: "LoadNestedTypesProto",
+ initString: codeBlock{
+ code: `
+ var client = new grpc.Client();
+ client.load([], "../../../../lib/testutils/httpmultibin/nested_types/nested_types.proto");`,
+ },
+ }
+
+ val, err := ts.Run(tt.initString.code)
+ assertResponse(t, tt.initString, err, val, ts)
+
+ expectedTypes := []string{
+ "grpc.testdata.nested.types.Outer",
+ "grpc.testdata.nested.types.Outer.MiddleAA",
+ "grpc.testdata.nested.types.Outer.MiddleAA.Inner",
+ "grpc.testdata.nested.types.Outer.MiddleBB",
+ "grpc.testdata.nested.types.Outer.MiddleBB.Inner",
+ "grpc.testdata.nested.types.MeldOuter",
+ }
+
+ for _, expected := range expectedTypes {
+ found, err := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName(expected))
+
+ assert.NotNil(t, found, "Expected to find the message type %s, but an error occurred", expected)
+ assert.Nil(t, err, "It was not expected that there would be an error, but it got: %v", err)
+ }
+}
diff --git a/js/modules/k6/grpc/grpc.go b/js/modules/k6/grpc/grpc.go
index 46ab0c923b6e..fdf2a8f42ec4 100644
--- a/js/modules/k6/grpc/grpc.go
+++ b/js/modules/k6/grpc/grpc.go
@@ -1,7 +1,13 @@
+// Package grpc is the root module of the k6-grpc extension.
package grpc
import (
+ "errors"
+ "fmt"
+
"github.com/dop251/goja"
+ "github.com/mstoykov/k6-taskqueue-lib/taskqueue"
+ "go.k6.io/k6/js/common"
"go.k6.io/k6/js/modules"
"google.golang.org/grpc/codes"
)
@@ -15,6 +21,7 @@ type (
ModuleInstance struct {
vu modules.VU
exports map[string]interface{}
+ metrics *instanceMetrics
}
)
@@ -31,18 +38,26 @@ func New() *RootModule {
// NewModuleInstance implements the modules.Module interface to return
// a new instance for each VU.
func (*RootModule) NewModuleInstance(vu modules.VU) modules.Instance {
+ metrics, err := registerMetrics(vu.InitEnv().Registry)
+ if err != nil {
+ common.Throw(vu.Runtime(), fmt.Errorf("failed to register GRPC module metrics: %w", err))
+ }
+
mi := &ModuleInstance{
vu: vu,
exports: make(map[string]interface{}),
+ metrics: metrics,
}
mi.exports["Client"] = mi.NewClient
mi.defineConstants()
+ mi.exports["Stream"] = mi.stream
+
return mi
}
// NewClient is the JS constructor for the grpc Client.
-func (mi *ModuleInstance) NewClient(call goja.ConstructorCall) *goja.Object {
+func (mi *ModuleInstance) NewClient(_ goja.ConstructorCall) *goja.Object {
rt := mi.vu.Runtime()
return rt.ToValue(&Client{vu: mi.vu}).ToObject(rt)
}
@@ -79,3 +94,78 @@ func (mi *ModuleInstance) Exports() modules.Exports {
Named: mi.exports,
}
}
+
+// stream returns a new stream object
+func (mi *ModuleInstance) stream(c goja.ConstructorCall) *goja.Object {
+ rt := mi.vu.Runtime()
+
+ client, err := extractClient(c.Argument(0), rt)
+ if err != nil {
+ common.Throw(rt, fmt.Errorf("invalid GRPC Stream's client: %w", err))
+ }
+
+ methodName := sanitizeMethodName(c.Argument(1).String())
+ methodDescriptor, err := client.getMethodDescriptor(methodName)
+ if err != nil {
+ common.Throw(rt, fmt.Errorf("invalid GRPC Stream's method: %w", err))
+ }
+
+ p, err := newCallParams(mi.vu, c.Argument(2))
+ if err != nil {
+ common.Throw(rt, fmt.Errorf("invalid GRPC Stream's parameters: %w", err))
+ }
+
+ p.SetSystemTags(mi.vu.State(), client.addr, methodName)
+
+ logger := mi.vu.State().Logger.WithField("streamMethod", methodName)
+
+ s := &stream{
+ vu: mi.vu,
+ client: client,
+ methodDescriptor: methodDescriptor,
+ method: methodName,
+ logger: logger,
+
+ tq: taskqueue.New(mi.vu.RegisterCallback),
+
+ instanceMetrics: mi.metrics,
+ builtinMetrics: mi.vu.State().BuiltinMetrics,
+ done: make(chan struct{}),
+ writingState: opened,
+
+ writeQueueCh: make(chan message),
+
+ eventListeners: newEventListeners(),
+ obj: rt.NewObject(),
+ tagsAndMeta: &p.TagsAndMeta,
+ }
+
+ defineStream(rt, s)
+
+ err = s.beginStream(p)
+ if err != nil {
+ s.tq.Close()
+
+ common.Throw(rt, err)
+ }
+
+ return s.obj
+}
+
+// extractClient extracts & validates a grpc.Client from a goja.Value.
+func extractClient(v goja.Value, rt *goja.Runtime) (*Client, error) {
+ if common.IsNullish(v) {
+ return nil, errors.New("empty gRPC client")
+ }
+
+ client, ok := v.ToObject(rt).Export().(*Client)
+ if !ok {
+ return nil, errors.New("not a gRPC client")
+ }
+
+ if client.conn == nil {
+ return nil, errors.New("no gRPC connection, you must call connect first")
+ }
+
+ return client, nil
+}
diff --git a/js/modules/k6/grpc/helpers_test.go b/js/modules/k6/grpc/helpers_test.go
new file mode 100644
index 000000000000..b9c39023347e
--- /dev/null
+++ b/js/modules/k6/grpc/helpers_test.go
@@ -0,0 +1,53 @@
+package grpc_test
+
+import (
+ "errors"
+ "strings"
+ "testing"
+
+ "github.com/dop251/goja"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.k6.io/k6/metrics"
+)
+
+func assertResponse(t *testing.T, cb codeBlock, err error, val goja.Value, ts testState) {
+ if isWindows && cb.windowsErr != "" && err != nil {
+ err = errors.New(strings.ReplaceAll(err.Error(), cb.windowsErr, cb.err))
+ }
+ if cb.err == "" {
+ assert.NoError(t, err)
+ } else {
+ require.Error(t, err)
+ assert.Contains(t, err.Error(), cb.err)
+ }
+ if cb.val != nil {
+ require.NotNil(t, val)
+ assert.Equal(t, cb.val, val.Export())
+ }
+ if cb.asserts != nil {
+ cb.asserts(t, ts.httpBin, ts.samples, err)
+ }
+}
+
+func assertMetricEmitted(
+ t *testing.T,
+ metricName string, //nolint:unparam
+ sampleContainers []metrics.SampleContainer,
+ url string,
+) {
+ seenMetric := false
+
+ for _, sampleContainer := range sampleContainers {
+ for _, sample := range sampleContainer.GetSamples() {
+ surl, ok := sample.Tags.Get("url")
+ assert.True(t, ok)
+ if surl == url {
+ if sample.Metric.Name == metricName {
+ seenMetric = true
+ }
+ }
+ }
+ }
+ assert.True(t, seenMetric, "url %s didn't emit %s", url, metricName)
+}
diff --git a/vendor/github.com/grafana/xk6-grpc/grpc/listeners.go b/js/modules/k6/grpc/listeners.go
similarity index 100%
rename from vendor/github.com/grafana/xk6-grpc/grpc/listeners.go
rename to js/modules/k6/grpc/listeners.go
diff --git a/vendor/github.com/grafana/xk6-grpc/grpc/metrics.go b/js/modules/k6/grpc/metrics.go
similarity index 100%
rename from vendor/github.com/grafana/xk6-grpc/grpc/metrics.go
rename to js/modules/k6/grpc/metrics.go
diff --git a/vendor/github.com/grafana/xk6-grpc/grpc/params.go b/js/modules/k6/grpc/params.go
similarity index 100%
rename from vendor/github.com/grafana/xk6-grpc/grpc/params.go
rename to js/modules/k6/grpc/params.go
diff --git a/js/modules/k6/grpc/params_test.go b/js/modules/k6/grpc/params_test.go
new file mode 100644
index 000000000000..c43c6d91fa2a
--- /dev/null
+++ b/js/modules/k6/grpc/params_test.go
@@ -0,0 +1,181 @@
+package grpc
+
+import (
+ "io"
+ "testing"
+ "time"
+
+ "github.com/dop251/goja"
+ "github.com/sirupsen/logrus"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "go.k6.io/k6/js/common"
+ "go.k6.io/k6/js/modulestest"
+ "go.k6.io/k6/lib"
+ "go.k6.io/k6/metrics"
+ "google.golang.org/grpc/metadata"
+ "gopkg.in/guregu/null.v3"
+)
+
+func TestCallParamsInvalidInput(t *testing.T) {
+ t.Parallel()
+
+ testCases := []struct {
+ Name string
+ JSON string
+ ErrContains string
+ }{
+ {
+ Name: "InvalidParam",
+ JSON: `{ void: true }`,
+ ErrContains: `unknown param: "void"`,
+ },
+ {
+ Name: "InvalidTimeoutType",
+ JSON: `{ timeout: true }`,
+ ErrContains: `invalid timeout value: unable to use type bool as a duration value`,
+ },
+ {
+ Name: "InvalidTimeout",
+ JSON: `{ timeout: "please" }`,
+ ErrContains: `invalid duration`,
+ },
+ {
+ Name: "InvalidMetadata",
+ JSON: `{ metadata: "lorem" }`,
+ ErrContains: `invalid metadata param: must be an object with key-value pairs`,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(tc.Name, func(t *testing.T) {
+ t.Parallel()
+
+ testRuntime, params := newParamsTestRuntime(t, tc.JSON)
+
+ _, err := newCallParams(testRuntime.VU, params)
+
+ assert.ErrorContains(t, err, tc.ErrContains)
+ })
+ }
+}
+
+func TestCallParamsMetadata(t *testing.T) {
+ t.Parallel()
+
+ testCases := []struct {
+ Name string
+ JSON string
+ ExpectedMetadata metadata.MD
+ }{
+ {
+ Name: "EmptyMetadata",
+ JSON: `{}`,
+ ExpectedMetadata: metadata.New(nil),
+ },
+ {
+ Name: "Metadata",
+ JSON: `{metadata: {foo: "bar", baz: "qux"}}`,
+ ExpectedMetadata: metadata.New(map[string]string{"foo": "bar", "baz": "qux"}),
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(tc.Name, func(t *testing.T) {
+ t.Parallel()
+
+ testRuntime, params := newParamsTestRuntime(t, tc.JSON)
+
+ p, err := newCallParams(testRuntime.VU, params)
+
+ require.NoError(t, err)
+ assert.Equal(t, tc.ExpectedMetadata, p.Metadata)
+ })
+ }
+}
+
+func TestCallParamsTimeOutParse(t *testing.T) {
+ t.Parallel()
+
+ testCases := []struct {
+ Name string
+ JSON string
+ Timeout time.Duration
+ }{
+ {
+ Name: "StringTimeout",
+ JSON: `{ timeout: "1h42m" }`,
+ Timeout: time.Hour + 42*time.Minute,
+ },
+ {
+ Name: "FloatTimeout",
+ JSON: `{ timeout: 400.50 }`,
+ Timeout: 400*time.Millisecond + 500*time.Microsecond,
+ },
+ {
+ Name: "IntegerTimeout",
+ JSON: `{ timeout: 2000 }`,
+ Timeout: 2000 * time.Millisecond,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(tc.Name, func(t *testing.T) {
+ t.Parallel()
+
+ testRuntime, params := newParamsTestRuntime(t, tc.JSON)
+
+ p, err := newCallParams(testRuntime.VU, params)
+ require.NoError(t, err)
+
+ assert.Equal(t, tc.Timeout, p.Timeout)
+ })
+ }
+}
+
+// newParamsTestRuntime creates a new test runtime
+// that could be used to test the params
+// it also moves to the VU context and creates the params
+// goja value that could be used in the tests
+func newParamsTestRuntime(t *testing.T, paramsJSON string) (*modulestest.Runtime, goja.Value) {
+ t.Helper()
+
+ testRuntime := modulestest.NewRuntime(t)
+ registry := metrics.NewRegistry()
+ root, err := lib.NewGroup("", nil)
+ require.NoError(t, err)
+
+ logger := logrus.New()
+ logger.SetLevel(logrus.InfoLevel)
+ logger.Out = io.Discard
+
+ state := &lib.State{
+ Group: root,
+ Options: lib.Options{
+ SystemTags: metrics.NewSystemTagSet(
+ metrics.TagName,
+ metrics.TagURL,
+ ),
+ UserAgent: null.StringFrom("k6-test"),
+ },
+ BuiltinMetrics: metrics.RegisterBuiltinMetrics(registry),
+ Tags: lib.NewVUStateTags(registry.RootTagSet()),
+ Logger: logger,
+ }
+
+ testRuntime.MoveToVUContext(state)
+
+ _, err = testRuntime.VU.Runtime().RunString(`let params = ` + paramsJSON + `;`)
+ require.NoError(t, err)
+
+ params := testRuntime.VU.Runtime().Get("params")
+ require.False(t, common.IsNullish(params))
+
+ return testRuntime, params
+}
diff --git a/vendor/github.com/grafana/xk6-grpc/grpc/stream.go b/js/modules/k6/grpc/stream.go
similarity index 99%
rename from vendor/github.com/grafana/xk6-grpc/grpc/stream.go
rename to js/modules/k6/grpc/stream.go
index e4ce159bbfe0..d4df1f480c65 100644
--- a/vendor/github.com/grafana/xk6-grpc/grpc/stream.go
+++ b/js/modules/k6/grpc/stream.go
@@ -9,13 +9,14 @@ import (
"sync"
"time"
- "github.com/dop251/goja"
- "github.com/grafana/xk6-grpc/lib/netext/grpcext"
- "github.com/mstoykov/k6-taskqueue-lib/taskqueue"
- "github.com/sirupsen/logrus"
"go.k6.io/k6/js/common"
"go.k6.io/k6/js/modules"
+ "go.k6.io/k6/lib/netext/grpcext"
"go.k6.io/k6/metrics"
+
+ "github.com/dop251/goja"
+ "github.com/mstoykov/k6-taskqueue-lib/taskqueue"
+ "github.com/sirupsen/logrus"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/reflect/protoreflect"
diff --git a/js/modules/k6/grpc/stream_test.go b/js/modules/k6/grpc/stream_test.go
new file mode 100644
index 000000000000..dbc660b11cc2
--- /dev/null
+++ b/js/modules/k6/grpc/stream_test.go
@@ -0,0 +1,365 @@
+package grpc_test
+
+import (
+ "context"
+ "errors"
+ "io"
+ "strings"
+ "testing"
+ "time"
+
+ "go.k6.io/k6/lib/testutils/grpcservice"
+ "go.k6.io/k6/lib/testutils/httpmultibin/grpc_wrappers_testing"
+
+ "github.com/dop251/goja"
+ "github.com/golang/protobuf/ptypes/wrappers"
+ "github.com/stretchr/testify/assert"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/metadata"
+ "google.golang.org/grpc/status"
+)
+
+func TestStream_InvalidHeader(t *testing.T) {
+ t.Parallel()
+
+ ts := newTestState(t)
+
+ initString := codeBlock{
+ code: `
+ var client = new grpc.Client();
+ client.load([], "../../../../lib/testutils/httpmultibin/grpc_testing/test.proto");`,
+ }
+
+ val, err := ts.Run(initString.code)
+ assertResponse(t, initString, err, val, ts)
+
+ ts.ToVUContext()
+
+ _, err = ts.Run(`
+ client.connect("GRPCBIN_ADDR");
+ new grpc.Stream(client, "foo/bar")`)
+
+ assert.Error(t, err)
+ assert.ErrorContains(t, err, `method "/foo/bar" not found in file descriptors`)
+}
+
+func TestStream_RequestHeaders(t *testing.T) {
+ t.Parallel()
+
+ ts := newTestState(t)
+
+ var registeredMetadata metadata.MD
+ stub := &featureExplorerStub{}
+ stub.listFeatures = func(rect *grpcservice.Rectangle, stream grpcservice.FeatureExplorer_ListFeaturesServer) error {
+ // collect metadata from the stream context
+ md, ok := metadata.FromIncomingContext(stream.Context())
+ if ok {
+ registeredMetadata = md
+ }
+
+ return nil
+ }
+
+ grpcservice.RegisterFeatureExplorerServer(ts.httpBin.ServerGRPC, stub)
+
+ initString := codeBlock{
+ code: `
+ var client = new grpc.Client();
+ client.load([], "../../../../lib/testutils/grpcservice/route_guide.proto");`,
+ }
+ vuString := codeBlock{
+ code: `
+ client.connect("GRPCBIN_ADDR");
+ let stream = new grpc.Stream(client, "main.FeatureExplorer/ListFeatures", { metadata: { "X-Load-Tester": "k6" } })
+ stream.write({
+ lo: {
+ latitude: 400000000,
+ longitude: -750000000,
+ },
+ hi: {
+ latitude: 420000000,
+ longitude: -730000000,
+ },
+ });
+ `,
+ }
+
+ val, err := ts.Run(initString.code)
+ assertResponse(t, initString, err, val, ts)
+
+ ts.ToVUContext()
+
+ val, err = ts.RunOnEventLoop(vuString.code)
+
+ assertResponse(t, vuString, err, val, ts)
+
+ // Check that the metadata was registered
+ assert.Len(t, registeredMetadata["x-load-tester"], 1)
+ assert.Equal(t, registeredMetadata["x-load-tester"][0], "k6")
+}
+
+func TestStream_ErrorHandling(t *testing.T) {
+ t.Parallel()
+
+ ts := newTestState(t)
+
+ stub := &featureExplorerStub{}
+
+ savedFeatures := []*grpcservice.Feature{
+ {
+ Name: "foo",
+ Location: &grpcservice.Point{
+ Latitude: 1,
+ Longitude: 2,
+ },
+ },
+ {
+ Name: "bar",
+ Location: &grpcservice.Point{
+ Latitude: 3,
+ Longitude: 4,
+ },
+ },
+ }
+
+ stub.listFeatures = func(rect *grpcservice.Rectangle, stream grpcservice.FeatureExplorer_ListFeaturesServer) error {
+ for _, feature := range savedFeatures {
+ if err := stream.Send(feature); err != nil {
+ return err
+ }
+ }
+
+ return status.Error(codes.Internal, "lorem ipsum")
+ }
+
+ grpcservice.RegisterFeatureExplorerServer(ts.httpBin.ServerGRPC, stub)
+
+ initString := codeBlock{
+ code: `
+ var client = new grpc.Client();
+ client.load([], "../../../../lib/testutils/grpcservice/route_guide.proto");`,
+ }
+ vuString := codeBlock{
+ code: `
+ client.connect("GRPCBIN_ADDR");
+ let stream = new grpc.Stream(client, "main.FeatureExplorer/ListFeatures")
+ stream.write({
+ lo: {
+ latitude: 1,
+ longitude: 2,
+ },
+ hi: {
+ latitude: 1,
+ longitude: 2,
+ },
+ });
+ stream.on('data', function (data) {
+ call('Feature:' + data.name);
+ })
+ stream.on('error', function (e) {
+ call('Code: ' + e.code + ' Message: ' + e.message);
+ });
+ `,
+ }
+
+ val, err := ts.Run(initString.code)
+ assertResponse(t, initString, err, val, ts)
+
+ ts.ToVUContext()
+
+ val, err = ts.RunOnEventLoop(vuString.code)
+
+ assertResponse(t, vuString, err, val, ts)
+
+ assert.Equal(t,
+ []string{
+ "Feature:foo",
+ "Feature:bar",
+ "Code: 13 Message: lorem ipsum",
+ },
+ ts.callRecorder.Recorded(),
+ )
+}
+
+// this test case is checking that everything that server sends
+// after the client finished (client.end called) is delivered to the client
+// and the end event is called
+func TestStream_ReceiveAllServerResponsesAfterEnd(t *testing.T) {
+ t.Parallel()
+
+ ts := newTestState(t)
+
+ stub := &featureExplorerStub{}
+
+ savedFeatures := []*grpcservice.Feature{
+ {
+ Name: "foo",
+ Location: &grpcservice.Point{
+ Latitude: 1,
+ Longitude: 2,
+ },
+ },
+ {
+ Name: "bar",
+ Location: &grpcservice.Point{
+ Latitude: 3,
+ Longitude: 4,
+ },
+ },
+ }
+
+ stub.listFeatures = func(rect *grpcservice.Rectangle, stream grpcservice.FeatureExplorer_ListFeaturesServer) error {
+ for _, feature := range savedFeatures {
+ // adding a delay to make server response "slower"
+ time.Sleep(200 * time.Millisecond)
+
+ if err := stream.Send(feature); err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }
+
+ grpcservice.RegisterFeatureExplorerServer(ts.httpBin.ServerGRPC, stub)
+
+ initString := codeBlock{
+ code: `
+ var client = new grpc.Client();
+ client.load([], "../../../../lib/testutils/grpcservice/route_guide.proto");`,
+ }
+ vuString := codeBlock{
+ code: `
+ client.connect("GRPCBIN_ADDR");
+ let stream = new grpc.Stream(client, "main.FeatureExplorer/ListFeatures")
+ stream.on('data', function (data) {
+ call('Feature:' + data.name);
+ });
+ stream.on('end', function () {
+ call('End called');
+ });
+
+ stream.write({
+ lo: {
+ latitude: 1,
+ longitude: 2,
+ },
+ hi: {
+ latitude: 1,
+ longitude: 2,
+ },
+ });
+ stream.end();
+ `,
+ }
+
+ val, err := ts.Run(initString.code)
+ assertResponse(t, initString, err, val, ts)
+
+ ts.ToVUContext()
+
+ val, err = ts.RunOnEventLoop(vuString.code)
+
+ assertResponse(t, vuString, err, val, ts)
+
+ assert.Equal(t, ts.callRecorder.Recorded(), []string{
+ "Feature:foo",
+ "Feature:bar",
+ "End called",
+ },
+ )
+}
+
+// featureExplorerStub is a stub for FeatureExplorerServer
+// it has ability to override methods
+type featureExplorerStub struct {
+ grpcservice.UnimplementedFeatureExplorerServer
+
+ getFeature func(ctx context.Context, point *grpcservice.Point) (*grpcservice.Feature, error)
+ listFeatures func(rect *grpcservice.Rectangle, stream grpcservice.FeatureExplorer_ListFeaturesServer) error
+}
+
+func (s *featureExplorerStub) GetFeature(ctx context.Context, point *grpcservice.Point) (*grpcservice.Feature, error) {
+ if s.getFeature != nil {
+ return s.getFeature(ctx, point)
+ }
+
+ return nil, status.Errorf(codes.Unimplemented, "method GetFeature not implemented")
+}
+
+func (s *featureExplorerStub) ListFeatures(rect *grpcservice.Rectangle, stream grpcservice.FeatureExplorer_ListFeaturesServer) error {
+ if s.listFeatures != nil {
+ return s.listFeatures(rect, stream)
+ }
+
+ return status.Errorf(codes.Unimplemented, "method ListFeatures not implemented")
+}
+
+func TestStream_Wrappers(t *testing.T) {
+ t.Parallel()
+
+ ts := newTestState(t)
+
+ stub := grpc_wrappers_testing.Register(ts.httpBin.ServerGRPC)
+ stub.TestStreamImplementation = func(stream grpc_wrappers_testing.Service_TestStreamServer) error {
+ result := ""
+
+ for {
+ msg, err := stream.Recv()
+ if errors.Is(err, io.EOF) {
+ return stream.SendAndClose(&wrappers.StringValue{
+ Value: strings.TrimRight(result, " "),
+ })
+ }
+
+ if err != nil {
+ return err
+ }
+
+ result += msg.Value + " "
+ }
+ }
+
+ replace := func(code string) (goja.Value, error) {
+ return ts.VU.Runtime().RunString(ts.httpBin.Replacer.Replace(code))
+ }
+
+ initString := codeBlock{
+ code: `
+ var client = new grpc.Client();
+ client.load([], "../../../../lib/testutils/httpmultibin/grpc_wrappers_testing/test.proto");`,
+ }
+ vuString := codeBlock{
+ code: `
+ client.connect("GRPCBIN_ADDR");
+ let stream = new grpc.Stream(client, "grpc.wrappers.testing.Service/TestStream");
+ stream.on('data', function (data) {
+ call('Result: ' + data);
+ })
+
+ stream.write('Hey');
+ stream.write('John');
+ stream.end();
+
+ stream.on('error', function (e) {
+ call('Code: ' + e.code + ' Message: ' + e.message);
+ });
+ `,
+ }
+
+ val, err := replace(initString.code)
+ assertResponse(t, initString, err, val, ts)
+
+ ts.ToVUContext()
+
+ val, err = replace(vuString.code)
+
+ ts.EventLoop.WaitOnRegistered()
+
+ assertResponse(t, vuString, err, val, ts)
+
+ assert.Equal(t, ts.callRecorder.Recorded(), []string{
+ "Result: Hey John",
+ },
+ )
+}
diff --git a/js/modules/k6/grpc/teststate_test.go b/js/modules/k6/grpc/teststate_test.go
index b4af7a98821d..0305e723b022 100644
--- a/js/modules/k6/grpc/teststate_test.go
+++ b/js/modules/k6/grpc/teststate_test.go
@@ -1,28 +1,25 @@
package grpc_test
import (
- "errors"
"io"
"net/url"
"os"
"runtime"
- "strings"
+ "sync"
"testing"
"github.com/dop251/goja"
"github.com/sirupsen/logrus"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
-
- "gopkg.in/guregu/null.v3"
-
- k6grpc "go.k6.io/k6/js/modules/k6/grpc"
"go.k6.io/k6/js/modulestest"
"go.k6.io/k6/lib"
"go.k6.io/k6/lib/fsext"
"go.k6.io/k6/lib/testutils"
"go.k6.io/k6/lib/testutils/httpmultibin"
"go.k6.io/k6/metrics"
+ "gopkg.in/guregu/null.v3"
+
+ xk6grpc "go.k6.io/k6/js/modules/k6/grpc"
)
const isWindows = runtime.GOOS == "windows"
@@ -43,24 +40,71 @@ type testcase struct {
vuString codeBlock // runs in the vu context
}
+// callRecorder a helper type that records all calls
+type callRecorder struct {
+ sync.Mutex
+ calls []string
+}
+
+// Call records a call
+func (r *callRecorder) Call(text string) {
+ r.Lock()
+ defer r.Unlock()
+
+ r.calls = append(r.calls, text)
+}
+
+// Len just returns the length of the calls
+func (r *callRecorder) Len() int {
+ r.Lock()
+ defer r.Unlock()
+
+ return len(r.calls)
+}
+
+// Recorded returns the recorded calls
+func (r *callRecorder) Recorded() []string {
+ r.Lock()
+ defer r.Unlock()
+
+ result := []string{}
+ result = append(result, r.calls...)
+
+ return result
+}
+
type testState struct {
*modulestest.Runtime
- httpBin *httpmultibin.HTTPMultiBin
- samples chan metrics.SampleContainer
- logger logrus.FieldLogger
- loggerHook *testutils.SimpleLogrusHook
+ httpBin *httpmultibin.HTTPMultiBin
+ samples chan metrics.SampleContainer
+ logger logrus.FieldLogger
+ loggerHook *testutils.SimpleLogrusHook
+ callRecorder *callRecorder
+}
+
+// Run replaces the httpbin address and runs the code.
+func (ts *testState) Run(code string) (goja.Value, error) {
+ return ts.VU.Runtime().RunString(ts.httpBin.Replacer.Replace(code))
+}
+
+// RunOnEventLoop replaces the httpbin address and run the code on event loop
+func (ts *testState) RunOnEventLoop(code string) (goja.Value, error) {
+ return ts.Runtime.RunOnEventLoop(ts.httpBin.Replacer.Replace(code))
}
+// newTestState creates a new test state.
func newTestState(t *testing.T) testState {
t.Helper()
tb := httpmultibin.NewHTTPMultiBin(t)
+
samples := make(chan metrics.SampleContainer, 1000)
testRuntime := modulestest.NewRuntime(t)
- cwd, err := os.Getwd() //nolint:golint,forbidigo
+ cwd, err := os.Getwd() //nolint:forbidigo
require.NoError(t, err)
fs := fsext.NewOsFs()
+
if isWindows {
fs = fsext.NewTrimFilePathSeparatorFs(fs)
}
@@ -74,17 +118,23 @@ func newTestState(t *testing.T) testState {
hook := testutils.NewLogHook()
logger.AddHook(hook)
+ recorder := &callRecorder{
+ calls: make([]string, 0),
+ }
+
ts := testState{
- Runtime: testRuntime,
- httpBin: tb,
- samples: samples,
- logger: logger,
- loggerHook: hook,
+ Runtime: testRuntime,
+ httpBin: tb,
+ samples: samples,
+ logger: logger,
+ loggerHook: hook,
+ callRecorder: recorder,
}
- m, ok := k6grpc.New().NewModuleInstance(ts.VU).(*k6grpc.ModuleInstance)
+ m, ok := xk6grpc.New().NewModuleInstance(ts.VU).(*xk6grpc.ModuleInstance)
require.True(t, ok)
require.NoError(t, ts.VU.Runtime().Set("grpc", m.Exports().Named))
+ require.NoError(t, ts.VU.Runtime().Set("call", recorder.Call))
return ts
}
@@ -116,49 +166,3 @@ func (ts *testState) ToVUContext() {
ts.MoveToVUContext(state)
}
-
-// Run replaces the httpbin address and runs the code.
-func (ts *testState) Run(code string) (goja.Value, error) {
- return ts.VU.Runtime().RunString(ts.httpBin.Replacer.Replace(code))
-}
-
-func assertMetricEmitted(
- t *testing.T,
- metricName string, //nolint:unparam
- sampleContainers []metrics.SampleContainer,
- url string,
-) {
- seenMetric := false
-
- for _, sampleContainer := range sampleContainers {
- for _, sample := range sampleContainer.GetSamples() {
- surl, ok := sample.Tags.Get("url")
- assert.True(t, ok)
- if surl == url {
- if sample.Metric.Name == metricName {
- seenMetric = true
- }
- }
- }
- }
- assert.True(t, seenMetric, "url %s didn't emit %s", url, metricName)
-}
-
-func assertResponse(t *testing.T, cb codeBlock, err error, val goja.Value, ts testState) {
- if isWindows && cb.windowsErr != "" && err != nil {
- err = errors.New(strings.ReplaceAll(err.Error(), cb.windowsErr, cb.err))
- }
- if cb.err == "" {
- assert.NoError(t, err)
- } else {
- require.Error(t, err)
- assert.Contains(t, err.Error(), cb.err)
- }
- if cb.val != nil {
- require.NotNil(t, val)
- assert.Equal(t, cb.val, val.Export())
- }
- if cb.asserts != nil {
- cb.asserts(t, ts.httpBin, ts.samples, err)
- }
-}
diff --git a/lib/netext/grpcext/conn.go b/lib/netext/grpcext/conn.go
index 2709f90e2411..5e9f9e0fc0b9 100644
--- a/lib/netext/grpcext/conn.go
+++ b/lib/netext/grpcext/conn.go
@@ -9,11 +9,11 @@ import (
"strconv"
"strings"
+ "github.com/sirupsen/logrus"
"go.k6.io/k6/lib"
"go.k6.io/k6/metrics"
protov1 "github.com/golang/protobuf/proto" //nolint:staticcheck,nolintlint // this is the old v1 version
- "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
@@ -34,6 +34,14 @@ type Request struct {
Message []byte
}
+// StreamRequest represents a gRPC stream request.
+type StreamRequest struct {
+ Method string
+ MethodDescriptor protoreflect.MethodDescriptor
+ TagsAndMeta *metrics.TagsAndMeta
+ Metadata metadata.MD
+}
+
// Response represents a gRPC response.
type Response struct {
Message interface{}
@@ -146,28 +154,42 @@ func (c *Conn) Invoke(
}
if resp != nil {
- // (rogchap) there is a lot of marshaling/unmarshaling here, but if we just pass the dynamic message
- // the default Marshaller would be used, which would strip any zero/default values from the JSON.
- // eg. given this message:
- // message Point {
- // double x = 1;
- // double y = 2;
- // double z = 3;
- // }
- // and a value like this:
- // msg := Point{X: 6, Y: 4, Z: 0}
- // would result in JSON output:
- // {"x":6,"y":4}
- // rather than the desired:
- // {"x":6,"y":4,"z":0}
- raw, _ := marshaler.Marshal(resp)
- var msg interface{}
- _ = json.Unmarshal(raw, &msg)
+ msg, err := convert(marshaler, resp)
+ if err != nil {
+ return nil, fmt.Errorf("unable to convert response object to JSON: %w", err)
+ }
+
response.Message = msg
}
return &response, nil
}
+// NewStream creates a new gRPC stream.
+func (c *Conn) NewStream(
+ ctx context.Context,
+ req StreamRequest,
+ opts ...grpc.CallOption,
+) (*Stream, error) {
+ ctx = metadata.NewOutgoingContext(ctx, req.Metadata)
+
+ ctx = withRPCState(ctx, &rpcState{tagsAndMeta: req.TagsAndMeta})
+
+ stream, err := c.raw.NewStream(ctx, &grpc.StreamDesc{
+ StreamName: string(req.MethodDescriptor.Name()),
+ ServerStreams: req.MethodDescriptor.IsStreamingServer(),
+ ClientStreams: req.MethodDescriptor.IsStreamingClient(),
+ }, req.Method, opts...)
+ if err != nil {
+ return nil, err
+ }
+
+ return &Stream{
+ raw: stream,
+ method: req.Method,
+ methodDescriptor: req.MethodDescriptor,
+ }, nil
+}
+
// Close closes the underhood connection.
func (c *Conn) Close() error {
return c.raw.Close()
diff --git a/lib/netext/grpcext/conn_test.go b/lib/netext/grpcext/conn_test.go
index 8a2a66243f32..fe418d79a34a 100644
--- a/lib/netext/grpcext/conn_test.go
+++ b/lib/netext/grpcext/conn_test.go
@@ -213,7 +213,7 @@ message Empty {
// invokemock is a mock for the grpc connection supporting only unary requests.
type invokemock func(in, out *dynamicpb.Message, opts ...grpc.CallOption) error
-func (im invokemock) Invoke(ctx context.Context, url string, payload interface{}, reply interface{}, opts ...grpc.CallOption) error {
+func (im invokemock) Invoke(_ context.Context, _ string, payload interface{}, reply interface{}, opts ...grpc.CallOption) error {
in, ok := payload.(*dynamicpb.Message)
if !ok {
return fmt.Errorf("unexpected type for payload")
@@ -225,12 +225,10 @@ func (im invokemock) Invoke(ctx context.Context, url string, payload interface{}
return im(in, out, opts...)
}
-func (invokemock) NewStream(ctx context.Context, desc *grpc.StreamDesc, method string, opts ...grpc.CallOption) (grpc.ClientStream, error) {
+func (invokemock) NewStream(_ context.Context, _ *grpc.StreamDesc, _ string, _ ...grpc.CallOption) (grpc.ClientStream, error) {
panic("not implemented")
}
func (invokemock) Close() error {
return nil
}
-
-// TODO: add a test for the ip metric tag
diff --git a/vendor/github.com/grafana/xk6-grpc/lib/netext/grpcext/stream.go b/lib/netext/grpcext/stream.go
similarity index 100%
rename from vendor/github.com/grafana/xk6-grpc/lib/netext/grpcext/stream.go
rename to lib/netext/grpcext/stream.go
diff --git a/lib/testutils/grpcservice/route_guide.pb.go b/lib/testutils/grpcservice/route_guide.pb.go
new file mode 100644
index 000000000000..bb5837f6de4a
--- /dev/null
+++ b/lib/testutils/grpcservice/route_guide.pb.go
@@ -0,0 +1,529 @@
+// Copyright 2015 gRPC authors, with some modification by the k6 team (2021).
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.26.0
+// protoc v3.12.4
+// source: route_guide.proto
+
+package grpcservice
+
+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)
+)
+
+// Points are represented as latitude-longitude pairs in the E7 representation
+// (degrees multiplied by 10**7 and rounded to the nearest integer).
+// Latitudes should be in the range +/- 90 degrees and longitude should be in
+// the range +/- 180 degrees (inclusive).
+type Point struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Latitude int32 `protobuf:"varint,1,opt,name=latitude,proto3" json:"latitude,omitempty"`
+ Longitude int32 `protobuf:"varint,2,opt,name=longitude,proto3" json:"longitude,omitempty"`
+}
+
+func (x *Point) Reset() {
+ *x = Point{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_route_guide_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Point) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Point) ProtoMessage() {}
+
+func (x *Point) ProtoReflect() protoreflect.Message {
+ mi := &file_route_guide_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 Point.ProtoReflect.Descriptor instead.
+func (*Point) Descriptor() ([]byte, []int) {
+ return file_route_guide_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Point) GetLatitude() int32 {
+ if x != nil {
+ return x.Latitude
+ }
+ return 0
+}
+
+func (x *Point) GetLongitude() int32 {
+ if x != nil {
+ return x.Longitude
+ }
+ return 0
+}
+
+// A latitude-longitude rectangle, represented as two diagonally opposite
+// points "lo" and "hi".
+type Rectangle struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // One corner of the rectangle.
+ Lo *Point `protobuf:"bytes,1,opt,name=lo,proto3" json:"lo,omitempty"`
+ // The other corner of the rectangle.
+ Hi *Point `protobuf:"bytes,2,opt,name=hi,proto3" json:"hi,omitempty"`
+}
+
+func (x *Rectangle) Reset() {
+ *x = Rectangle{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_route_guide_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Rectangle) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Rectangle) ProtoMessage() {}
+
+func (x *Rectangle) ProtoReflect() protoreflect.Message {
+ mi := &file_route_guide_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 Rectangle.ProtoReflect.Descriptor instead.
+func (*Rectangle) Descriptor() ([]byte, []int) {
+ return file_route_guide_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *Rectangle) GetLo() *Point {
+ if x != nil {
+ return x.Lo
+ }
+ return nil
+}
+
+func (x *Rectangle) GetHi() *Point {
+ if x != nil {
+ return x.Hi
+ }
+ return nil
+}
+
+// A feature names something at a given point.
+//
+// If a feature could not be named, the name is empty.
+type Feature struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The name of the feature.
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ // The point where the feature is detected.
+ Location *Point `protobuf:"bytes,2,opt,name=location,proto3" json:"location,omitempty"`
+}
+
+func (x *Feature) Reset() {
+ *x = Feature{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_route_guide_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *Feature) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Feature) ProtoMessage() {}
+
+func (x *Feature) ProtoReflect() protoreflect.Message {
+ mi := &file_route_guide_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 Feature.ProtoReflect.Descriptor instead.
+func (*Feature) Descriptor() ([]byte, []int) {
+ return file_route_guide_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *Feature) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *Feature) GetLocation() *Point {
+ if x != nil {
+ return x.Location
+ }
+ return nil
+}
+
+// A RouteNote is a message sent while at a given point.
+type RouteNote struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The location from which the message is sent.
+ Location *Point `protobuf:"bytes,1,opt,name=location,proto3" json:"location,omitempty"`
+ // The message to be sent.
+ Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
+}
+
+func (x *RouteNote) Reset() {
+ *x = RouteNote{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_route_guide_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *RouteNote) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RouteNote) ProtoMessage() {}
+
+func (x *RouteNote) ProtoReflect() protoreflect.Message {
+ mi := &file_route_guide_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 RouteNote.ProtoReflect.Descriptor instead.
+func (*RouteNote) Descriptor() ([]byte, []int) {
+ return file_route_guide_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *RouteNote) GetLocation() *Point {
+ if x != nil {
+ return x.Location
+ }
+ return nil
+}
+
+func (x *RouteNote) GetMessage() string {
+ if x != nil {
+ return x.Message
+ }
+ return ""
+}
+
+// A RouteSummary is received in response to a RecordRoute rpc.
+//
+// It contains the number of individual points received, the number of
+// detected features, and the total distance covered as the cumulative sum of
+// the distance between each point.
+type RouteSummary struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The number of points received.
+ PointCount int32 `protobuf:"varint,1,opt,name=point_count,json=pointCount,proto3" json:"point_count,omitempty"`
+ // The number of known features passed while traversing the route.
+ FeatureCount int32 `protobuf:"varint,2,opt,name=feature_count,json=featureCount,proto3" json:"feature_count,omitempty"`
+ // The distance covered in metres.
+ Distance int32 `protobuf:"varint,3,opt,name=distance,proto3" json:"distance,omitempty"`
+ // The duration of the traversal in seconds.
+ ElapsedTime int32 `protobuf:"varint,4,opt,name=elapsed_time,json=elapsedTime,proto3" json:"elapsed_time,omitempty"`
+}
+
+func (x *RouteSummary) Reset() {
+ *x = RouteSummary{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_route_guide_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *RouteSummary) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RouteSummary) ProtoMessage() {}
+
+func (x *RouteSummary) ProtoReflect() protoreflect.Message {
+ mi := &file_route_guide_proto_msgTypes[4]
+ 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 RouteSummary.ProtoReflect.Descriptor instead.
+func (*RouteSummary) Descriptor() ([]byte, []int) {
+ return file_route_guide_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *RouteSummary) GetPointCount() int32 {
+ if x != nil {
+ return x.PointCount
+ }
+ return 0
+}
+
+func (x *RouteSummary) GetFeatureCount() int32 {
+ if x != nil {
+ return x.FeatureCount
+ }
+ return 0
+}
+
+func (x *RouteSummary) GetDistance() int32 {
+ if x != nil {
+ return x.Distance
+ }
+ return 0
+}
+
+func (x *RouteSummary) GetElapsedTime() int32 {
+ if x != nil {
+ return x.ElapsedTime
+ }
+ return 0
+}
+
+var File_route_guide_proto protoreflect.FileDescriptor
+
+var file_route_guide_proto_rawDesc = []byte{
+ 0x0a, 0x11, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x5f, 0x67, 0x75, 0x69, 0x64, 0x65, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x22, 0x41, 0x0a, 0x05, 0x50, 0x6f, 0x69,
+ 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x12, 0x1c,
+ 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x05, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x22, 0x45, 0x0a, 0x09,
+ 0x52, 0x65, 0x63, 0x74, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x12, 0x1b, 0x0a, 0x02, 0x6c, 0x6f, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x50, 0x6f, 0x69,
+ 0x6e, 0x74, 0x52, 0x02, 0x6c, 0x6f, 0x12, 0x1b, 0x0a, 0x02, 0x68, 0x69, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52,
+ 0x02, 0x68, 0x69, 0x22, 0x46, 0x0a, 0x07, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x12,
+ 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x50, 0x6f, 0x69, 0x6e,
+ 0x74, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x4e, 0x0a, 0x09, 0x52,
+ 0x6f, 0x75, 0x74, 0x65, 0x4e, 0x6f, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6d, 0x61, 0x69,
+ 0x6e, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x93, 0x01, 0x0a, 0x0c,
+ 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b,
+ 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x05, 0x52, 0x0a, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x23, 0x0a,
+ 0x0d, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x75,
+ 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x03,
+ 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x21,
+ 0x0a, 0x0c, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04,
+ 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69, 0x6d,
+ 0x65, 0x32, 0x71, 0x0a, 0x0f, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x45, 0x78, 0x70, 0x6c,
+ 0x6f, 0x72, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75,
+ 0x72, 0x65, 0x12, 0x0b, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x1a,
+ 0x0d, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x00,
+ 0x12, 0x32, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73,
+ 0x12, 0x0f, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x52, 0x65, 0x63, 0x74, 0x61, 0x6e, 0x67, 0x6c,
+ 0x65, 0x1a, 0x0d, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65,
+ 0x22, 0x00, 0x30, 0x01, 0x32, 0x75, 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x47, 0x75, 0x69,
+ 0x64, 0x65, 0x12, 0x32, 0x0a, 0x0b, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x52, 0x6f, 0x75, 0x74,
+ 0x65, 0x12, 0x0b, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x1a, 0x12,
+ 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x53, 0x75, 0x6d, 0x6d, 0x61,
+ 0x72, 0x79, 0x22, 0x00, 0x28, 0x01, 0x12, 0x33, 0x0a, 0x09, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x43,
+ 0x68, 0x61, 0x74, 0x12, 0x0f, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65,
+ 0x4e, 0x6f, 0x74, 0x65, 0x1a, 0x0f, 0x2e, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x52, 0x6f, 0x75, 0x74,
+ 0x65, 0x4e, 0x6f, 0x74, 0x65, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x0f, 0x5a, 0x0d, 0x2e,
+ 0x2f, 0x67, 0x72, 0x70, 0x63, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_route_guide_proto_rawDescOnce sync.Once
+ file_route_guide_proto_rawDescData = file_route_guide_proto_rawDesc
+)
+
+func file_route_guide_proto_rawDescGZIP() []byte {
+ file_route_guide_proto_rawDescOnce.Do(func() {
+ file_route_guide_proto_rawDescData = protoimpl.X.CompressGZIP(file_route_guide_proto_rawDescData)
+ })
+ return file_route_guide_proto_rawDescData
+}
+
+var file_route_guide_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_route_guide_proto_goTypes = []interface{}{
+ (*Point)(nil), // 0: main.Point
+ (*Rectangle)(nil), // 1: main.Rectangle
+ (*Feature)(nil), // 2: main.Feature
+ (*RouteNote)(nil), // 3: main.RouteNote
+ (*RouteSummary)(nil), // 4: main.RouteSummary
+}
+var file_route_guide_proto_depIdxs = []int32{
+ 0, // 0: main.Rectangle.lo:type_name -> main.Point
+ 0, // 1: main.Rectangle.hi:type_name -> main.Point
+ 0, // 2: main.Feature.location:type_name -> main.Point
+ 0, // 3: main.RouteNote.location:type_name -> main.Point
+ 0, // 4: main.FeatureExplorer.GetFeature:input_type -> main.Point
+ 1, // 5: main.FeatureExplorer.ListFeatures:input_type -> main.Rectangle
+ 0, // 6: main.RouteGuide.RecordRoute:input_type -> main.Point
+ 3, // 7: main.RouteGuide.RouteChat:input_type -> main.RouteNote
+ 2, // 8: main.FeatureExplorer.GetFeature:output_type -> main.Feature
+ 2, // 9: main.FeatureExplorer.ListFeatures:output_type -> main.Feature
+ 4, // 10: main.RouteGuide.RecordRoute:output_type -> main.RouteSummary
+ 3, // 11: main.RouteGuide.RouteChat:output_type -> main.RouteNote
+ 8, // [8:12] is the sub-list for method output_type
+ 4, // [4:8] is the sub-list for method input_type
+ 4, // [4:4] is the sub-list for extension type_name
+ 4, // [4:4] is the sub-list for extension extendee
+ 0, // [0:4] is the sub-list for field type_name
+}
+
+func init() { file_route_guide_proto_init() }
+func file_route_guide_proto_init() {
+ if File_route_guide_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_route_guide_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Point); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_route_guide_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Rectangle); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_route_guide_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*Feature); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_route_guide_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*RouteNote); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_route_guide_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*RouteSummary); 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_route_guide_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 5,
+ NumExtensions: 0,
+ NumServices: 2,
+ },
+ GoTypes: file_route_guide_proto_goTypes,
+ DependencyIndexes: file_route_guide_proto_depIdxs,
+ MessageInfos: file_route_guide_proto_msgTypes,
+ }.Build()
+ File_route_guide_proto = out.File
+ file_route_guide_proto_rawDesc = nil
+ file_route_guide_proto_goTypes = nil
+ file_route_guide_proto_depIdxs = nil
+}
diff --git a/lib/testutils/grpcservice/route_guide.proto b/lib/testutils/grpcservice/route_guide.proto
new file mode 100644
index 000000000000..1742d46759c4
--- /dev/null
+++ b/lib/testutils/grpcservice/route_guide.proto
@@ -0,0 +1,113 @@
+// Copyright 2015 gRPC authors, with some modification by the k6 team (2021).
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+syntax = "proto3";
+
+package main;
+
+option go_package = "./grpcservice";
+
+// FeatureExplorer is the service for exploring Features.
+// Interface exported by the server.
+service FeatureExplorer {
+ // A simple RPC.
+ //
+ // Obtains the feature at a given position.
+ //
+ // A feature with an empty name is returned if there's no feature at the given
+ // position.
+ rpc GetFeature(Point) returns (Feature) {}
+
+ // A server-to-client streaming RPC.
+ //
+ // Obtains the Features available within the given Rectangle. Results are
+ // streamed rather than returned at once (e.g. in a response message with a
+ // repeated field), as the rectangle may cover a large area and contain a
+ // huge number of features.
+ rpc ListFeatures(Rectangle) returns (stream Feature) {}
+}
+
+// Interface exported by the server.
+service RouteGuide {
+
+ // A client-to-server streaming RPC.
+ //
+ // Accepts a stream of Points on a route being traversed, returning a
+ // RouteSummary when traversal is completed.
+ rpc RecordRoute(stream Point) returns (RouteSummary) {}
+
+ // A Bidirectional streaming RPC.
+ //
+ // Accepts a stream of RouteNotes sent while a route is being traversed,
+ // while receiving other RouteNotes (e.g. from other users).
+ rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
+}
+
+// Points are represented as latitude-longitude pairs in the E7 representation
+// (degrees multiplied by 10**7 and rounded to the nearest integer).
+// Latitudes should be in the range +/- 90 degrees and longitude should be in
+// the range +/- 180 degrees (inclusive).
+message Point {
+ int32 latitude = 1;
+ int32 longitude = 2;
+}
+
+// A latitude-longitude rectangle, represented as two diagonally opposite
+// points "lo" and "hi".
+message Rectangle {
+ // One corner of the rectangle.
+ Point lo = 1;
+
+ // The other corner of the rectangle.
+ Point hi = 2;
+}
+
+// A feature names something at a given point.
+//
+// If a feature could not be named, the name is empty.
+message Feature {
+ // The name of the feature.
+ string name = 1;
+
+ // The point where the feature is detected.
+ Point location = 2;
+}
+
+// A RouteNote is a message sent while at a given point.
+message RouteNote {
+ // The location from which the message is sent.
+ Point location = 1;
+
+ // The message to be sent.
+ string message = 2;
+}
+
+// A RouteSummary is received in response to a RecordRoute rpc.
+//
+// It contains the number of individual points received, the number of
+// detected features, and the total distance covered as the cumulative sum of
+// the distance between each point.
+message RouteSummary {
+ // The number of points received.
+ int32 point_count = 1;
+
+ // The number of known features passed while traversing the route.
+ int32 feature_count = 2;
+
+ // The distance covered in metres.
+ int32 distance = 3;
+
+ // The duration of the traversal in seconds.
+ int32 elapsed_time = 4;
+}
diff --git a/lib/testutils/grpcservice/route_guide_grpc.pb.go b/lib/testutils/grpcservice/route_guide_grpc.pb.go
new file mode 100644
index 000000000000..2552b6adbb45
--- /dev/null
+++ b/lib/testutils/grpcservice/route_guide_grpc.pb.go
@@ -0,0 +1,393 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+
+package grpcservice
+
+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
+
+// FeatureExplorerClient is the client API for FeatureExplorer 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 FeatureExplorerClient interface {
+ // A simple RPC.
+ //
+ // Obtains the feature at a given position.
+ //
+ // A feature with an empty name is returned if there's no feature at the given
+ // position.
+ GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error)
+ // A server-to-client streaming RPC.
+ //
+ // Obtains the Features available within the given Rectangle. Results are
+ // streamed rather than returned at once (e.g. in a response message with a
+ // repeated field), as the rectangle may cover a large area and contain a
+ // huge number of features.
+ ListFeatures(ctx context.Context, in *Rectangle, opts ...grpc.CallOption) (FeatureExplorer_ListFeaturesClient, error)
+}
+
+type featureExplorerClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewFeatureExplorerClient(cc grpc.ClientConnInterface) FeatureExplorerClient {
+ return &featureExplorerClient{cc}
+}
+
+func (c *featureExplorerClient) GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error) {
+ out := new(Feature)
+ err := c.cc.Invoke(ctx, "/main.FeatureExplorer/GetFeature", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *featureExplorerClient) ListFeatures(ctx context.Context, in *Rectangle, opts ...grpc.CallOption) (FeatureExplorer_ListFeaturesClient, error) {
+ stream, err := c.cc.NewStream(ctx, &FeatureExplorer_ServiceDesc.Streams[0], "/main.FeatureExplorer/ListFeatures", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &featureExplorerListFeaturesClient{stream}
+ if err := x.ClientStream.SendMsg(in); err != nil {
+ return nil, err
+ }
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ return x, nil
+}
+
+type FeatureExplorer_ListFeaturesClient interface {
+ Recv() (*Feature, error)
+ grpc.ClientStream
+}
+
+type featureExplorerListFeaturesClient struct {
+ grpc.ClientStream
+}
+
+func (x *featureExplorerListFeaturesClient) Recv() (*Feature, error) {
+ m := new(Feature)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// FeatureExplorerServer is the server API for FeatureExplorer service.
+// All implementations must embed UnimplementedFeatureExplorerServer
+// for forward compatibility
+type FeatureExplorerServer interface {
+ // A simple RPC.
+ //
+ // Obtains the feature at a given position.
+ //
+ // A feature with an empty name is returned if there's no feature at the given
+ // position.
+ GetFeature(context.Context, *Point) (*Feature, error)
+ // A server-to-client streaming RPC.
+ //
+ // Obtains the Features available within the given Rectangle. Results are
+ // streamed rather than returned at once (e.g. in a response message with a
+ // repeated field), as the rectangle may cover a large area and contain a
+ // huge number of features.
+ ListFeatures(*Rectangle, FeatureExplorer_ListFeaturesServer) error
+ mustEmbedUnimplementedFeatureExplorerServer()
+}
+
+// UnimplementedFeatureExplorerServer must be embedded to have forward compatible implementations.
+type UnimplementedFeatureExplorerServer struct {
+}
+
+func (UnimplementedFeatureExplorerServer) GetFeature(context.Context, *Point) (*Feature, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method GetFeature not implemented")
+}
+func (UnimplementedFeatureExplorerServer) ListFeatures(*Rectangle, FeatureExplorer_ListFeaturesServer) error {
+ return status.Errorf(codes.Unimplemented, "method ListFeatures not implemented")
+}
+func (UnimplementedFeatureExplorerServer) mustEmbedUnimplementedFeatureExplorerServer() {}
+
+// UnsafeFeatureExplorerServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to FeatureExplorerServer will
+// result in compilation errors.
+type UnsafeFeatureExplorerServer interface {
+ mustEmbedUnimplementedFeatureExplorerServer()
+}
+
+func RegisterFeatureExplorerServer(s grpc.ServiceRegistrar, srv FeatureExplorerServer) {
+ s.RegisterService(&FeatureExplorer_ServiceDesc, srv)
+}
+
+func _FeatureExplorer_GetFeature_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(Point)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(FeatureExplorerServer).GetFeature(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/main.FeatureExplorer/GetFeature",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(FeatureExplorerServer).GetFeature(ctx, req.(*Point))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _FeatureExplorer_ListFeatures_Handler(srv interface{}, stream grpc.ServerStream) error {
+ m := new(Rectangle)
+ if err := stream.RecvMsg(m); err != nil {
+ return err
+ }
+ return srv.(FeatureExplorerServer).ListFeatures(m, &featureExplorerListFeaturesServer{stream})
+}
+
+type FeatureExplorer_ListFeaturesServer interface {
+ Send(*Feature) error
+ grpc.ServerStream
+}
+
+type featureExplorerListFeaturesServer struct {
+ grpc.ServerStream
+}
+
+func (x *featureExplorerListFeaturesServer) Send(m *Feature) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+// FeatureExplorer_ServiceDesc is the grpc.ServiceDesc for FeatureExplorer service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var FeatureExplorer_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "main.FeatureExplorer",
+ HandlerType: (*FeatureExplorerServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "GetFeature",
+ Handler: _FeatureExplorer_GetFeature_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "ListFeatures",
+ Handler: _FeatureExplorer_ListFeatures_Handler,
+ ServerStreams: true,
+ },
+ },
+ Metadata: "route_guide.proto",
+}
+
+// RouteGuideClient is the client API for RouteGuide 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 RouteGuideClient interface {
+ // A client-to-server streaming RPC.
+ //
+ // Accepts a stream of Points on a route being traversed, returning a
+ // RouteSummary when traversal is completed.
+ RecordRoute(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RecordRouteClient, error)
+ // A Bidirectional streaming RPC.
+ //
+ // Accepts a stream of RouteNotes sent while a route is being traversed,
+ // while receiving other RouteNotes (e.g. from other users).
+ RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error)
+}
+
+type routeGuideClient struct {
+ cc grpc.ClientConnInterface
+}
+
+func NewRouteGuideClient(cc grpc.ClientConnInterface) RouteGuideClient {
+ return &routeGuideClient{cc}
+}
+
+func (c *routeGuideClient) RecordRoute(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RecordRouteClient, error) {
+ stream, err := c.cc.NewStream(ctx, &RouteGuide_ServiceDesc.Streams[0], "/main.RouteGuide/RecordRoute", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &routeGuideRecordRouteClient{stream}
+ return x, nil
+}
+
+type RouteGuide_RecordRouteClient interface {
+ Send(*Point) error
+ CloseAndRecv() (*RouteSummary, error)
+ grpc.ClientStream
+}
+
+type routeGuideRecordRouteClient struct {
+ grpc.ClientStream
+}
+
+func (x *routeGuideRecordRouteClient) Send(m *Point) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *routeGuideRecordRouteClient) CloseAndRecv() (*RouteSummary, error) {
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ m := new(RouteSummary)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func (c *routeGuideClient) RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error) {
+ stream, err := c.cc.NewStream(ctx, &RouteGuide_ServiceDesc.Streams[1], "/main.RouteGuide/RouteChat", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &routeGuideRouteChatClient{stream}
+ return x, nil
+}
+
+type RouteGuide_RouteChatClient interface {
+ Send(*RouteNote) error
+ Recv() (*RouteNote, error)
+ grpc.ClientStream
+}
+
+type routeGuideRouteChatClient struct {
+ grpc.ClientStream
+}
+
+func (x *routeGuideRouteChatClient) Send(m *RouteNote) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *routeGuideRouteChatClient) Recv() (*RouteNote, error) {
+ m := new(RouteNote)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// RouteGuideServer is the server API for RouteGuide service.
+// All implementations must embed UnimplementedRouteGuideServer
+// for forward compatibility
+type RouteGuideServer interface {
+ // A client-to-server streaming RPC.
+ //
+ // Accepts a stream of Points on a route being traversed, returning a
+ // RouteSummary when traversal is completed.
+ RecordRoute(RouteGuide_RecordRouteServer) error
+ // A Bidirectional streaming RPC.
+ //
+ // Accepts a stream of RouteNotes sent while a route is being traversed,
+ // while receiving other RouteNotes (e.g. from other users).
+ RouteChat(RouteGuide_RouteChatServer) error
+ mustEmbedUnimplementedRouteGuideServer()
+}
+
+// UnimplementedRouteGuideServer must be embedded to have forward compatible implementations.
+type UnimplementedRouteGuideServer struct {
+}
+
+func (UnimplementedRouteGuideServer) RecordRoute(RouteGuide_RecordRouteServer) error {
+ return status.Errorf(codes.Unimplemented, "method RecordRoute not implemented")
+}
+func (UnimplementedRouteGuideServer) RouteChat(RouteGuide_RouteChatServer) error {
+ return status.Errorf(codes.Unimplemented, "method RouteChat not implemented")
+}
+func (UnimplementedRouteGuideServer) mustEmbedUnimplementedRouteGuideServer() {}
+
+// UnsafeRouteGuideServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to RouteGuideServer will
+// result in compilation errors.
+type UnsafeRouteGuideServer interface {
+ mustEmbedUnimplementedRouteGuideServer()
+}
+
+func RegisterRouteGuideServer(s grpc.ServiceRegistrar, srv RouteGuideServer) {
+ s.RegisterService(&RouteGuide_ServiceDesc, srv)
+}
+
+func _RouteGuide_RecordRoute_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(RouteGuideServer).RecordRoute(&routeGuideRecordRouteServer{stream})
+}
+
+type RouteGuide_RecordRouteServer interface {
+ SendAndClose(*RouteSummary) error
+ Recv() (*Point, error)
+ grpc.ServerStream
+}
+
+type routeGuideRecordRouteServer struct {
+ grpc.ServerStream
+}
+
+func (x *routeGuideRecordRouteServer) SendAndClose(m *RouteSummary) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *routeGuideRecordRouteServer) Recv() (*Point, error) {
+ m := new(Point)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+func _RouteGuide_RouteChat_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(RouteGuideServer).RouteChat(&routeGuideRouteChatServer{stream})
+}
+
+type RouteGuide_RouteChatServer interface {
+ Send(*RouteNote) error
+ Recv() (*RouteNote, error)
+ grpc.ServerStream
+}
+
+type routeGuideRouteChatServer struct {
+ grpc.ServerStream
+}
+
+func (x *routeGuideRouteChatServer) Send(m *RouteNote) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *routeGuideRouteChatServer) Recv() (*RouteNote, error) {
+ m := new(RouteNote)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// RouteGuide_ServiceDesc is the grpc.ServiceDesc for RouteGuide service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var RouteGuide_ServiceDesc = grpc.ServiceDesc{
+ ServiceName: "main.RouteGuide",
+ HandlerType: (*RouteGuideServer)(nil),
+ Methods: []grpc.MethodDesc{},
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "RecordRoute",
+ Handler: _RouteGuide_RecordRoute_Handler,
+ ClientStreams: true,
+ },
+ {
+ StreamName: "RouteChat",
+ Handler: _RouteGuide_RouteChat_Handler,
+ ServerStreams: true,
+ ClientStreams: true,
+ },
+ },
+ Metadata: "route_guide.proto",
+}
diff --git a/lib/testutils/grpcservice/service.go b/lib/testutils/grpcservice/service.go
new file mode 100644
index 000000000000..267c02f2c275
--- /dev/null
+++ b/lib/testutils/grpcservice/service.go
@@ -0,0 +1,834 @@
+// Package grpcservice contains the implementation of the test GRPC service.
+package grpcservice
+
+import (
+ context "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "log"
+ "math"
+ "math/rand"
+ "os"
+ sync "sync"
+ "time"
+
+ "google.golang.org/protobuf/proto"
+)
+
+// It generates the Go code for protobuf and the gRPC server used by this implementation.
+// Check the following links for getting more details about protoc generation:
+// * https://grpc.io/docs/protoc-installation
+// * https://grpc.io/docs/languages/go/quickstart/
+//
+//
+//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative route_guide.proto
+
+// FeatureExplorerImplementation contains an implementation of the FeatureExplorer service.
+type FeatureExplorerImplementation struct {
+ Logf func(format string, v ...any)
+ UnimplementedFeatureExplorerServer
+ savedFeatures []*Feature // read-only after initialized
+}
+
+// NewFeatureExplorerServer creates a FeatureExplorer server.
+func NewFeatureExplorerServer(features ...*Feature) *FeatureExplorerImplementation {
+ return &FeatureExplorerImplementation{savedFeatures: features, Logf: log.Printf}
+}
+
+// GetFeature returns the feature at the given point.
+func (s *FeatureExplorerImplementation) GetFeature(_ context.Context, point *Point) (*Feature, error) {
+ s.Logf("GetFeature called with: %+v\n", point)
+
+ n := rand.Intn(1000) //nolint:gosec
+ time.Sleep(time.Duration(n) * time.Millisecond)
+
+ for _, feature := range s.savedFeatures {
+ if proto.Equal(feature.Location, point) {
+ return feature, nil
+ }
+ }
+ // No feature was found, return an unnamed feature
+ return &Feature{Location: point}, nil
+}
+
+// ListFeatures lists all features contained within the given bounding Rectangle.
+func (s *FeatureExplorerImplementation) ListFeatures(rect *Rectangle, stream FeatureExplorer_ListFeaturesServer) error {
+ s.Logf("ListFeatures called with: %+v\n", rect)
+
+ for _, feature := range s.savedFeatures {
+ if inRange(feature.Location, rect) {
+ time.Sleep(100 * time.Millisecond)
+ if err := stream.Send(feature); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// RouteGuideImplementation contains an implementation of the RouteGuide service.
+type RouteGuideImplementation struct {
+ Logf func(format string, v ...any)
+ UnimplementedRouteGuideServer
+ savedFeatures []*Feature // read-only after initialized
+
+ mu sync.Mutex // protects routeNotes
+ routeNotes map[string][]*RouteNote
+}
+
+// NewRouteGuideServer creates a RouteGuide server.
+func NewRouteGuideServer(features ...*Feature) *RouteGuideImplementation {
+ s := &RouteGuideImplementation{
+ savedFeatures: features,
+ routeNotes: make(map[string][]*RouteNote),
+ Logf: log.Printf,
+ }
+ return s
+}
+
+// RecordRoute records a route composited of a sequence of points.
+//
+// It gets a stream of points, and responds with statistics about the "trip":
+// number of points, number of known features visited, total distance traveled, and
+// total time spent.
+func (s *RouteGuideImplementation) RecordRoute(stream RouteGuide_RecordRouteServer) error {
+ s.Logf("RecordRoute called")
+
+ var pointCount, featureCount, distance int32
+ var lastPoint *Point
+ startTime := time.Now()
+ for {
+ point, err := stream.Recv()
+ if errors.Is(err, io.EOF) {
+ endTime := time.Now()
+
+ s.Logf("RecordRoute finished, sending summary")
+
+ errClose := stream.SendAndClose(&RouteSummary{
+ PointCount: pointCount,
+ FeatureCount: featureCount,
+ Distance: distance,
+ ElapsedTime: int32(endTime.Sub(startTime).Seconds()),
+ })
+ if errClose != nil {
+ s.Logf("Close error: %s\n", errClose)
+ }
+
+ return errClose
+ }
+ if err != nil {
+ return err
+ }
+ pointCount++
+ for _, feature := range s.savedFeatures {
+ if proto.Equal(feature.Location, point) {
+ featureCount++
+ }
+ }
+ if lastPoint != nil {
+ distance += calcDistance(lastPoint, point)
+ }
+ lastPoint = point
+ }
+}
+
+// RouteChat receives a stream of message/location pairs, and responds with a stream of all
+// previous messages at each of those locations.
+func (s *RouteGuideImplementation) RouteChat(stream RouteGuide_RouteChatServer) error {
+ s.Logf("RouteChat called")
+
+ for {
+ in, err := stream.Recv()
+ if errors.Is(err, io.EOF) {
+ return nil
+ }
+ if err != nil {
+ return err
+ }
+ key := serialize(in.Location)
+
+ s.mu.Lock()
+ s.routeNotes[key] = append(s.routeNotes[key], in)
+ // Note: this copy prevents blocking other clients while serving this one.
+ // We don't need to do a deep copy, because elements in the slice are
+ // insert-only and never modified.
+ rn := make([]*RouteNote, len(s.routeNotes[key]))
+ copy(rn, s.routeNotes[key])
+ s.mu.Unlock()
+
+ for _, note := range rn {
+ if err := stream.Send(note); err != nil {
+ return err
+ }
+ }
+ }
+}
+
+// LoadFeatures loads features from a JSON file.
+func LoadFeatures(filePath string) []*Feature {
+ var data []byte
+ if filePath != "" {
+ var err error
+ data, err = os.ReadFile(filePath) //nolint:forbidigo,gosec
+ if err != nil {
+ panic(fmt.Sprintf("Failed to load default features: %v", err))
+ }
+ } else {
+ data = []byte(exampleData)
+ }
+ var features []*Feature
+ if err := json.Unmarshal(data, &features); err != nil {
+ panic(fmt.Sprintf("Failed to load default features: %v", err))
+ }
+ return features
+}
+
+func toRadians(num float64) float64 {
+ return num * math.Pi / float64(180)
+}
+
+// calcDistance calculates the distance between two points using the "haversine" formula.
+// The formula is based on http://mathforum.org/library/drmath/view/51879.html.
+func calcDistance(p1 *Point, p2 *Point) int32 {
+ const CordFactor float64 = 1e7
+ const R = float64(6371000) // earth radius in metres
+ lat1 := toRadians(float64(p1.Latitude) / CordFactor)
+ lat2 := toRadians(float64(p2.Latitude) / CordFactor)
+ lng1 := toRadians(float64(p1.Longitude) / CordFactor)
+ lng2 := toRadians(float64(p2.Longitude) / CordFactor)
+ dlat := lat2 - lat1
+ dlng := lng2 - lng1
+
+ a := math.Sin(dlat/2)*math.Sin(dlat/2) +
+ math.Cos(lat1)*math.Cos(lat2)*
+ math.Sin(dlng/2)*math.Sin(dlng/2)
+ c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
+
+ distance := R * c
+ return int32(distance)
+}
+
+func inRange(point *Point, rect *Rectangle) bool {
+ left := math.Min(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
+ right := math.Max(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
+ top := math.Max(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
+ bottom := math.Min(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
+
+ if float64(point.Longitude) >= left &&
+ float64(point.Longitude) <= right &&
+ float64(point.Latitude) >= bottom &&
+ float64(point.Latitude) <= top {
+ return true
+ }
+ return false
+}
+
+func serialize(point *Point) string {
+ return fmt.Sprintf("%d %d", point.Latitude, point.Longitude)
+}
+
+// exampleData is a copy of testdata/route_guide_db.json. It's to avoid
+// specifying file path with `go run`.
+const exampleData = `[{
+ "location": {
+ "latitude": 407838351,
+ "longitude": -746143763
+ },
+ "name": "Patriots Path, Mendham, NJ 07945, USA"
+}, {
+ "location": {
+ "latitude": 408122808,
+ "longitude": -743999179
+ },
+ "name": "101 New Jersey 10, Whippany, NJ 07981, USA"
+}, {
+ "location": {
+ "latitude": 413628156,
+ "longitude": -749015468
+ },
+ "name": "U.S. 6, Shohola, PA 18458, USA"
+}, {
+ "location": {
+ "latitude": 419999544,
+ "longitude": -740371136
+ },
+ "name": "5 Conners Road, Kingston, NY 12401, USA"
+}, {
+ "location": {
+ "latitude": 414008389,
+ "longitude": -743951297
+ },
+ "name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA"
+}, {
+ "location": {
+ "latitude": 419611318,
+ "longitude": -746524769
+ },
+ "name": "287 Flugertown Road, Livingston Manor, NY 12758, USA"
+}, {
+ "location": {
+ "latitude": 406109563,
+ "longitude": -742186778
+ },
+ "name": "4001 Tremley Point Road, Linden, NJ 07036, USA"
+}, {
+ "location": {
+ "latitude": 416802456,
+ "longitude": -742370183
+ },
+ "name": "352 South Mountain Road, Wallkill, NY 12589, USA"
+}, {
+ "location": {
+ "latitude": 412950425,
+ "longitude": -741077389
+ },
+ "name": "Bailey Turn Road, Harriman, NY 10926, USA"
+}, {
+ "location": {
+ "latitude": 412144655,
+ "longitude": -743949739
+ },
+ "name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA"
+}, {
+ "location": {
+ "latitude": 415736605,
+ "longitude": -742847522
+ },
+ "name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA"
+}, {
+ "location": {
+ "latitude": 413843930,
+ "longitude": -740501726
+ },
+ "name": "162 Merrill Road, Highland Mills, NY 10930, USA"
+}, {
+ "location": {
+ "latitude": 410873075,
+ "longitude": -744459023
+ },
+ "name": "Clinton Road, West Milford, NJ 07480, USA"
+}, {
+ "location": {
+ "latitude": 412346009,
+ "longitude": -744026814
+ },
+ "name": "16 Old Brook Lane, Warwick, NY 10990, USA"
+}, {
+ "location": {
+ "latitude": 402948455,
+ "longitude": -747903913
+ },
+ "name": "3 Drake Lane, Pennington, NJ 08534, USA"
+}, {
+ "location": {
+ "latitude": 406337092,
+ "longitude": -740122226
+ },
+ "name": "6324 8th Avenue, Brooklyn, NY 11220, USA"
+}, {
+ "location": {
+ "latitude": 406421967,
+ "longitude": -747727624
+ },
+ "name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA"
+}, {
+ "location": {
+ "latitude": 416318082,
+ "longitude": -749677716
+ },
+ "name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA"
+}, {
+ "location": {
+ "latitude": 415301720,
+ "longitude": -748416257
+ },
+ "name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA"
+}, {
+ "location": {
+ "latitude": 402647019,
+ "longitude": -747071791
+ },
+ "name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA"
+}, {
+ "location": {
+ "latitude": 412567807,
+ "longitude": -741058078
+ },
+ "name": "New York State Reference Route 987E, Southfields, NY 10975, USA"
+}, {
+ "location": {
+ "latitude": 416855156,
+ "longitude": -744420597
+ },
+ "name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA"
+}, {
+ "location": {
+ "latitude": 404663628,
+ "longitude": -744820157
+ },
+ "name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA"
+}, {
+ "location": {
+ "latitude": 407113723,
+ "longitude": -749746483
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 402133926,
+ "longitude": -743613249
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 400273442,
+ "longitude": -741220915
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411236786,
+ "longitude": -744070769
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411633782,
+ "longitude": -746784970
+ },
+ "name": "211-225 Plains Road, Augusta, NJ 07822, USA"
+}, {
+ "location": {
+ "latitude": 415830701,
+ "longitude": -742952812
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 413447164,
+ "longitude": -748712898
+ },
+ "name": "165 Pedersen Ridge Road, Milford, PA 18337, USA"
+}, {
+ "location": {
+ "latitude": 405047245,
+ "longitude": -749800722
+ },
+ "name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA"
+}, {
+ "location": {
+ "latitude": 418858923,
+ "longitude": -746156790
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 417951888,
+ "longitude": -748484944
+ },
+ "name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA"
+}, {
+ "location": {
+ "latitude": 407033786,
+ "longitude": -743977337
+ },
+ "name": "26 East 3rd Street, New Providence, NJ 07974, USA"
+}, {
+ "location": {
+ "latitude": 417548014,
+ "longitude": -740075041
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 410395868,
+ "longitude": -744972325
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404615353,
+ "longitude": -745129803
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 406589790,
+ "longitude": -743560121
+ },
+ "name": "611 Lawrence Avenue, Westfield, NJ 07090, USA"
+}, {
+ "location": {
+ "latitude": 414653148,
+ "longitude": -740477477
+ },
+ "name": "18 Lannis Avenue, New Windsor, NY 12553, USA"
+}, {
+ "location": {
+ "latitude": 405957808,
+ "longitude": -743255336
+ },
+ "name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA"
+}, {
+ "location": {
+ "latitude": 411733589,
+ "longitude": -741648093
+ },
+ "name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA"
+}, {
+ "location": {
+ "latitude": 412676291,
+ "longitude": -742606606
+ },
+ "name": "1270 Lakes Road, Monroe, NY 10950, USA"
+}, {
+ "location": {
+ "latitude": 409224445,
+ "longitude": -748286738
+ },
+ "name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA"
+}, {
+ "location": {
+ "latitude": 406523420,
+ "longitude": -742135517
+ },
+ "name": "652 Garden Street, Elizabeth, NJ 07202, USA"
+}, {
+ "location": {
+ "latitude": 401827388,
+ "longitude": -740294537
+ },
+ "name": "349 Sea Spray Court, Neptune City, NJ 07753, USA"
+}, {
+ "location": {
+ "latitude": 410564152,
+ "longitude": -743685054
+ },
+ "name": "13-17 Stanley Street, West Milford, NJ 07480, USA"
+}, {
+ "location": {
+ "latitude": 408472324,
+ "longitude": -740726046
+ },
+ "name": "47 Industrial Avenue, Teterboro, NJ 07608, USA"
+}, {
+ "location": {
+ "latitude": 412452168,
+ "longitude": -740214052
+ },
+ "name": "5 White Oak Lane, Stony Point, NY 10980, USA"
+}, {
+ "location": {
+ "latitude": 409146138,
+ "longitude": -746188906
+ },
+ "name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA"
+}, {
+ "location": {
+ "latitude": 404701380,
+ "longitude": -744781745
+ },
+ "name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA"
+}, {
+ "location": {
+ "latitude": 409642566,
+ "longitude": -746017679
+ },
+ "name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA"
+}, {
+ "location": {
+ "latitude": 408031728,
+ "longitude": -748645385
+ },
+ "name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA"
+}, {
+ "location": {
+ "latitude": 413700272,
+ "longitude": -742135189
+ },
+ "name": "367 Prospect Road, Chester, NY 10918, USA"
+}, {
+ "location": {
+ "latitude": 404310607,
+ "longitude": -740282632
+ },
+ "name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA"
+}, {
+ "location": {
+ "latitude": 409319800,
+ "longitude": -746201391
+ },
+ "name": "11 Ward Street, Mount Arlington, NJ 07856, USA"
+}, {
+ "location": {
+ "latitude": 406685311,
+ "longitude": -742108603
+ },
+ "name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA"
+}, {
+ "location": {
+ "latitude": 419018117,
+ "longitude": -749142781
+ },
+ "name": "43 Dreher Road, Roscoe, NY 12776, USA"
+}, {
+ "location": {
+ "latitude": 412856162,
+ "longitude": -745148837
+ },
+ "name": "Swan Street, Pine Island, NY 10969, USA"
+}, {
+ "location": {
+ "latitude": 416560744,
+ "longitude": -746721964
+ },
+ "name": "66 Pleasantview Avenue, Monticello, NY 12701, USA"
+}, {
+ "location": {
+ "latitude": 405314270,
+ "longitude": -749836354
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 414219548,
+ "longitude": -743327440
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 415534177,
+ "longitude": -742900616
+ },
+ "name": "565 Winding Hills Road, Montgomery, NY 12549, USA"
+}, {
+ "location": {
+ "latitude": 406898530,
+ "longitude": -749127080
+ },
+ "name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA"
+}, {
+ "location": {
+ "latitude": 407586880,
+ "longitude": -741670168
+ },
+ "name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA"
+}, {
+ "location": {
+ "latitude": 400106455,
+ "longitude": -742870190
+ },
+ "name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA"
+}, {
+ "location": {
+ "latitude": 400066188,
+ "longitude": -746793294
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418803880,
+ "longitude": -744102673
+ },
+ "name": "40 Mountain Road, Napanoch, NY 12458, USA"
+}, {
+ "location": {
+ "latitude": 414204288,
+ "longitude": -747895140
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 414777405,
+ "longitude": -740615601
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 415464475,
+ "longitude": -747175374
+ },
+ "name": "48 North Road, Forestburgh, NY 12777, USA"
+}, {
+ "location": {
+ "latitude": 404062378,
+ "longitude": -746376177
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 405688272,
+ "longitude": -749285130
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 400342070,
+ "longitude": -748788996
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401809022,
+ "longitude": -744157964
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404226644,
+ "longitude": -740517141
+ },
+ "name": "9 Thompson Avenue, Leonardo, NJ 07737, USA"
+}, {
+ "location": {
+ "latitude": 410322033,
+ "longitude": -747871659
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 407100674,
+ "longitude": -747742727
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418811433,
+ "longitude": -741718005
+ },
+ "name": "213 Bush Road, Stone Ridge, NY 12484, USA"
+}, {
+ "location": {
+ "latitude": 415034302,
+ "longitude": -743850945
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411349992,
+ "longitude": -743694161
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404839914,
+ "longitude": -744759616
+ },
+ "name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA"
+}, {
+ "location": {
+ "latitude": 414638017,
+ "longitude": -745957854
+ },
+ "name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA"
+}, {
+ "location": {
+ "latitude": 412127800,
+ "longitude": -740173578
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401263460,
+ "longitude": -747964303
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 412843391,
+ "longitude": -749086026
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 418512773,
+ "longitude": -743067823
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404318328,
+ "longitude": -740835638
+ },
+ "name": "42-102 Main Street, Belford, NJ 07718, USA"
+}, {
+ "location": {
+ "latitude": 419020746,
+ "longitude": -741172328
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404080723,
+ "longitude": -746119569
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 401012643,
+ "longitude": -744035134
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 404306372,
+ "longitude": -741079661
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 403966326,
+ "longitude": -748519297
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 405002031,
+ "longitude": -748407866
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 409532885,
+ "longitude": -742200683
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 416851321,
+ "longitude": -742674555
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 406411633,
+ "longitude": -741722051
+ },
+ "name": "3387 Richmond Terrace, Staten Island, NY 10303, USA"
+}, {
+ "location": {
+ "latitude": 413069058,
+ "longitude": -744597778
+ },
+ "name": "261 Van Sickle Road, Goshen, NY 10924, USA"
+}, {
+ "location": {
+ "latitude": 418465462,
+ "longitude": -746859398
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 411733222,
+ "longitude": -744228360
+ },
+ "name": ""
+}, {
+ "location": {
+ "latitude": 410248224,
+ "longitude": -747127767
+ },
+ "name": "3 Hasta Way, Newton, NJ 07860, USA"
+}]`
diff --git a/lib/testutils/httpmultibin/grpc_wrappers_testing/generate.go b/lib/testutils/httpmultibin/grpc_wrappers_testing/generate.go
deleted file mode 100644
index 4d8e3d9df9dd..000000000000
--- a/lib/testutils/httpmultibin/grpc_wrappers_testing/generate.go
+++ /dev/null
@@ -1,4 +0,0 @@
-// Package grpc_wrappers_testing contains the test service that helps to test gRPC wrappers.
-package grpc_wrappers_testing //nolint:revive,stylecheck
-
-//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative test.proto
diff --git a/lib/testutils/httpmultibin/grpc_wrappers_testing/service.go b/lib/testutils/httpmultibin/grpc_wrappers_testing/service.go
index c6092a545313..92fe2acd29aa 100644
--- a/lib/testutils/httpmultibin/grpc_wrappers_testing/service.go
+++ b/lib/testutils/httpmultibin/grpc_wrappers_testing/service.go
@@ -1,14 +1,18 @@
-package grpc_wrappers_testing //nolint:revive,stylecheck
+// Package grpc_wrappers_testing provides a test service that could be used for the testing gRPC wrappers
+package grpc_wrappers_testing //nolint:revive,stylecheck // we want to be consistent with the other packages
import (
- "context"
+ context "context"
+ _struct "github.com/golang/protobuf/ptypes/struct"
wrappers "github.com/golang/protobuf/ptypes/wrappers"
- "google.golang.org/grpc"
+ grpc "google.golang.org/grpc"
)
+//go:generate protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative test.proto
+
// Register registers a test service that could be used for the testing gRPC wrappers
-func Register(r grpc.ServiceRegistrar) *service { //nolint:revive
+func Register(r grpc.ServiceRegistrar) *service { //nolint:revive // this is a test service
s := &service{}
RegisterServiceServer(r, s)
@@ -23,6 +27,8 @@ type service struct {
TestIntegerImplementation func(context.Context, *wrappers.Int64Value) (*wrappers.Int64Value, error)
TestBooleanImplementation func(context.Context, *wrappers.BoolValue) (*wrappers.BoolValue, error)
TestDoubleImplementation func(context.Context, *wrappers.DoubleValue) (*wrappers.DoubleValue, error)
+ TestValueImplementation func(context.Context, *_struct.Value) (*_struct.Value, error)
+ TestStreamImplementation func(Service_TestStreamServer) error
}
func (s *service) TestString(ctx context.Context, in *wrappers.StringValue) (*wrappers.StringValue, error) {
@@ -56,3 +62,19 @@ func (s *service) TestDouble(ctx context.Context, in *wrappers.DoubleValue) (*wr
return s.UnimplementedServiceServer.TestDouble(ctx, in)
}
+
+func (s *service) TestValue(ctx context.Context, in *_struct.Value) (*_struct.Value, error) {
+ if s.TestValueImplementation != nil {
+ return s.TestValueImplementation(ctx, in)
+ }
+
+ return s.UnimplementedServiceServer.TestValue(ctx, in)
+}
+
+func (s *service) TestStream(stream Service_TestStreamServer) error {
+ if s.TestStreamImplementation != nil {
+ return s.TestStreamImplementation(stream)
+ }
+
+ return s.UnimplementedServiceServer.TestStream(stream)
+}
diff --git a/lib/testutils/httpmultibin/grpc_wrappers_testing/test.pb.go b/lib/testutils/httpmultibin/grpc_wrappers_testing/test.pb.go
index 3c69d5be8694..b11e8500b828 100644
--- a/lib/testutils/httpmultibin/grpc_wrappers_testing/test.pb.go
+++ b/lib/testutils/httpmultibin/grpc_wrappers_testing/test.pb.go
@@ -7,6 +7,7 @@
package grpc_wrappers_testing
import (
+ _struct "github.com/golang/protobuf/ptypes/struct"
wrappers "github.com/golang/protobuf/ptypes/wrappers"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -27,28 +28,38 @@ var file_test_proto_rawDesc = []byte{
0x70, 0x63, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x74, 0x65, 0x73, 0x74,
0x69, 0x6e, 0x67, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72,
- 0x6f, 0x74, 0x6f, 0x32, 0xad, 0x02, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
- 0x48, 0x0a, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x2e,
- 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
- 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1c, 0x2e, 0x67, 0x6f,
+ 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x32, 0xb6, 0x03, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x48, 0x0a,
+ 0x0a, 0x54, 0x65, 0x73, 0x74, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74,
- 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x47, 0x0a, 0x0b, 0x54, 0x65, 0x73,
- 0x74, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x12, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
- 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34,
- 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
- 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c,
- 0x75, 0x65, 0x12, 0x45, 0x0a, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61,
- 0x6e, 0x12, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
- 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1a, 0x2e,
- 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
- 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x48, 0x0a, 0x0a, 0x54, 0x65, 0x73,
- 0x74, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
- 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65,
- 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+ 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69,
+ 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x47, 0x0a, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x49,
+ 0x6e, 0x74, 0x65, 0x67, 0x65, 0x72, 0x12, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61,
+ 0x6c, 0x75, 0x65, 0x1a, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65,
+ 0x12, 0x45, 0x0a, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x42, 0x6f, 0x6f, 0x6c, 0x65, 0x61, 0x6e, 0x12,
+ 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+ 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1a, 0x2e, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f,
+ 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x48, 0x0a, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x44,
+ 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61,
- 0x6c, 0x75, 0x65, 0x42, 0x19, 0x5a, 0x17, 0x2e, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x77, 0x72,
- 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x62, 0x06,
- 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x6c, 0x75, 0x65, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75,
+ 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x54, 0x65, 0x73, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4a,
+ 0x0a, 0x0a, 0x54, 0x65, 0x73, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1c, 0x2e, 0x67,
+ 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53,
+ 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x1c, 0x2e, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
+ 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x28, 0x01, 0x42, 0x19, 0x5a, 0x17, 0x2e, 0x2f,
+ 0x67, 0x72, 0x70, 0x63, 0x5f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x5f, 0x74, 0x65,
+ 0x73, 0x74, 0x69, 0x6e, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var file_test_proto_goTypes = []interface{}{
@@ -56,18 +67,23 @@ var file_test_proto_goTypes = []interface{}{
(*wrappers.Int64Value)(nil), // 1: google.protobuf.Int64Value
(*wrappers.BoolValue)(nil), // 2: google.protobuf.BoolValue
(*wrappers.DoubleValue)(nil), // 3: google.protobuf.DoubleValue
+ (*_struct.Value)(nil), // 4: google.protobuf.Value
}
var file_test_proto_depIdxs = []int32{
0, // 0: grpc.wrappers.testing.Service.TestString:input_type -> google.protobuf.StringValue
1, // 1: grpc.wrappers.testing.Service.TestInteger:input_type -> google.protobuf.Int64Value
2, // 2: grpc.wrappers.testing.Service.TestBoolean:input_type -> google.protobuf.BoolValue
3, // 3: grpc.wrappers.testing.Service.TestDouble:input_type -> google.protobuf.DoubleValue
- 0, // 4: grpc.wrappers.testing.Service.TestString:output_type -> google.protobuf.StringValue
- 1, // 5: grpc.wrappers.testing.Service.TestInteger:output_type -> google.protobuf.Int64Value
- 2, // 6: grpc.wrappers.testing.Service.TestBoolean:output_type -> google.protobuf.BoolValue
- 3, // 7: grpc.wrappers.testing.Service.TestDouble:output_type -> google.protobuf.DoubleValue
- 4, // [4:8] is the sub-list for method output_type
- 0, // [0:4] is the sub-list for method input_type
+ 4, // 4: grpc.wrappers.testing.Service.TestValue:input_type -> google.protobuf.Value
+ 0, // 5: grpc.wrappers.testing.Service.TestStream:input_type -> google.protobuf.StringValue
+ 0, // 6: grpc.wrappers.testing.Service.TestString:output_type -> google.protobuf.StringValue
+ 1, // 7: grpc.wrappers.testing.Service.TestInteger:output_type -> google.protobuf.Int64Value
+ 2, // 8: grpc.wrappers.testing.Service.TestBoolean:output_type -> google.protobuf.BoolValue
+ 3, // 9: grpc.wrappers.testing.Service.TestDouble:output_type -> google.protobuf.DoubleValue
+ 4, // 10: grpc.wrappers.testing.Service.TestValue:output_type -> google.protobuf.Value
+ 0, // 11: grpc.wrappers.testing.Service.TestStream:output_type -> google.protobuf.StringValue
+ 6, // [6:12] is the sub-list for method output_type
+ 0, // [0:6] 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
diff --git a/lib/testutils/httpmultibin/grpc_wrappers_testing/test.proto b/lib/testutils/httpmultibin/grpc_wrappers_testing/test.proto
index 6ba9f925a1b0..a1e9eb5fec20 100644
--- a/lib/testutils/httpmultibin/grpc_wrappers_testing/test.proto
+++ b/lib/testutils/httpmultibin/grpc_wrappers_testing/test.proto
@@ -6,6 +6,7 @@ package grpc.wrappers.testing;
// https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/wrappers.proto
import "google/protobuf/wrappers.proto";
+import "google/protobuf/struct.proto";
option go_package ="./grpc_wrappers_testing";
@@ -14,4 +15,7 @@ service Service {
rpc TestInteger(google.protobuf.Int64Value) returns (google.protobuf.Int64Value);
rpc TestBoolean(google.protobuf.BoolValue) returns (google.protobuf.BoolValue);
rpc TestDouble(google.protobuf.DoubleValue) returns (google.protobuf.DoubleValue);
+ rpc TestValue(google.protobuf.Value) returns (google.protobuf.Value);
+
+ rpc TestStream(stream google.protobuf.StringValue) returns (google.protobuf.StringValue);
}
diff --git a/lib/testutils/httpmultibin/grpc_wrappers_testing/test_grpc.pb.go b/lib/testutils/httpmultibin/grpc_wrappers_testing/test_grpc.pb.go
index 4f7cb02ec5f0..0af4769311e5 100644
--- a/lib/testutils/httpmultibin/grpc_wrappers_testing/test_grpc.pb.go
+++ b/lib/testutils/httpmultibin/grpc_wrappers_testing/test_grpc.pb.go
@@ -4,6 +4,7 @@ package grpc_wrappers_testing
import (
context "context"
+ _struct "github.com/golang/protobuf/ptypes/struct"
wrappers "github.com/golang/protobuf/ptypes/wrappers"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
@@ -23,6 +24,8 @@ type ServiceClient interface {
TestInteger(ctx context.Context, in *wrappers.Int64Value, opts ...grpc.CallOption) (*wrappers.Int64Value, error)
TestBoolean(ctx context.Context, in *wrappers.BoolValue, opts ...grpc.CallOption) (*wrappers.BoolValue, error)
TestDouble(ctx context.Context, in *wrappers.DoubleValue, opts ...grpc.CallOption) (*wrappers.DoubleValue, error)
+ TestValue(ctx context.Context, in *_struct.Value, opts ...grpc.CallOption) (*_struct.Value, error)
+ TestStream(ctx context.Context, opts ...grpc.CallOption) (Service_TestStreamClient, error)
}
type serviceClient struct {
@@ -69,6 +72,49 @@ func (c *serviceClient) TestDouble(ctx context.Context, in *wrappers.DoubleValue
return out, nil
}
+func (c *serviceClient) TestValue(ctx context.Context, in *_struct.Value, opts ...grpc.CallOption) (*_struct.Value, error) {
+ out := new(_struct.Value)
+ err := c.cc.Invoke(ctx, "/grpc.wrappers.testing.Service/TestValue", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *serviceClient) TestStream(ctx context.Context, opts ...grpc.CallOption) (Service_TestStreamClient, error) {
+ stream, err := c.cc.NewStream(ctx, &Service_ServiceDesc.Streams[0], "/grpc.wrappers.testing.Service/TestStream", opts...)
+ if err != nil {
+ return nil, err
+ }
+ x := &serviceTestStreamClient{stream}
+ return x, nil
+}
+
+type Service_TestStreamClient interface {
+ Send(*wrappers.StringValue) error
+ CloseAndRecv() (*wrappers.StringValue, error)
+ grpc.ClientStream
+}
+
+type serviceTestStreamClient struct {
+ grpc.ClientStream
+}
+
+func (x *serviceTestStreamClient) Send(m *wrappers.StringValue) error {
+ return x.ClientStream.SendMsg(m)
+}
+
+func (x *serviceTestStreamClient) CloseAndRecv() (*wrappers.StringValue, error) {
+ if err := x.ClientStream.CloseSend(); err != nil {
+ return nil, err
+ }
+ m := new(wrappers.StringValue)
+ if err := x.ClientStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
// ServiceServer is the server API for Service service.
// All implementations must embed UnimplementedServiceServer
// for forward compatibility
@@ -77,6 +123,8 @@ type ServiceServer interface {
TestInteger(context.Context, *wrappers.Int64Value) (*wrappers.Int64Value, error)
TestBoolean(context.Context, *wrappers.BoolValue) (*wrappers.BoolValue, error)
TestDouble(context.Context, *wrappers.DoubleValue) (*wrappers.DoubleValue, error)
+ TestValue(context.Context, *_struct.Value) (*_struct.Value, error)
+ TestStream(Service_TestStreamServer) error
mustEmbedUnimplementedServiceServer()
}
@@ -96,6 +144,12 @@ func (UnimplementedServiceServer) TestBoolean(context.Context, *wrappers.BoolVal
func (UnimplementedServiceServer) TestDouble(context.Context, *wrappers.DoubleValue) (*wrappers.DoubleValue, error) {
return nil, status.Errorf(codes.Unimplemented, "method TestDouble not implemented")
}
+func (UnimplementedServiceServer) TestValue(context.Context, *_struct.Value) (*_struct.Value, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method TestValue not implemented")
+}
+func (UnimplementedServiceServer) TestStream(Service_TestStreamServer) error {
+ return status.Errorf(codes.Unimplemented, "method TestStream not implemented")
+}
func (UnimplementedServiceServer) mustEmbedUnimplementedServiceServer() {}
// UnsafeServiceServer may be embedded to opt out of forward compatibility for this service.
@@ -181,6 +235,50 @@ func _Service_TestDouble_Handler(srv interface{}, ctx context.Context, dec func(
return interceptor(ctx, in, info, handler)
}
+func _Service_TestValue_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(_struct.Value)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(ServiceServer).TestValue(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/grpc.wrappers.testing.Service/TestValue",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(ServiceServer).TestValue(ctx, req.(*_struct.Value))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Service_TestStream_Handler(srv interface{}, stream grpc.ServerStream) error {
+ return srv.(ServiceServer).TestStream(&serviceTestStreamServer{stream})
+}
+
+type Service_TestStreamServer interface {
+ SendAndClose(*wrappers.StringValue) error
+ Recv() (*wrappers.StringValue, error)
+ grpc.ServerStream
+}
+
+type serviceTestStreamServer struct {
+ grpc.ServerStream
+}
+
+func (x *serviceTestStreamServer) SendAndClose(m *wrappers.StringValue) error {
+ return x.ServerStream.SendMsg(m)
+}
+
+func (x *serviceTestStreamServer) Recv() (*wrappers.StringValue, error) {
+ m := new(wrappers.StringValue)
+ if err := x.ServerStream.RecvMsg(m); err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
// Service_ServiceDesc is the grpc.ServiceDesc for Service service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -204,7 +302,17 @@ var Service_ServiceDesc = grpc.ServiceDesc{
MethodName: "TestDouble",
Handler: _Service_TestDouble_Handler,
},
+ {
+ MethodName: "TestValue",
+ Handler: _Service_TestValue_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{
+ {
+ StreamName: "TestStream",
+ Handler: _Service_TestStream_Handler,
+ ClientStreams: true,
+ },
},
- Streams: []grpc.StreamDesc{},
Metadata: "test.proto",
}
diff --git a/lib/testutils/httpmultibin/nested_types/nested_types.proto b/lib/testutils/httpmultibin/nested_types/nested_types.proto
new file mode 100644
index 000000000000..66dd353be35d
--- /dev/null
+++ b/lib/testutils/httpmultibin/nested_types/nested_types.proto
@@ -0,0 +1,36 @@
+// The purpose of this proto file is to demonstrate that we can have
+// nested types and that we should be able to load them correctly.
+
+syntax = "proto3";
+
+package grpc.testdata.nested.types;
+
+// Example to demonstrate that it is possible to define
+// and use message types within other message types
+message Outer { // Level 0
+ message MiddleAA { // Level 1
+ message Inner { // Level 2
+ int64 ival = 1;
+ bool booly = 2;
+ }
+ Inner inner = 1;
+ }
+
+ message MiddleBB { // Level 1
+ message Inner { // Level 2
+ int32 ival = 1;
+ bool booly = 2;
+ }
+ Inner inner = 1;
+ }
+
+ MiddleAA middleAA = 1;
+ MiddleBB middleBB = 2;
+}
+
+// Example to demonstrate that it is possible to reuse
+// a message type outside its parent message type
+message MeldOuter {
+ Outer.MiddleAA.Inner innerAA = 1;
+ Outer.MiddleBB.Inner innerBB = 2;
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go b/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go
new file mode 100644
index 000000000000..8d82abe21333
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go
@@ -0,0 +1,78 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: github.com/golang/protobuf/ptypes/struct/struct.proto
+
+package structpb
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ structpb "google.golang.org/protobuf/types/known/structpb"
+ reflect "reflect"
+)
+
+// Symbols defined in public import of google/protobuf/struct.proto.
+
+type NullValue = structpb.NullValue
+
+const NullValue_NULL_VALUE = structpb.NullValue_NULL_VALUE
+
+var NullValue_name = structpb.NullValue_name
+var NullValue_value = structpb.NullValue_value
+
+type Struct = structpb.Struct
+type Value = structpb.Value
+type Value_NullValue = structpb.Value_NullValue
+type Value_NumberValue = structpb.Value_NumberValue
+type Value_StringValue = structpb.Value_StringValue
+type Value_BoolValue = structpb.Value_BoolValue
+type Value_StructValue = structpb.Value_StructValue
+type Value_ListValue = structpb.Value_ListValue
+type ListValue = structpb.ListValue
+
+var File_github_com_golang_protobuf_ptypes_struct_struct_proto protoreflect.FileDescriptor
+
+var file_github_com_golang_protobuf_ptypes_struct_struct_proto_rawDesc = []byte{
+ 0x0a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c,
+ 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79,
+ 0x70, 0x65, 0x73, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63,
+ 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2f, 0x70, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63,
+ 0x74, 0x3b, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x70, 0x62, 0x50, 0x00, 0x62, 0x06, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var file_github_com_golang_protobuf_ptypes_struct_struct_proto_goTypes = []interface{}{}
+var file_github_com_golang_protobuf_ptypes_struct_struct_proto_depIdxs = []int32{
+ 0, // [0:0] is the sub-list for method output_type
+ 0, // [0:0] 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_github_com_golang_protobuf_ptypes_struct_struct_proto_init() }
+func file_github_com_golang_protobuf_ptypes_struct_struct_proto_init() {
+ if File_github_com_golang_protobuf_ptypes_struct_struct_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_github_com_golang_protobuf_ptypes_struct_struct_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 0,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_github_com_golang_protobuf_ptypes_struct_struct_proto_goTypes,
+ DependencyIndexes: file_github_com_golang_protobuf_ptypes_struct_struct_proto_depIdxs,
+ }.Build()
+ File_github_com_golang_protobuf_ptypes_struct_struct_proto = out.File
+ file_github_com_golang_protobuf_ptypes_struct_struct_proto_rawDesc = nil
+ file_github_com_golang_protobuf_ptypes_struct_struct_proto_goTypes = nil
+ file_github_com_golang_protobuf_ptypes_struct_struct_proto_depIdxs = nil
+}
diff --git a/vendor/github.com/grafana/xk6-grpc/LICENSE b/vendor/github.com/grafana/xk6-grpc/LICENSE
deleted file mode 100644
index 0ad25db4bd1d..000000000000
--- a/vendor/github.com/grafana/xk6-grpc/LICENSE
+++ /dev/null
@@ -1,661 +0,0 @@
- GNU AFFERO GENERAL PUBLIC LICENSE
- Version 3, 19 November 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU Affero General Public License is a free, copyleft license for
-software and other kinds of works, specifically designed to ensure
-cooperation with the community in the case of network server software.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-our General Public Licenses are intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- Developers that use our General Public Licenses protect your rights
-with two steps: (1) assert copyright on the software, and (2) offer
-you this License which gives you legal permission to copy, distribute
-and/or modify the software.
-
- A secondary benefit of defending all users' freedom is that
-improvements made in alternate versions of the program, if they
-receive widespread use, become available for other developers to
-incorporate. Many developers of free software are heartened and
-encouraged by the resulting cooperation. However, in the case of
-software used on network servers, this result may fail to come about.
-The GNU General Public License permits making a modified version and
-letting the public access it on a server without ever releasing its
-source code to the public.
-
- The GNU Affero General Public License is designed specifically to
-ensure that, in such cases, the modified source code becomes available
-to the community. It requires the operator of a network server to
-provide the source code of the modified version running there to the
-users of that server. Therefore, public use of a modified version, on
-a publicly accessible server, gives the public access to the source
-code of the modified version.
-
- An older license, called the Affero General Public License and
-published by Affero, was designed to accomplish similar goals. This is
-a different license, not a version of the Affero GPL, but Affero has
-released a new version of the Affero GPL which permits relicensing under
-this license.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU Affero General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Remote Network Interaction; Use with the GNU General Public License.
-
- Notwithstanding any other provision of this License, if you modify the
-Program, your modified version must prominently offer all users
-interacting with it remotely through a computer network (if your version
-supports such interaction) an opportunity to receive the Corresponding
-Source of your version by providing access to the Corresponding Source
-from a network server at no charge, through some standard or customary
-means of facilitating copying of software. This Corresponding Source
-shall include the Corresponding Source for any work covered by version 3
-of the GNU General Public License that is incorporated pursuant to the
-following paragraph.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the work with which it is combined will remain governed by version
-3 of the GNU General Public License.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU Affero General Public License from time to time. Such new versions
-will be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU Affero General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU Affero General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU Affero General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If your software can interact with users remotely through a computer
-network, you should also make sure that it provides a way for users to
-get its source. For example, if your program is a web application, its
-interface could display a "Source" link that leads users to an archive
-of the code. There are many ways you could offer source, and different
-solutions will be better for different programs; see section 13 for the
-specific requirements.
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU AGPL, see
-.
diff --git a/vendor/github.com/grafana/xk6-grpc/grpc/client.go b/vendor/github.com/grafana/xk6-grpc/grpc/client.go
deleted file mode 100644
index ef46ffce0e25..000000000000
--- a/vendor/github.com/grafana/xk6-grpc/grpc/client.go
+++ /dev/null
@@ -1,469 +0,0 @@
-package grpc
-
-import (
- "context"
- "crypto/tls"
- "crypto/x509"
- "encoding/pem"
- "errors"
- "fmt"
- "io"
- "strings"
- "time"
-
- "github.com/grafana/xk6-grpc/lib/netext/grpcext"
- "go.k6.io/k6/js/common"
- "go.k6.io/k6/js/modules"
-
- "github.com/dop251/goja"
- "github.com/jhump/protoreflect/desc"
- "github.com/jhump/protoreflect/desc/protoparse"
- "google.golang.org/grpc"
- "google.golang.org/grpc/credentials"
- "google.golang.org/grpc/credentials/insecure"
- "google.golang.org/grpc/metadata"
- "google.golang.org/protobuf/proto"
- "google.golang.org/protobuf/reflect/protodesc"
- "google.golang.org/protobuf/reflect/protoreflect"
- "google.golang.org/protobuf/reflect/protoregistry"
- "google.golang.org/protobuf/types/descriptorpb"
- "google.golang.org/protobuf/types/dynamicpb"
-)
-
-// Client represents a gRPC client that can be used to make RPC requests
-type Client struct {
- mds map[string]protoreflect.MethodDescriptor
- conn *grpcext.Conn
- vu modules.VU
- addr string
-}
-
-// Load will parse the given proto files and make the file descriptors available to request.
-func (c *Client) Load(importPaths []string, filenames ...string) ([]MethodInfo, error) {
- if c.vu.State() != nil {
- return nil, errors.New("load must be called in the init context")
- }
-
- initEnv := c.vu.InitEnv()
- if initEnv == nil {
- return nil, errors.New("missing init environment")
- }
-
- // If no import paths are specified, use the current working directory
- if len(importPaths) == 0 {
- importPaths = append(importPaths, initEnv.CWD.Path)
- }
-
- parser := protoparse.Parser{
- ImportPaths: importPaths,
- InferImportPaths: false,
- Accessor: protoparse.FileAccessor(func(filename string) (io.ReadCloser, error) {
- absFilePath := initEnv.GetAbsFilePath(filename)
- return initEnv.FileSystems["file"].Open(absFilePath)
- }),
- }
-
- fds, err := parser.ParseFiles(filenames...)
- if err != nil {
- return nil, err
- }
-
- fdset := &descriptorpb.FileDescriptorSet{}
-
- seen := make(map[string]struct{})
- for _, fd := range fds {
- fdset.File = append(fdset.File, walkFileDescriptors(seen, fd)...)
- }
- return c.convertToMethodInfo(fdset)
-}
-
-// LoadProtoset will parse the given protoset file (serialized FileDescriptorSet) and make the file
-// descriptors available to request.
-func (c *Client) LoadProtoset(protosetPath string) ([]MethodInfo, error) {
- if c.vu.State() != nil {
- return nil, errors.New("load must be called in the init context")
- }
-
- initEnv := c.vu.InitEnv()
- if initEnv == nil {
- return nil, errors.New("missing init environment")
- }
-
- absFilePath := initEnv.GetAbsFilePath(protosetPath)
- fdsetFile, err := initEnv.FileSystems["file"].Open(absFilePath)
- if err != nil {
- return nil, fmt.Errorf("couldn't open protoset: %w", err)
- }
-
- defer func() { _ = fdsetFile.Close() }()
- fdsetBytes, err := io.ReadAll(fdsetFile)
- if err != nil {
- return nil, fmt.Errorf("couldn't read protoset: %w", err)
- }
-
- fdset := &descriptorpb.FileDescriptorSet{}
- if err = proto.Unmarshal(fdsetBytes, fdset); err != nil {
- return nil, fmt.Errorf("couldn't unmarshal protoset file %s: %w", protosetPath, err)
- }
-
- return c.convertToMethodInfo(fdset)
-}
-
-// Note: this function was lifted from `lib/options.go`
-func decryptPrivateKey(key, password []byte) ([]byte, error) {
- block, _ := pem.Decode(key)
- if block == nil {
- return nil, errors.New("failed to decode PEM key")
- }
-
- blockType := block.Type
- if blockType == "ENCRYPTED PRIVATE KEY" {
- return nil, errors.New("encrypted pkcs8 formatted key is not supported")
- }
- /*
- Even though `DecryptPEMBlock` has been deprecated since 1.16.x it is still
- being used here because it is deprecated due to it not supporting *good* cryptography
- ultimately though we want to support something so we will be using it for now.
- */
- decryptedKey, err := x509.DecryptPEMBlock(block, password) //nolint:staticcheck
- if err != nil {
- return nil, err
- }
- key = pem.EncodeToMemory(&pem.Block{
- Type: blockType,
- Bytes: decryptedKey,
- })
- return key, nil
-}
-
-func buildTLSConfig(parentConfig *tls.Config, certificate, key []byte, caCertificates [][]byte) (*tls.Config, error) {
- var cp *x509.CertPool
- if len(caCertificates) > 0 {
- cp, _ = x509.SystemCertPool()
- for i, caCert := range caCertificates {
- if ok := cp.AppendCertsFromPEM(caCert); !ok {
- return nil, fmt.Errorf("failed to append ca certificate [%d] from PEM", i)
- }
- }
- }
-
- // Ignoring 'TLS MinVersion is too low' because this tls.Config will inherit MinValue and MaxValue
- // from the vu state tls.Config
-
- //nolint:golint,gosec
- tlsCfg := &tls.Config{
- CipherSuites: parentConfig.CipherSuites,
- InsecureSkipVerify: parentConfig.InsecureSkipVerify,
- MinVersion: parentConfig.MinVersion,
- MaxVersion: parentConfig.MaxVersion,
- Renegotiation: parentConfig.Renegotiation,
- RootCAs: cp,
- }
- if len(certificate) > 0 && len(key) > 0 {
- cert, err := tls.X509KeyPair(certificate, key)
- if err != nil {
- return nil, fmt.Errorf("failed to append certificate from PEM: %w", err)
- }
- tlsCfg.Certificates = []tls.Certificate{cert}
- }
- return tlsCfg, nil
-}
-
-func buildTLSConfigFromMap(parentConfig *tls.Config, tlsConfigMap map[string]interface{}) (*tls.Config, error) {
- var cert, key, pass []byte
- var ca [][]byte
- var err error
- if certstr, ok := tlsConfigMap["cert"].(string); ok {
- cert = []byte(certstr)
- }
- if keystr, ok := tlsConfigMap["key"].(string); ok {
- key = []byte(keystr)
- }
- if passwordStr, ok := tlsConfigMap["password"].(string); ok {
- pass = []byte(passwordStr)
- if len(pass) > 0 {
- if key, err = decryptPrivateKey(key, pass); err != nil {
- return nil, err
- }
- }
- }
- if cas, ok := tlsConfigMap["cacerts"]; ok {
- var caCertsArray []interface{}
- if caCertsArray, ok = cas.([]interface{}); ok {
- ca = make([][]byte, len(caCertsArray))
- for i, entry := range caCertsArray {
- var entryStr string
- if entryStr, ok = entry.(string); ok {
- ca[i] = []byte(entryStr)
- }
- }
- } else if caCertStr, caCertStrOk := cas.(string); caCertStrOk {
- ca = [][]byte{[]byte(caCertStr)}
- }
- }
- return buildTLSConfig(parentConfig, cert, key, ca)
-}
-
-// Connect is a block dial to the gRPC server at the given address (host:port)
-func (c *Client) Connect(addr string, params goja.Value) (bool, error) {
- state := c.vu.State()
- if state == nil {
- return false, common.NewInitContextError("connecting to a gRPC server in the init context is not supported")
- }
-
- p, err := newConnectParams(c.vu, params)
- if err != nil {
- return false, fmt.Errorf("invalid grpc.connect() parameters: %w", err)
- }
-
- opts := grpcext.DefaultOptions(c.vu.State)
-
- var tcred credentials.TransportCredentials
- if !p.IsPlaintext {
- tlsCfg := state.TLSConfig.Clone()
- if len(p.TLS) > 0 {
- if tlsCfg, err = buildTLSConfigFromMap(tlsCfg, p.TLS); err != nil {
- return false, err
- }
- }
- tlsCfg.NextProtos = []string{"h2"}
-
- tcred = credentials.NewTLS(tlsCfg)
- } else {
- tcred = insecure.NewCredentials()
- }
- opts = append(opts, grpc.WithTransportCredentials(tcred))
-
- if ua := state.Options.UserAgent; ua.Valid {
- opts = append(opts, grpc.WithUserAgent(ua.ValueOrZero()))
- }
-
- ctx, cancel := context.WithTimeout(c.vu.Context(), p.Timeout)
- defer cancel()
-
- if p.MaxReceiveSize > 0 {
- opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(int(p.MaxReceiveSize))))
- }
-
- if p.MaxSendSize > 0 {
- opts = append(opts, grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(int(p.MaxSendSize))))
- }
-
- c.addr = addr
- c.conn, err = grpcext.Dial(ctx, addr, opts...)
- if err != nil {
- return false, err
- }
-
- if !p.UseReflectionProtocol {
- return true, nil
- }
-
- ctx = metadata.NewOutgoingContext(ctx, p.ReflectionMetadata)
-
- fdset, err := c.conn.Reflect(ctx)
- if err != nil {
- return false, err
- }
- _, err = c.convertToMethodInfo(fdset)
- if err != nil {
- return false, fmt.Errorf("can't convert method info: %w", err)
- }
-
- return true, err
-}
-
-// Invoke creates and calls a unary RPC by fully qualified method name
-func (c *Client) Invoke(
- method string,
- req goja.Value,
- params goja.Value,
-) (*grpcext.Response, error) {
- state := c.vu.State()
- if state == nil {
- return nil, common.NewInitContextError("invoking RPC methods in the init context is not supported")
- }
- if c.conn == nil {
- return nil, errors.New("no gRPC connection, you must call connect first")
- }
- if method == "" {
- return nil, errors.New("method to invoke cannot be empty")
- }
- if method[0] != '/' {
- method = "/" + method
- }
- methodDesc := c.mds[method]
- if methodDesc == nil {
- return nil, fmt.Errorf("method %q not found in file descriptors", method)
- }
-
- p, err := newCallParams(c.vu, params)
- if err != nil {
- return nil, fmt.Errorf("invalid GRPC's client.invoke() parameters: %w", err)
- }
-
- // k6 GRPC Invoke's default timeout is 2 minutes
- if p.Timeout == time.Duration(0) {
- p.Timeout = 2 * time.Minute
- }
-
- if req == nil {
- return nil, errors.New("request cannot be nil")
- }
- b, err := req.ToObject(c.vu.Runtime()).MarshalJSON()
- if err != nil {
- return nil, fmt.Errorf("unable to serialise request object: %w", err)
- }
-
- ctx, cancel := context.WithTimeout(c.vu.Context(), p.Timeout)
- defer cancel()
-
- p.SetSystemTags(state, c.addr, method)
-
- reqmsg := grpcext.Request{
- MethodDescriptor: methodDesc,
- Message: b,
- TagsAndMeta: &p.TagsAndMeta,
- }
-
- return c.conn.Invoke(ctx, method, p.Metadata, reqmsg)
-}
-
-// Close will close the client gRPC connection
-func (c *Client) Close() error {
- if c.conn == nil {
- return nil
- }
- err := c.conn.Close()
- c.conn = nil
-
- return err
-}
-
-// MethodInfo holds information on any parsed method descriptors that can be used by the goja VM
-type MethodInfo struct {
- Package string
- Service string
- FullMethod string
- grpc.MethodInfo `json:"-" js:"-"`
-}
-
-func (c *Client) convertToMethodInfo(fdset *descriptorpb.FileDescriptorSet) ([]MethodInfo, error) {
- files, err := protodesc.NewFiles(fdset)
- if err != nil {
- return nil, err
- }
- var rtn []MethodInfo
- if c.mds == nil {
- // This allows us to call load() multiple times, without overwriting the
- // previously loaded definitions.
- c.mds = make(map[string]protoreflect.MethodDescriptor)
- }
- appendMethodInfo := func(
- fd protoreflect.FileDescriptor,
- sd protoreflect.ServiceDescriptor,
- md protoreflect.MethodDescriptor,
- ) {
- name := fmt.Sprintf("/%s/%s", sd.FullName(), md.Name())
- c.mds[name] = md
- rtn = append(rtn, MethodInfo{
- MethodInfo: grpc.MethodInfo{
- Name: string(md.Name()),
- IsClientStream: md.IsStreamingClient(),
- IsServerStream: md.IsStreamingServer(),
- },
- Package: string(fd.Package()),
- Service: string(sd.Name()),
- FullMethod: name,
- })
- }
- files.RangeFiles(func(fd protoreflect.FileDescriptor) bool {
- sds := fd.Services()
- for i := 0; i < sds.Len(); i++ {
- sd := sds.Get(i)
- mds := sd.Methods()
- for j := 0; j < mds.Len(); j++ {
- md := mds.Get(j)
- appendMethodInfo(fd, sd, md)
- }
- }
-
- messages := fd.Messages()
-
- stack := make([]protoreflect.MessageDescriptor, 0, messages.Len())
- for i := 0; i < messages.Len(); i++ {
- stack = append(stack, messages.Get(i))
- }
-
- for len(stack) > 0 {
- message := stack[len(stack)-1]
- stack = stack[:len(stack)-1]
-
- _, errFind := protoregistry.GlobalTypes.FindMessageByName(message.FullName())
- if errors.Is(errFind, protoregistry.NotFound) {
- err = protoregistry.GlobalTypes.RegisterMessage(dynamicpb.NewMessageType(message))
- if err != nil {
- return false
- }
- }
-
- nested := message.Messages()
- for i := 0; i < nested.Len(); i++ {
- stack = append(stack, nested.Get(i))
- }
- }
-
- return true
- })
- if err != nil {
- return nil, err
- }
- return rtn, nil
-}
-
-func walkFileDescriptors(seen map[string]struct{}, fd *desc.FileDescriptor) []*descriptorpb.FileDescriptorProto {
- fds := []*descriptorpb.FileDescriptorProto{}
-
- if _, ok := seen[fd.GetName()]; ok {
- return fds
- }
- seen[fd.GetName()] = struct{}{}
- fds = append(fds, fd.AsFileDescriptorProto())
-
- for _, dep := range fd.GetDependencies() {
- deps := walkFileDescriptors(seen, dep)
- fds = append(fds, deps...)
- }
-
- return fds
-}
-
-// sanitizeMethodName
-func sanitizeMethodName(name string) string {
- if name == "" {
- return name
- }
-
- if !strings.HasPrefix(name, "/") {
- name = "/" + name
- }
-
- return name
-}
-
-// getMethodDescriptor sanitize it, and gets GRPC method descriptor or an error if not found
-func (c *Client) getMethodDescriptor(method string) (protoreflect.MethodDescriptor, error) {
- method = sanitizeMethodName(method)
-
- if method == "" {
- return nil, errors.New("method to invoke cannot be empty")
- }
-
- methodDesc := c.mds[method]
-
- if methodDesc == nil {
- return nil, fmt.Errorf("method %q not found in file descriptors", method)
- }
-
- return methodDesc, nil
-}
diff --git a/vendor/github.com/grafana/xk6-grpc/grpc/grpc.go b/vendor/github.com/grafana/xk6-grpc/grpc/grpc.go
deleted file mode 100644
index fdf2a8f42ec4..000000000000
--- a/vendor/github.com/grafana/xk6-grpc/grpc/grpc.go
+++ /dev/null
@@ -1,171 +0,0 @@
-// Package grpc is the root module of the k6-grpc extension.
-package grpc
-
-import (
- "errors"
- "fmt"
-
- "github.com/dop251/goja"
- "github.com/mstoykov/k6-taskqueue-lib/taskqueue"
- "go.k6.io/k6/js/common"
- "go.k6.io/k6/js/modules"
- "google.golang.org/grpc/codes"
-)
-
-type (
- // RootModule is the global module instance that will create module
- // instances for each VU.
- RootModule struct{}
-
- // ModuleInstance represents an instance of the GRPC module for every VU.
- ModuleInstance struct {
- vu modules.VU
- exports map[string]interface{}
- metrics *instanceMetrics
- }
-)
-
-var (
- _ modules.Module = &RootModule{}
- _ modules.Instance = &ModuleInstance{}
-)
-
-// New returns a pointer to a new RootModule instance.
-func New() *RootModule {
- return &RootModule{}
-}
-
-// NewModuleInstance implements the modules.Module interface to return
-// a new instance for each VU.
-func (*RootModule) NewModuleInstance(vu modules.VU) modules.Instance {
- metrics, err := registerMetrics(vu.InitEnv().Registry)
- if err != nil {
- common.Throw(vu.Runtime(), fmt.Errorf("failed to register GRPC module metrics: %w", err))
- }
-
- mi := &ModuleInstance{
- vu: vu,
- exports: make(map[string]interface{}),
- metrics: metrics,
- }
-
- mi.exports["Client"] = mi.NewClient
- mi.defineConstants()
- mi.exports["Stream"] = mi.stream
-
- return mi
-}
-
-// NewClient is the JS constructor for the grpc Client.
-func (mi *ModuleInstance) NewClient(_ goja.ConstructorCall) *goja.Object {
- rt := mi.vu.Runtime()
- return rt.ToValue(&Client{vu: mi.vu}).ToObject(rt)
-}
-
-// defineConstants defines the constant variables of the module.
-func (mi *ModuleInstance) defineConstants() {
- rt := mi.vu.Runtime()
- mustAddCode := func(name string, code codes.Code) {
- mi.exports[name] = rt.ToValue(code)
- }
-
- mustAddCode("StatusOK", codes.OK)
- mustAddCode("StatusCanceled", codes.Canceled)
- mustAddCode("StatusUnknown", codes.Unknown)
- mustAddCode("StatusInvalidArgument", codes.InvalidArgument)
- mustAddCode("StatusDeadlineExceeded", codes.DeadlineExceeded)
- mustAddCode("StatusNotFound", codes.NotFound)
- mustAddCode("StatusAlreadyExists", codes.AlreadyExists)
- mustAddCode("StatusPermissionDenied", codes.PermissionDenied)
- mustAddCode("StatusResourceExhausted", codes.ResourceExhausted)
- mustAddCode("StatusFailedPrecondition", codes.FailedPrecondition)
- mustAddCode("StatusAborted", codes.Aborted)
- mustAddCode("StatusOutOfRange", codes.OutOfRange)
- mustAddCode("StatusUnimplemented", codes.Unimplemented)
- mustAddCode("StatusInternal", codes.Internal)
- mustAddCode("StatusUnavailable", codes.Unavailable)
- mustAddCode("StatusDataLoss", codes.DataLoss)
- mustAddCode("StatusUnauthenticated", codes.Unauthenticated)
-}
-
-// Exports returns the exports of the grpc module.
-func (mi *ModuleInstance) Exports() modules.Exports {
- return modules.Exports{
- Named: mi.exports,
- }
-}
-
-// stream returns a new stream object
-func (mi *ModuleInstance) stream(c goja.ConstructorCall) *goja.Object {
- rt := mi.vu.Runtime()
-
- client, err := extractClient(c.Argument(0), rt)
- if err != nil {
- common.Throw(rt, fmt.Errorf("invalid GRPC Stream's client: %w", err))
- }
-
- methodName := sanitizeMethodName(c.Argument(1).String())
- methodDescriptor, err := client.getMethodDescriptor(methodName)
- if err != nil {
- common.Throw(rt, fmt.Errorf("invalid GRPC Stream's method: %w", err))
- }
-
- p, err := newCallParams(mi.vu, c.Argument(2))
- if err != nil {
- common.Throw(rt, fmt.Errorf("invalid GRPC Stream's parameters: %w", err))
- }
-
- p.SetSystemTags(mi.vu.State(), client.addr, methodName)
-
- logger := mi.vu.State().Logger.WithField("streamMethod", methodName)
-
- s := &stream{
- vu: mi.vu,
- client: client,
- methodDescriptor: methodDescriptor,
- method: methodName,
- logger: logger,
-
- tq: taskqueue.New(mi.vu.RegisterCallback),
-
- instanceMetrics: mi.metrics,
- builtinMetrics: mi.vu.State().BuiltinMetrics,
- done: make(chan struct{}),
- writingState: opened,
-
- writeQueueCh: make(chan message),
-
- eventListeners: newEventListeners(),
- obj: rt.NewObject(),
- tagsAndMeta: &p.TagsAndMeta,
- }
-
- defineStream(rt, s)
-
- err = s.beginStream(p)
- if err != nil {
- s.tq.Close()
-
- common.Throw(rt, err)
- }
-
- return s.obj
-}
-
-// extractClient extracts & validates a grpc.Client from a goja.Value.
-func extractClient(v goja.Value, rt *goja.Runtime) (*Client, error) {
- if common.IsNullish(v) {
- return nil, errors.New("empty gRPC client")
- }
-
- client, ok := v.ToObject(rt).Export().(*Client)
- if !ok {
- return nil, errors.New("not a gRPC client")
- }
-
- if client.conn == nil {
- return nil, errors.New("no gRPC connection, you must call connect first")
- }
-
- return client, nil
-}
diff --git a/vendor/github.com/grafana/xk6-grpc/lib/netext/grpcext/conn.go b/vendor/github.com/grafana/xk6-grpc/lib/netext/grpcext/conn.go
deleted file mode 100644
index 5e9f9e0fc0b9..000000000000
--- a/vendor/github.com/grafana/xk6-grpc/lib/netext/grpcext/conn.go
+++ /dev/null
@@ -1,346 +0,0 @@
-// Package grpcext allows gRPC requests collecting stats info.
-package grpcext
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net"
- "strconv"
- "strings"
-
- "github.com/sirupsen/logrus"
- "go.k6.io/k6/lib"
- "go.k6.io/k6/metrics"
-
- protov1 "github.com/golang/protobuf/proto" //nolint:staticcheck,nolintlint // this is the old v1 version
- "google.golang.org/grpc"
- "google.golang.org/grpc/codes"
- "google.golang.org/grpc/metadata"
- grpcstats "google.golang.org/grpc/stats"
- "google.golang.org/grpc/status"
- "google.golang.org/protobuf/encoding/protojson"
- "google.golang.org/protobuf/encoding/prototext"
- "google.golang.org/protobuf/proto"
- "google.golang.org/protobuf/reflect/protoreflect"
- "google.golang.org/protobuf/types/descriptorpb"
- "google.golang.org/protobuf/types/dynamicpb"
-)
-
-// Request represents a gRPC request.
-type Request struct {
- MethodDescriptor protoreflect.MethodDescriptor
- TagsAndMeta *metrics.TagsAndMeta
- Message []byte
-}
-
-// StreamRequest represents a gRPC stream request.
-type StreamRequest struct {
- Method string
- MethodDescriptor protoreflect.MethodDescriptor
- TagsAndMeta *metrics.TagsAndMeta
- Metadata metadata.MD
-}
-
-// Response represents a gRPC response.
-type Response struct {
- Message interface{}
- Error interface{}
- Headers map[string][]string
- Trailers map[string][]string
- Status codes.Code
-}
-
-type clientConnCloser interface {
- grpc.ClientConnInterface
- Close() error
-}
-
-// Conn is a gRPC client connection.
-type Conn struct {
- raw clientConnCloser
-}
-
-// DefaultOptions generates an option set
-// with common options for requests from a VU.
-func DefaultOptions(getState func() *lib.State) []grpc.DialOption {
- dialer := func(ctx context.Context, addr string) (net.Conn, error) {
- return getState().Dialer.DialContext(ctx, "tcp", addr)
- }
-
- return []grpc.DialOption{
- grpc.WithBlock(),
- grpc.FailOnNonTempDialError(true),
- grpc.WithReturnConnectionError(),
- grpc.WithStatsHandler(statsHandler{getState: getState}),
- grpc.WithContextDialer(dialer),
- }
-}
-
-// Dial establish a gRPC connection.
-func Dial(ctx context.Context, addr string, options ...grpc.DialOption) (*Conn, error) {
- conn, err := grpc.DialContext(ctx, addr, options...)
- if err != nil {
- return nil, err
- }
- return &Conn{
- raw: conn,
- }, nil
-}
-
-// Reflect returns using the reflection the FileDescriptorSet describing the service.
-func (c *Conn) Reflect(ctx context.Context) (*descriptorpb.FileDescriptorSet, error) {
- rc := reflectionClient{Conn: c.raw}
- return rc.Reflect(ctx)
-}
-
-// Invoke executes a unary gRPC request.
-func (c *Conn) Invoke(
- ctx context.Context,
- url string,
- md metadata.MD,
- req Request,
- opts ...grpc.CallOption,
-) (*Response, error) {
- if url == "" {
- return nil, fmt.Errorf("url is required")
- }
- if req.MethodDescriptor == nil {
- return nil, fmt.Errorf("request method descriptor is required")
- }
- if len(req.Message) == 0 {
- return nil, fmt.Errorf("request message is required")
- }
-
- ctx = metadata.NewOutgoingContext(ctx, md)
-
- reqdm := dynamicpb.NewMessage(req.MethodDescriptor.Input())
- if err := protojson.Unmarshal(req.Message, reqdm); err != nil {
- return nil, fmt.Errorf("unable to serialise request object to protocol buffer: %w", err)
- }
-
- ctx = withRPCState(ctx, &rpcState{tagsAndMeta: req.TagsAndMeta})
-
- resp := dynamicpb.NewMessage(req.MethodDescriptor.Output())
- header, trailer := metadata.New(nil), metadata.New(nil)
-
- copts := make([]grpc.CallOption, 0, len(opts)+2)
- copts = append(copts, opts...)
- copts = append(copts, grpc.Header(&header), grpc.Trailer(&trailer))
-
- err := c.raw.Invoke(ctx, url, reqdm, resp, copts...)
-
- response := Response{
- Headers: header,
- Trailers: trailer,
- }
-
- marshaler := protojson.MarshalOptions{EmitUnpopulated: true}
-
- if err != nil {
- sterr := status.Convert(err)
- response.Status = sterr.Code()
-
- // (rogchap) when you access a JSON property in goja, you are actually accessing the underling
- // Go type (struct, map, slice etc); because these are dynamic messages the Unmarshaled JSON does
- // not map back to a "real" field or value (as a normal Go type would). If we don't marshal and then
- // unmarshal back to a map, you will get "undefined" when accessing JSON properties, even when
- // JSON.Stringify() shows the object to be correctly present.
-
- raw, _ := marshaler.Marshal(sterr.Proto())
- errMsg := make(map[string]interface{})
- _ = json.Unmarshal(raw, &errMsg)
- response.Error = errMsg
- }
-
- if resp != nil {
- msg, err := convert(marshaler, resp)
- if err != nil {
- return nil, fmt.Errorf("unable to convert response object to JSON: %w", err)
- }
-
- response.Message = msg
- }
- return &response, nil
-}
-
-// NewStream creates a new gRPC stream.
-func (c *Conn) NewStream(
- ctx context.Context,
- req StreamRequest,
- opts ...grpc.CallOption,
-) (*Stream, error) {
- ctx = metadata.NewOutgoingContext(ctx, req.Metadata)
-
- ctx = withRPCState(ctx, &rpcState{tagsAndMeta: req.TagsAndMeta})
-
- stream, err := c.raw.NewStream(ctx, &grpc.StreamDesc{
- StreamName: string(req.MethodDescriptor.Name()),
- ServerStreams: req.MethodDescriptor.IsStreamingServer(),
- ClientStreams: req.MethodDescriptor.IsStreamingClient(),
- }, req.Method, opts...)
- if err != nil {
- return nil, err
- }
-
- return &Stream{
- raw: stream,
- method: req.Method,
- methodDescriptor: req.MethodDescriptor,
- }, nil
-}
-
-// Close closes the underhood connection.
-func (c *Conn) Close() error {
- return c.raw.Close()
-}
-
-type statsHandler struct {
- getState func() *lib.State
-}
-
-// TagConn implements the grpcstats.Handler interface
-func (statsHandler) TagConn(ctx context.Context, _ *grpcstats.ConnTagInfo) context.Context { // noop
- return ctx
-}
-
-// HandleConn implements the grpcstats.Handler interface
-func (statsHandler) HandleConn(context.Context, grpcstats.ConnStats) {
- // noop
-}
-
-// TagRPC implements the grpcstats.Handler interface
-func (statsHandler) TagRPC(ctx context.Context, _ *grpcstats.RPCTagInfo) context.Context {
- // noop
- return ctx
-}
-
-// HandleRPC implements the grpcstats.Handler interface
-func (h statsHandler) HandleRPC(ctx context.Context, stat grpcstats.RPCStats) {
- state := h.getState()
- stateRPC := getRPCState(ctx) //nolint:ifshort
-
- // If the request is done by the reflection handler then the tags will be
- // nil. In this case, we can reuse the VU.State's Tags.
- if stateRPC == nil {
- // TODO: investigate this more, there has to be a way to fix it :/
- ctm := state.Tags.GetCurrentValues()
- stateRPC = &rpcState{tagsAndMeta: &ctm}
- }
-
- switch s := stat.(type) {
- case *grpcstats.OutHeader:
- // TODO: figure out something better, e.g. via TagConn() or TagRPC()?
- if state.Options.SystemTags.Has(metrics.TagIP) && s.RemoteAddr != nil {
- if ip, _, err := net.SplitHostPort(s.RemoteAddr.String()); err == nil {
- stateRPC.tagsAndMeta.SetSystemTagOrMeta(metrics.TagIP, ip)
- }
- }
- case *grpcstats.End:
- if state.Options.SystemTags.Has(metrics.TagStatus) {
- stateRPC.tagsAndMeta.SetSystemTagOrMeta(metrics.TagStatus, strconv.Itoa(int(status.Code(s.Error))))
- }
-
- metrics.PushIfNotDone(ctx, state.Samples, metrics.Sample{
- TimeSeries: metrics.TimeSeries{
- Metric: state.BuiltinMetrics.GRPCReqDuration,
- Tags: stateRPC.tagsAndMeta.Tags,
- },
- Time: s.EndTime,
- Metadata: stateRPC.tagsAndMeta.Metadata,
- Value: metrics.D(s.EndTime.Sub(s.BeginTime)),
- })
- }
-
- // (rogchap) Re-using --http-debug flag as gRPC is technically still HTTP
- if state.Options.HTTPDebug.String != "" {
- logger := state.Logger.WithField("source", "http-debug")
- httpDebugOption := state.Options.HTTPDebug.String
- DebugStat(logger, stat, httpDebugOption)
- }
-}
-
-// DebugStat prints debugging information based on RPCStats.
-func DebugStat(logger logrus.FieldLogger, stat grpcstats.RPCStats, httpDebugOption string) {
- switch s := stat.(type) {
- case *grpcstats.OutHeader:
- logger.Infof("Out Header:\nFull Method: %s\nRemote Address: %s\n%s\n",
- s.FullMethod, s.RemoteAddr, formatMetadata(s.Header))
- case *grpcstats.OutTrailer:
- if len(s.Trailer) > 0 {
- logger.Infof("Out Trailer:\n%s\n", formatMetadata(s.Trailer))
- }
- case *grpcstats.OutPayload:
- if httpDebugOption == "full" {
- logger.Infof("Out Payload:\nWire Length: %d\nSent Time: %s\n%s\n\n",
- s.WireLength, s.SentTime, formatPayload(s.Payload))
- }
- case *grpcstats.InHeader:
- if len(s.Header) > 0 {
- logger.Infof("In Header:\nWire Length: %d\n%s\n", s.WireLength, formatMetadata(s.Header))
- }
- case *grpcstats.InTrailer:
- if len(s.Trailer) > 0 {
- logger.Infof("In Trailer:\nWire Length: %d\n%s\n", s.WireLength, formatMetadata(s.Trailer))
- }
- case *grpcstats.InPayload:
- if httpDebugOption == "full" {
- logger.Infof("In Payload:\nWire Length: %d\nReceived Time: %s\n%s\n\n",
- s.WireLength, s.RecvTime, formatPayload(s.Payload))
- }
- }
-}
-
-func formatMetadata(md metadata.MD) string {
- var sb strings.Builder
- for k, v := range md {
- sb.WriteString(k)
- sb.WriteString(": ")
- sb.WriteString(strings.Join(v, ", "))
- sb.WriteRune('\n')
- }
-
- return sb.String()
-}
-
-func formatPayload(payload interface{}) string {
- msg, ok := payload.(proto.Message)
- if !ok {
- // check to see if we are dealing with a APIv1 message
- msgV1, ok := payload.(protov1.Message)
- if !ok {
- return ""
- }
- msg = protov1.MessageV2(msgV1)
- }
-
- marshaler := prototext.MarshalOptions{
- Multiline: true,
- Indent: " ",
- }
- b, err := marshaler.Marshal(msg)
- if err != nil {
- return ""
- }
- return string(b)
-}
-
-type contextKey string
-
-var ctxKeyRPCState = contextKey("rpcState") //nolint:gochecknoglobals
-
-type rpcState struct {
- tagsAndMeta *metrics.TagsAndMeta
-}
-
-func withRPCState(ctx context.Context, rpcState *rpcState) context.Context {
- return context.WithValue(ctx, ctxKeyRPCState, rpcState)
-}
-
-func getRPCState(ctx context.Context) *rpcState {
- v := ctx.Value(ctxKeyRPCState)
- if v == nil {
- return nil
- }
- return v.(*rpcState) //nolint: forcetypeassert
-}
diff --git a/vendor/github.com/grafana/xk6-grpc/lib/netext/grpcext/reflect.go b/vendor/github.com/grafana/xk6-grpc/lib/netext/grpcext/reflect.go
deleted file mode 100644
index 548e2aeab3bc..000000000000
--- a/vendor/github.com/grafana/xk6-grpc/lib/netext/grpcext/reflect.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package grpcext
-
-import (
- "context"
- "fmt"
-
- "github.com/jhump/protoreflect/desc"
- "github.com/jhump/protoreflect/grpcreflect"
- "google.golang.org/grpc"
- "google.golang.org/protobuf/types/descriptorpb"
-)
-
-// ReflectionClient wraps a grpc.ServerReflectionClient.
-type reflectionClient struct {
- Conn grpc.ClientConnInterface
-}
-
-// Reflect will use the grpc reflection api to make the file descriptors available to request.
-// It is called in the connect function the first time the Client.Connect function is called.
-func (rc *reflectionClient) Reflect(ctx context.Context) (*descriptorpb.FileDescriptorSet, error) {
- client := grpcreflect.NewClientAuto(ctx, rc.Conn)
-
- services, err := client.ListServices()
- if err != nil {
- return nil, fmt.Errorf("can't list services: %w", err)
- }
-
- seen := make(map[fileDescriptorLookupKey]bool, len(services))
- fdset := &descriptorpb.FileDescriptorSet{
- File: make([]*descriptorpb.FileDescriptorProto, 0, len(services)),
- }
-
- for _, srv := range services {
- srvDescriptor, err := client.ResolveService(srv)
- if err != nil {
- return nil, fmt.Errorf("can't get method on service %q: %w", srv, err)
- }
-
- stack := []*desc.FileDescriptor{srvDescriptor.GetFile()}
-
- for len(stack) > 0 {
- fdp := stack[len(stack)-1]
- stack = stack[:len(stack)-1]
-
- fdkey := fileDescriptorLookupKey{
- Package: fdp.GetPackage(),
- Name: fdp.GetName(),
- }
-
- stack = append(stack, fdp.GetDependencies()...)
-
- if seen[fdkey] {
- // When a proto file contains declarations for multiple services
- // then the same proto file is returned multiple times,
- // this prevents adding the returned proto file as a duplicate.
- continue
- }
- seen[fdkey] = true
- fdset.File = append(fdset.File, fdp.AsFileDescriptorProto())
- }
- }
-
- return fdset, nil
-}
-
-type fileDescriptorLookupKey struct {
- Package string
- Name string
-}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 14bb54479a91..008c04ceffd7 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -137,6 +137,7 @@ github.com/golang/protobuf/proto
github.com/golang/protobuf/ptypes
github.com/golang/protobuf/ptypes/any
github.com/golang/protobuf/ptypes/duration
+github.com/golang/protobuf/ptypes/struct
github.com/golang/protobuf/ptypes/timestamp
github.com/golang/protobuf/ptypes/wrappers
# github.com/google/pprof v0.0.0-20230728192033-2ba5b33183c6
@@ -160,10 +161,6 @@ github.com/grafana/xk6-browser/k6ext
github.com/grafana/xk6-browser/keyboardlayout
github.com/grafana/xk6-browser/log
github.com/grafana/xk6-browser/storage
-# github.com/grafana/xk6-grpc v0.1.4-0.20230919144024-6ed5daf33509
-## explicit; go 1.19
-github.com/grafana/xk6-grpc/grpc
-github.com/grafana/xk6-grpc/lib/netext/grpcext
# github.com/grafana/xk6-output-prometheus-remote v0.3.1
## explicit; go 1.18
github.com/grafana/xk6-output-prometheus-remote/pkg/remote