From 686f2c2ec7acf00a7e7a0a6e78eec0ca0baec12f Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 31 Aug 2021 21:24:32 +0800 Subject: [PATCH 01/22] feat: init descriptors from proto --- .../http/grpcproxy/descriptor_operation.go | 114 ++++++++++++ pkg/filter/http/grpcproxy/grpc.go | 173 ++++++++++++++++++ pkg/model/bootstrap.go | 6 + 3 files changed, 293 insertions(+) create mode 100644 pkg/filter/http/grpcproxy/descriptor_operation.go create mode 100644 pkg/filter/http/grpcproxy/grpc.go diff --git a/pkg/filter/http/grpcproxy/descriptor_operation.go b/pkg/filter/http/grpcproxy/descriptor_operation.go new file mode 100644 index 000000000..b8f5810a3 --- /dev/null +++ b/pkg/filter/http/grpcproxy/descriptor_operation.go @@ -0,0 +1,114 @@ +package grpcproxy + +import ( + "fmt" + "sync" + + "github.com/jhump/protoreflect/desc" + "github.com/jhump/protoreflect/desc/protoparse" + "github.com/jhump/protoreflect/dynamic" +) + +type fileSource struct { + files map[string]*desc.FileDescriptor + er *dynamic.ExtensionRegistry + erInit sync.Once +} + +// initFromFileDescriptor +// protoparse.ResolveFilenames(importPaths, fileNames...) +// rel: https://pkg.go.dev/github.com/jhump/protoreflect/desc/protoparse?utm_source=godoc#ResolveFilenames +func (af *Filter) initFromFileDescriptor(importPaths []string, fileNames ...string) error { + fileNames, err := protoparse.ResolveFilenames(importPaths, fileNames...) + if err != nil { + return err + } + p := protoparse.Parser{ + ImportPaths: importPaths, + InferImportPaths: len(importPaths) == 0, + IncludeSourceCodeInfo: true, + } + fds, err := p.ParseFiles(fileNames...) + if err != nil { + return fmt.Errorf("could not parse given files: %v", err) + } + + for _, fd := range fds { + name := fd.GetName() + fs.files[name] = fd + } + + return nil +} + +func DescriptorSourceFromFileDescriptors(files ...*desc.FileDescriptor) (*fileSource, error) { + fds := map[string]*desc.FileDescriptor{} + for _, fd := range files { + if err := addFile(fd, fds); err != nil { + return nil, err + } + } + return &fileSource{files: fds}, nil +} + +func addFile(fd *desc.FileDescriptor, fds map[string]*desc.FileDescriptor) error { + name := fd.GetName() + if existing, ok := fds[name]; ok { + // already added this file + if existing != fd { + // doh! duplicate files provided + return fmt.Errorf("given files include multiple copies of %q", name) + } + return nil + } + fds[name] = fd + for _, dep := range fd.GetDependencies() { + if err := addFile(dep, fds); err != nil { + return err + } + } + return nil +} + +func (fs *fileSource) ListServices() ([]string, error) { + set := map[string]bool{} + for _, fd := range fs.files { + for _, svc := range fd.GetServices() { + set[svc.GetFullyQualifiedName()] = true + } + } + sl := make([]string, 0, len(set)) + for svc := range set { + sl = append(sl, svc) + } + return sl, nil +} + +func (fs *fileSource) GetAllFiles() ([]*desc.FileDescriptor, error) { + files := make([]*desc.FileDescriptor, len(fs.files)) + i := 0 + for _, fd := range fs.files { + files[i] = fd + i++ + } + return files, nil +} + +func (fs *fileSource) FindSymbol(fullyQualifiedName string) (desc.Descriptor, error) { + for _, fd := range fs.files { + if dsc := fd.FindSymbol(fullyQualifiedName); dsc != nil { + return dsc, nil + } + } + return nil, fmt.Errorf("could not found symbol %v", fullyQualifiedName) +} + +func (fs *fileSource) AllExtensionsForType(typeName string) ([]*desc.FieldDescriptor, error) { + fs.erInit.Do(func() { + fs.er = &dynamic.ExtensionRegistry{} + for _, fd := range fs.files { + fs.er.AddExtensionsFromFile(fd) + } + }) + return fs.er.AllExtensionsForType(typeName), nil +} diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go new file mode 100644 index 000000000..2c938436a --- /dev/null +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -0,0 +1,173 @@ +package grpcproxy + +import ( + "encoding/json" + "fmt" + "github.com/gogo/protobuf/proto" + "github.com/golang/protobuf/jsonpb" + "github.com/jhump/protoreflect/desc" + "github.com/jhump/protoreflect/dynamic" + "io" + stdHttp "net/http" + "strings" + + "github.com/apache/dubbo-go-pixiu/pkg/common/constant" + "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" + "github.com/apache/dubbo-go-pixiu/pkg/config" + "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" +) + +const ( + // Kind is the kind of Fallback. + Kind = constant.HTTPGrpcProxyFilter +) + +var ( + fs fileSource +) + +func init() { + filter.RegisterHttpFilter(&Plugin{}) +} + +type ( + // Plugin is grpc filter plugin. + Plugin struct { + } + + // Filter is grpc filter instance + Filter struct { + cfg *Config + } + + // Config describe the config of AccessFilter + Config struct { + Proto string `yaml:"proto_descriptor" json:"proto_descriptor"` + rules []*Rule `yaml:"rules" json:"rules"` + } + + Rule struct { + Selector string `yaml:"selector" json:"selector"` + Match Match `yaml:"match" json:"match"` + } + + Match struct { + method string `yaml:"method" json:"method"` + } +) + +func (ap *Plugin) Kind() string { + return Kind +} + +func (ap *Plugin) CreateFilter() (filter.HttpFilter, error) { + return &Filter{cfg: &Config{}}, nil +} + +func (af *Filter) PrepareFilterChain(ctx *http.HttpContext) error { + ctx.AppendFilterFunc(af.Handle) + return nil +} + +// Handle use the default http to grpc transcoding strategy https://cloud.google.com/endpoints/docs/grpc/transcoding +func (af *Filter) Handle(c *http.HttpContext) { + paths := strings.Split(c.Request.URL.Path, ",") + if len(paths) < 2 { + writeResp(c, stdHttp.StatusBadRequest, "request path invalid") + return + } + svc := paths[0] + mth := paths[1] + + dscp, err := fs.FindSymbol(svc + "." + mth) + if err != nil { + writeResp(c, stdHttp.StatusMethodNotAllowed, "method not found") + return + } + + svcDesc, ok := dscp.(*desc.ServiceDescriptor) + if !ok { + writeResp(c, stdHttp.StatusMethodNotAllowed, fmt.Sprintf("service not expose, %s", svc)) + return + } + + mthDesc := svcDesc.FindMethodByName(mth) + + var extReg dynamic.ExtensionRegistry + registered := make(map[string]bool) + err = RegisterExtension(&extReg, mthDesc.GetInputType(), registered) + if err != nil { + writeResp(c, stdHttp.StatusInternalServerError, "register extension failed") + return + } + + err = RegisterExtension(&extReg, mthDesc.GetOutputType(), registered) + if err != nil { + writeResp(c, stdHttp.StatusInternalServerError, "register extension failed") + return + } + + msgFac := dynamic.NewMessageFactoryWithExtensionRegistry(&extReg) + grpcReq := msgFac.NewMessage(mthDesc.GetInputType()) + + err = jsonToProtoMsg(c.Request.Body, grpcReq) + if err != nil { + writeResp(c, stdHttp.StatusBadRequest, "convert to proto msg failed") + return + } + +} + +func RegisterExtension(extReg *dynamic.ExtensionRegistry, msgDesc *desc.MessageDescriptor, registered map[string]bool) error { + msgType := msgDesc.GetFullyQualifiedName() + if _, ok := registered[msgType]; ok { + return nil + } + + if len(msgDesc.GetExtensionRanges()) > 0 { + fds, err := fs.AllExtensionsForType(msgType) + if err != nil { + return fmt.Errorf("failed to find msg type {%s} in file source", msgType) + } + + err = extReg.AddExtension(fds...) + if err != nil { + return fmt.Errorf("failed to register extensions of msgType {%s}, err is {%s}", msgType, err.Error()) + } + } + + for _, fd := range msgDesc.GetFields() { + if fd.GetMessageType() != nil { + err := RegisterExtension(extReg, fd.GetMessageType(), registered) + if err != nil { + return err + } + } + } + + return nil +} + +func jsonToProtoMsg(reader io.Reader, msg proto.Message) error { + return jsonpb.Unmarshal(reader, msg) +} + +func writeResp(c *http.HttpContext, code int, msg string) { + resp, _ := json.Marshal(http.ErrResponse{Message: msg}) + c.WriteJSONWithStatus(code, resp) +} + +func (af *Filter) Config() interface{} { + return af.cfg +} + +func (af *Filter) Apply() error { + gc := config.GetBootstrap().StaticResources.GRPCConfig + err := af.initFromFileDescriptor([]string{gc.ProtoPath}, gc.FileNames...) + if err != nil { + logger.Errorf("failed to init proto files, %s", err.Error()) + } + + return nil +} diff --git a/pkg/model/bootstrap.go b/pkg/model/bootstrap.go index 52b415b67..431a99194 100644 --- a/pkg/model/bootstrap.go +++ b/pkg/model/bootstrap.go @@ -60,6 +60,7 @@ type StaticResources struct { TimeoutConfig TimeoutConfig `yaml:"timeout_config" json:"timeout_config" mapstructure:"timeout_config"` ShutdownConfig *ShutdownConfig `yaml:"shutdown_config" json:"shutdown_config" mapstructure:"shutdown_config"` PprofConf PprofConf `yaml:"pprofConf" json:"pprofConf" mapstructure:"pprofConf"` + GRPCConfig GRPCConfig `yaml:"grpc_config" json:"grpc_config" mapstructure:"grpc_config"` } // DynamicResources TODO @@ -83,3 +84,8 @@ type TimeoutConfig struct { ConnectTimeoutStr string `yaml:"connect_timeout" json:"connect_timeout,omitempty"` // ConnectTimeout timeout for connect to cluster node RequestTimeoutStr string `yaml:"request_timeout" json:"request_timeout,omitempty"` } + +type GRPCConfig struct { + ProtoPath string `yaml:"proto_path" json:"proto_path"` + FileNames []string `yaml:"file_names" json:"file_names"` +} From c54705d766dad186bf7ab9767e1559f0fbbe8b8c Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Wed, 1 Sep 2021 01:47:05 +0800 Subject: [PATCH 02/22] feat: invoke implement --- pkg/filter/http/grpcproxy/grpc.go | 50 +++++++++++++++++++- pkg/filter/http/grpcproxy/invoke.go | 71 +++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 pkg/filter/http/grpcproxy/invoke.go diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index 2c938436a..b7c98fee0 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -1,15 +1,21 @@ package grpcproxy import ( + "context" "encoding/json" "fmt" "github.com/gogo/protobuf/proto" "github.com/golang/protobuf/jsonpb" "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/dynamic" + "github.com/jhump/protoreflect/dynamic/grpcdynamic" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "io" stdHttp "net/http" "strings" + "sync" "github.com/apache/dubbo-go-pixiu/pkg/common/constant" "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" @@ -39,6 +45,8 @@ type ( // Filter is grpc filter instance Filter struct { cfg *Config + // hold grpc.ClientConns + pool sync.Pool } // Config describe the config of AccessFilter @@ -72,7 +80,7 @@ func (af *Filter) PrepareFilterChain(ctx *http.HttpContext) error { // Handle use the default http to grpc transcoding strategy https://cloud.google.com/endpoints/docs/grpc/transcoding func (af *Filter) Handle(c *http.HttpContext) { - paths := strings.Split(c.Request.URL.Path, ",") + paths := strings.Split(c.Request.URL.Path, "/") if len(paths) < 2 { writeResp(c, stdHttp.StatusBadRequest, "request path invalid") return @@ -94,6 +102,7 @@ func (af *Filter) Handle(c *http.HttpContext) { mthDesc := svcDesc.FindMethodByName(mth) + // TODO(Kenway): Can extension registry being cached ? var extReg dynamic.ExtensionRegistry registered := make(map[string]bool) err = RegisterExtension(&extReg, mthDesc.GetInputType(), registered) @@ -117,6 +126,35 @@ func (af *Filter) Handle(c *http.HttpContext) { return } + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + var clientConn *grpc.ClientConn + clientConn = af.pool.Get().(*grpc.ClientConn) + if clientConn == nil { + // TODO(Kenway): Support Credential and TLS + clientConn, err = grpc.DialContext(ctx, c.GetAPI().IntegrationRequest.Host, grpc.WithInsecure()) + if err != nil || clientConn == nil { + writeResp(c, stdHttp.StatusServiceUnavailable, "connect to grpc server failed") + return + } + af.pool.Put(clientConn) + } + + stub := grpcdynamic.NewStubWithMessageFactory(clientConn, msgFac) + + resp, err := Invoke(ctx, stub, mthDesc, grpcReq) + if st, ok := status.FromError(err); !ok || isServerError(st) { + writeResp(c, stdHttp.StatusInternalServerError, err.Error()) + return + } + + res, err := protoMsgToJson(resp) + if err != nil { + writeResp(c, stdHttp.StatusInternalServerError, "serialize proto msg to json failed") + return + } + writeResp(c, stdHttp.StatusOK, res) } func RegisterExtension(extReg *dynamic.ExtensionRegistry, msgDesc *desc.MessageDescriptor, registered map[string]bool) error { @@ -153,11 +191,21 @@ func jsonToProtoMsg(reader io.Reader, msg proto.Message) error { return jsonpb.Unmarshal(reader, msg) } +func protoMsgToJson(msg proto.Message) (string, error) { + m := jsonpb.Marshaler{} + return m.MarshalToString(msg) +} + func writeResp(c *http.HttpContext, code int, msg string) { resp, _ := json.Marshal(http.ErrResponse{Message: msg}) c.WriteJSONWithStatus(code, resp) } +func isServerError(st *status.Status) bool { + return st.Code() == codes.DeadlineExceeded || st.Code() == codes.ResourceExhausted || st.Code() == codes.Internal || + st.Code() == codes.Unavailable +} + func (af *Filter) Config() interface{} { return af.cfg } diff --git a/pkg/filter/http/grpcproxy/invoke.go b/pkg/filter/http/grpcproxy/invoke.go new file mode 100644 index 000000000..0f4123e69 --- /dev/null +++ b/pkg/filter/http/grpcproxy/invoke.go @@ -0,0 +1,71 @@ +package grpcproxy + +import ( + "context" + "github.com/golang/protobuf/proto" + "github.com/jhump/protoreflect/desc" + "github.com/jhump/protoreflect/dynamic/grpcdynamic" + "google.golang.org/grpc/status" +) + +func Invoke(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message) (proto.Message, error) { + var resp proto.Message + var err error + // TODO(Kenway): use a better way to use bi-direction stream + // Bi-direction Stream + if mthDesc.IsServerStreaming() && mthDesc.IsClientStreaming() { + resp, err = invokeBiDirectionStream(ctx, stub, mthDesc, grpcReq) + } else if mthDesc.IsClientStreaming() { + resp, err = invokeClientStream(ctx, stub, mthDesc, grpcReq) + } else if mthDesc.IsServerStreaming() { + resp, err = invokeServerStream(ctx, stub, mthDesc, grpcReq) + } else { + resp, err = invokeUnary(ctx, stub, mthDesc, grpcReq) + } + + return resp, err +} + +func invokeBiDirectionStream(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message) (proto.Message, error) { + stream, err := stub.InvokeRpcBidiStream(ctx, mthDesc) + if _, ok := status.FromError(err); !ok { + return nil, err + } + defer func(stream *grpcdynamic.BidiStream) { + _ = stream.CloseSend() + }(stream) + + err = stream.SendMsg(grpcReq) + if _, ok := status.FromError(err); !ok { + return nil, err + } + + return stream.RecvMsg() +} + +func invokeClientStream(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message) (proto.Message, error) { + stream, err := stub.InvokeRpcClientStream(ctx, mthDesc) + if _, ok := status.FromError(err); !ok { + return nil, err + } + + err = stream.SendMsg(grpcReq) + if _, ok := status.FromError(err); !ok { + return nil, err + } + + return stream.CloseAndReceive() +} + +func invokeServerStream(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message) (proto.Message, error) { + stream, err := stub.InvokeRpcServerStream(ctx, mthDesc, grpcReq) + if _, ok := status.FromError(err); !ok { + return nil, err + } + + return stream.RecvMsg() +} + +func invokeUnary(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message) (proto.Message, error) { + return stub.InvokeRpc(ctx, mthDesc, grpcReq) +} From 60558597d163d244f20a873375e202ea9661e780 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Wed, 1 Sep 2021 13:54:09 +0800 Subject: [PATCH 03/22] refactor: use config in filter --- .../http/grpcproxy/descriptor_operation.go | 19 ++++++- pkg/filter/http/grpcproxy/grpc.go | 56 +++++++++++++++---- pkg/model/bootstrap.go | 6 -- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/pkg/filter/http/grpcproxy/descriptor_operation.go b/pkg/filter/http/grpcproxy/descriptor_operation.go index b8f5810a3..1e1db3cfe 100644 --- a/pkg/filter/http/grpcproxy/descriptor_operation.go +++ b/pkg/filter/http/grpcproxy/descriptor_operation.go @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + package grpcproxy import ( @@ -35,7 +52,7 @@ func (af *Filter) initFromFileDescriptor(importPaths []string, fileNames ...stri for _, fd := range fds { name := fd.GetName() - fs.files[name] = fd + fsrc.files[name] = fd } return nil diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index b7c98fee0..4f2ed3149 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + package grpcproxy import ( @@ -13,15 +30,15 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "io" + "io/fs" stdHttp "net/http" + "path/filepath" "strings" "sync" "github.com/apache/dubbo-go-pixiu/pkg/common/constant" "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" - "github.com/apache/dubbo-go-pixiu/pkg/config" "github.com/apache/dubbo-go-pixiu/pkg/context/http" - "github.com/apache/dubbo-go-pixiu/pkg/logger" ) const ( @@ -30,7 +47,7 @@ const ( ) var ( - fs fileSource + fsrc fileSource ) func init() { @@ -51,7 +68,7 @@ type ( // Config describe the config of AccessFilter Config struct { - Proto string `yaml:"proto_descriptor" json:"proto_descriptor"` + Path string `yaml:"path" json:"path"` rules []*Rule `yaml:"rules" json:"rules"` } @@ -88,7 +105,7 @@ func (af *Filter) Handle(c *http.HttpContext) { svc := paths[0] mth := paths[1] - dscp, err := fs.FindSymbol(svc + "." + mth) + dscp, err := fsrc.FindSymbol(svc + "." + mth) if err != nil { writeResp(c, stdHttp.StatusMethodNotAllowed, "method not found") return @@ -138,7 +155,6 @@ func (af *Filter) Handle(c *http.HttpContext) { writeResp(c, stdHttp.StatusServiceUnavailable, "connect to grpc server failed") return } - af.pool.Put(clientConn) } stub := grpcdynamic.NewStubWithMessageFactory(clientConn, msgFac) @@ -155,6 +171,7 @@ func (af *Filter) Handle(c *http.HttpContext) { return } writeResp(c, stdHttp.StatusOK, res) + af.pool.Put(clientConn) } func RegisterExtension(extReg *dynamic.ExtensionRegistry, msgDesc *desc.MessageDescriptor, registered map[string]bool) error { @@ -164,7 +181,7 @@ func RegisterExtension(extReg *dynamic.ExtensionRegistry, msgDesc *desc.MessageD } if len(msgDesc.GetExtensionRanges()) > 0 { - fds, err := fs.AllExtensionsForType(msgType) + fds, err := fsrc.AllExtensionsForType(msgType) if err != nil { return fmt.Errorf("failed to find msg type {%s} in file source", msgType) } @@ -211,11 +228,28 @@ func (af *Filter) Config() interface{} { } func (af *Filter) Apply() error { - gc := config.GetBootstrap().StaticResources.GRPCConfig - err := af.initFromFileDescriptor([]string{gc.ProtoPath}, gc.FileNames...) + gc := af.cfg + fileLists := make([]string, 0) + err := filepath.Walk(gc.Path, func(path string, info fs.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() { + sp := strings.Split(info.Name(), ".") + length := len(sp) + if length >= 2 && sp[length-1] == "proto" { + fileLists = append(fileLists, info.Name()) + } + } + return nil + }) if err != nil { - logger.Errorf("failed to init proto files, %s", err.Error()) + return err + } + err = af.initFromFileDescriptor([]string{gc.Path}, fileLists...) + if err != nil { + return err } - return nil } diff --git a/pkg/model/bootstrap.go b/pkg/model/bootstrap.go index 431a99194..52b415b67 100644 --- a/pkg/model/bootstrap.go +++ b/pkg/model/bootstrap.go @@ -60,7 +60,6 @@ type StaticResources struct { TimeoutConfig TimeoutConfig `yaml:"timeout_config" json:"timeout_config" mapstructure:"timeout_config"` ShutdownConfig *ShutdownConfig `yaml:"shutdown_config" json:"shutdown_config" mapstructure:"shutdown_config"` PprofConf PprofConf `yaml:"pprofConf" json:"pprofConf" mapstructure:"pprofConf"` - GRPCConfig GRPCConfig `yaml:"grpc_config" json:"grpc_config" mapstructure:"grpc_config"` } // DynamicResources TODO @@ -84,8 +83,3 @@ type TimeoutConfig struct { ConnectTimeoutStr string `yaml:"connect_timeout" json:"connect_timeout,omitempty"` // ConnectTimeout timeout for connect to cluster node RequestTimeoutStr string `yaml:"request_timeout" json:"request_timeout,omitempty"` } - -type GRPCConfig struct { - ProtoPath string `yaml:"proto_path" json:"proto_path"` - FileNames []string `yaml:"file_names" json:"file_names"` -} From fdf382a19c5b0a509511e98edf87597d32d1aa01 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Wed, 1 Sep 2021 16:00:16 +0800 Subject: [PATCH 04/22] refactor: remove response by throw err to context --- pkg/filter/http/grpcproxy/grpc.go | 47 ++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index 4f2ed3149..787922f4b 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -19,19 +19,19 @@ package grpcproxy import ( "context" - "encoding/json" "fmt" + "github.com/apache/dubbo-go-pixiu/pkg/logger" "github.com/gogo/protobuf/proto" "github.com/golang/protobuf/jsonpb" "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/dynamic" "github.com/jhump/protoreflect/dynamic/grpcdynamic" + perrors "github.com/pkg/errors" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "io" "io/fs" - stdHttp "net/http" "path/filepath" "strings" "sync" @@ -44,6 +44,8 @@ import ( const ( // Kind is the kind of Fallback. Kind = constant.HTTPGrpcProxyFilter + + loggerHeader = "[grpc-proxy]" ) var ( @@ -99,7 +101,9 @@ func (af *Filter) PrepareFilterChain(ctx *http.HttpContext) error { func (af *Filter) Handle(c *http.HttpContext) { paths := strings.Split(c.Request.URL.Path, "/") if len(paths) < 2 { - writeResp(c, stdHttp.StatusBadRequest, "request path invalid") + logger.Errorf("%s request from %s, err {%}", loggerHeader, c, "request path invalid") + c.Err = perrors.New("request path invalid") + c.Next() return } svc := paths[0] @@ -107,13 +111,16 @@ func (af *Filter) Handle(c *http.HttpContext) { dscp, err := fsrc.FindSymbol(svc + "." + mth) if err != nil { - writeResp(c, stdHttp.StatusMethodNotAllowed, "method not found") + logger.Errorf("%s request from %s, err {%}", loggerHeader, c.Request.RequestURI, "request path invalid") + c.Err = perrors.New("method not allow") + c.Next() return } svcDesc, ok := dscp.(*desc.ServiceDescriptor) if !ok { - writeResp(c, stdHttp.StatusMethodNotAllowed, fmt.Sprintf("service not expose, %s", svc)) + c.Err = perrors.New(fmt.Sprintf("service not expose, %s", svc)) + c.Next() return } @@ -124,13 +131,15 @@ func (af *Filter) Handle(c *http.HttpContext) { registered := make(map[string]bool) err = RegisterExtension(&extReg, mthDesc.GetInputType(), registered) if err != nil { - writeResp(c, stdHttp.StatusInternalServerError, "register extension failed") + c.Err = perrors.New("register extension failed") + c.Next() return } err = RegisterExtension(&extReg, mthDesc.GetOutputType(), registered) if err != nil { - writeResp(c, stdHttp.StatusInternalServerError, "register extension failed") + c.Err = perrors.New("register extension failed") + c.Next() return } @@ -139,7 +148,9 @@ func (af *Filter) Handle(c *http.HttpContext) { err = jsonToProtoMsg(c.Request.Body, grpcReq) if err != nil { - writeResp(c, stdHttp.StatusBadRequest, "convert to proto msg failed") + logger.Errorf("failed to convert json to proto msg, %s", err.Error()) + c.Err = err + c.Next() return } @@ -152,7 +163,8 @@ func (af *Filter) Handle(c *http.HttpContext) { // TODO(Kenway): Support Credential and TLS clientConn, err = grpc.DialContext(ctx, c.GetAPI().IntegrationRequest.Host, grpc.WithInsecure()) if err != nil || clientConn == nil { - writeResp(c, stdHttp.StatusServiceUnavailable, "connect to grpc server failed") + logger.Error("failed to connect to grpc service provider") + c.Err = err return } } @@ -161,17 +173,23 @@ func (af *Filter) Handle(c *http.HttpContext) { resp, err := Invoke(ctx, stub, mthDesc, grpcReq) if st, ok := status.FromError(err); !ok || isServerError(st) { - writeResp(c, stdHttp.StatusInternalServerError, err.Error()) + logger.Error("failed to invoke grpc service provider, %s", err.Error()) + c.Err = err + c.Next() return } res, err := protoMsgToJson(resp) if err != nil { - writeResp(c, stdHttp.StatusInternalServerError, "serialize proto msg to json failed") + c.Err = err + c.Next() return } - writeResp(c, stdHttp.StatusOK, res) + + // let response filter handle resp + c.SourceResp = res af.pool.Put(clientConn) + c.Next() } func RegisterExtension(extReg *dynamic.ExtensionRegistry, msgDesc *desc.MessageDescriptor, registered map[string]bool) error { @@ -213,11 +231,6 @@ func protoMsgToJson(msg proto.Message) (string, error) { return m.MarshalToString(msg) } -func writeResp(c *http.HttpContext, code int, msg string) { - resp, _ := json.Marshal(http.ErrResponse{Message: msg}) - c.WriteJSONWithStatus(code, resp) -} - func isServerError(st *status.Status) bool { return st.Code() == codes.DeadlineExceeded || st.Code() == codes.ResourceExhausted || st.Code() == codes.Internal || st.Code() == codes.Unavailable From 38ff6c6b0c816f691be47e23aea939314037de8d Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Wed, 1 Sep 2021 16:35:06 +0800 Subject: [PATCH 05/22] feat: add log info --- pkg/filter/http/grpcproxy/grpc.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index 787922f4b..ea8dfed13 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -101,7 +101,7 @@ func (af *Filter) PrepareFilterChain(ctx *http.HttpContext) error { func (af *Filter) Handle(c *http.HttpContext) { paths := strings.Split(c.Request.URL.Path, "/") if len(paths) < 2 { - logger.Errorf("%s request from %s, err {%}", loggerHeader, c, "request path invalid") + logger.Errorf("%s err {%s}", loggerHeader, "request path invalid") c.Err = perrors.New("request path invalid") c.Next() return @@ -111,7 +111,7 @@ func (af *Filter) Handle(c *http.HttpContext) { dscp, err := fsrc.FindSymbol(svc + "." + mth) if err != nil { - logger.Errorf("%s request from %s, err {%}", loggerHeader, c.Request.RequestURI, "request path invalid") + logger.Errorf("%s err {%s}", loggerHeader, "request path invalid") c.Err = perrors.New("method not allow") c.Next() return @@ -119,6 +119,7 @@ func (af *Filter) Handle(c *http.HttpContext) { svcDesc, ok := dscp.(*desc.ServiceDescriptor) if !ok { + logger.Errorf("%s err {service not expose, %s}", loggerHeader, svc) c.Err = perrors.New(fmt.Sprintf("service not expose, %s", svc)) c.Next() return @@ -131,6 +132,7 @@ func (af *Filter) Handle(c *http.HttpContext) { registered := make(map[string]bool) err = RegisterExtension(&extReg, mthDesc.GetInputType(), registered) if err != nil { + logger.Errorf("%s err {%s}", loggerHeader, "register extension failed") c.Err = perrors.New("register extension failed") c.Next() return @@ -138,6 +140,7 @@ func (af *Filter) Handle(c *http.HttpContext) { err = RegisterExtension(&extReg, mthDesc.GetOutputType(), registered) if err != nil { + logger.Errorf("%s err {%s}", loggerHeader, "register extension failed") c.Err = perrors.New("register extension failed") c.Next() return @@ -148,7 +151,7 @@ func (af *Filter) Handle(c *http.HttpContext) { err = jsonToProtoMsg(c.Request.Body, grpcReq) if err != nil { - logger.Errorf("failed to convert json to proto msg, %s", err.Error()) + logger.Errorf("%s err {failed to convert json to proto msg, %s}", loggerHeader, err.Error()) c.Err = err c.Next() return @@ -158,13 +161,16 @@ func (af *Filter) Handle(c *http.HttpContext) { defer cancel() var clientConn *grpc.ClientConn + re := c.GetRouteEntry() + logger.Debugf("%s client choose endpoint from cluster :%v", loggerHeader, re.Cluster) clientConn = af.pool.Get().(*grpc.ClientConn) if clientConn == nil { // TODO(Kenway): Support Credential and TLS - clientConn, err = grpc.DialContext(ctx, c.GetAPI().IntegrationRequest.Host, grpc.WithInsecure()) + clientConn, err = grpc.DialContext(ctx, re.Cluster, grpc.WithInsecure()) if err != nil || clientConn == nil { - logger.Error("failed to connect to grpc service provider") + logger.Errorf("%s err {failed to connect to grpc service provider}", loggerHeader) c.Err = err + c.Next() return } } @@ -173,7 +179,7 @@ func (af *Filter) Handle(c *http.HttpContext) { resp, err := Invoke(ctx, stub, mthDesc, grpcReq) if st, ok := status.FromError(err); !ok || isServerError(st) { - logger.Error("failed to invoke grpc service provider, %s", err.Error()) + logger.Error("%s err {failed to invoke grpc service provider, %s}", loggerHeader, err.Error()) c.Err = err c.Next() return @@ -181,6 +187,7 @@ func (af *Filter) Handle(c *http.HttpContext) { res, err := protoMsgToJson(resp) if err != nil { + logger.Error("%s err {failed to convert proto msg to json, %s}", loggerHeader, err.Error()) c.Err = err c.Next() return From e96792d01e3a70dff7a50440bc20c72fc7c3da5c Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Wed, 1 Sep 2021 16:38:39 +0800 Subject: [PATCH 06/22] feat: remote stream invoke support --- pkg/filter/http/grpcproxy/invoke.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg/filter/http/grpcproxy/invoke.go b/pkg/filter/http/grpcproxy/invoke.go index 0f4123e69..e7b5d9e17 100644 --- a/pkg/filter/http/grpcproxy/invoke.go +++ b/pkg/filter/http/grpcproxy/invoke.go @@ -5,6 +5,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/dynamic/grpcdynamic" + perrors "github.com/pkg/errors" "google.golang.org/grpc/status" ) @@ -14,11 +15,14 @@ func Invoke(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDesc // TODO(Kenway): use a better way to use bi-direction stream // Bi-direction Stream if mthDesc.IsServerStreaming() && mthDesc.IsClientStreaming() { - resp, err = invokeBiDirectionStream(ctx, stub, mthDesc, grpcReq) + err = perrors.New("currently not support bi-direction stream") + //resp, err = invokeBiDirectionStream(ctx, stub, mthDesc, grpcReq) } else if mthDesc.IsClientStreaming() { - resp, err = invokeClientStream(ctx, stub, mthDesc, grpcReq) + err = perrors.New("currently not support client side stream") + //resp, err = invokeClientStream(ctx, stub, mthDesc, grpcReq) } else if mthDesc.IsServerStreaming() { - resp, err = invokeServerStream(ctx, stub, mthDesc, grpcReq) + err = perrors.New("currently not support server side stream") + //resp, err = invokeServerStream(ctx, stub, mthDesc, grpcReq) } else { resp, err = invokeUnary(ctx, stub, mthDesc, grpcReq) } From 12987dc91158ffe9b06952ed23161961c978aafb Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Sun, 5 Sep 2021 22:18:42 +0800 Subject: [PATCH 07/22] feat: grpc example --- go.mod | 9 +- go.sum | 8 + pkg/filter/http/grpcproxy/grpc.go | 6 +- pkg/filter/http/grpcproxy/invoke.go | 17 + samples/http/grpc/pixiu/api_config.yaml | 35 ++ samples/http/grpc/pixiu/conf.yaml | 72 ++++ samples/http/grpc/proto/hello_grpc.pb.go | 320 ++++++++++++++++++ samples/http/grpc/proto/hello_grpc.proto | 40 +++ samples/http/grpc/proto/hello_grpc_grpc.pb.go | 97 ++++++ samples/http/grpc/server/main.go | 81 +++++ 10 files changed, 679 insertions(+), 6 deletions(-) create mode 100644 samples/http/grpc/pixiu/api_config.yaml create mode 100644 samples/http/grpc/pixiu/conf.yaml create mode 100644 samples/http/grpc/proto/hello_grpc.pb.go create mode 100644 samples/http/grpc/proto/hello_grpc.proto create mode 100644 samples/http/grpc/proto/hello_grpc_grpc.pb.go create mode 100644 samples/http/grpc/server/main.go diff --git a/go.mod b/go.mod index 9f552ba95..66ae94310 100644 --- a/go.mod +++ b/go.mod @@ -11,10 +11,12 @@ require ( github.com/dubbogo/gost v1.11.14 github.com/emirpasic/gods v1.12.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 + github.com/gogo/protobuf v1.3.2 github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9 - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.2 github.com/google/uuid v1.2.0 // indirect github.com/hashicorp/consul/api v1.5.0 + github.com/jhump/protoreflect v1.9.0 github.com/mitchellh/mapstructure v1.4.1 github.com/pkg/errors v0.9.1 github.com/prometheus/common v0.29.0 // indirect @@ -33,12 +35,13 @@ require ( go.uber.org/zap v1.16.0 golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc // indirect golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect - google.golang.org/protobuf v1.27.1 // indirect + google.golang.org/grpc v1.27.0 + google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 ) replace ( - github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 => github.com/envoyproxy/go-control-plane v0.8.0 + github.com/envoyproxy/go-control-plane => github.com/envoyproxy/go-control-plane v0.8.0 google.golang.org/api => google.golang.org/api v0.13.0 google.golang.org/grpc => google.golang.org/grpc v1.27.0 ) diff --git a/go.sum b/go.sum index b6bd9a275..6e31c9698 100644 --- a/go.sum +++ b/go.sum @@ -349,6 +349,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -387,6 +388,7 @@ github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEo github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= @@ -518,6 +520,8 @@ github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGk github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= +github.com/jhump/protoreflect v1.9.0 h1:npqHz788dryJiR/l6K/RUQAyh2SwV91+d1dnh4RjO9w= +github.com/jhump/protoreflect v1.9.0/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 h1:mGIXW/lubQ4B+3bXTLxcTMTjUNDqoF6T/HUW9LbFx9s= github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= @@ -648,6 +652,7 @@ github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -1163,8 +1168,10 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201014170642-d1624618ad65/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= @@ -1229,6 +1236,7 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index ea8dfed13..ef3ff810d 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -100,14 +100,14 @@ func (af *Filter) PrepareFilterChain(ctx *http.HttpContext) error { // Handle use the default http to grpc transcoding strategy https://cloud.google.com/endpoints/docs/grpc/transcoding func (af *Filter) Handle(c *http.HttpContext) { paths := strings.Split(c.Request.URL.Path, "/") - if len(paths) < 2 { + if len(paths) < 4 { logger.Errorf("%s err {%s}", loggerHeader, "request path invalid") c.Err = perrors.New("request path invalid") c.Next() return } - svc := paths[0] - mth := paths[1] + svc := paths[2] + mth := paths[3] dscp, err := fsrc.FindSymbol(svc + "." + mth) if err != nil { diff --git a/pkg/filter/http/grpcproxy/invoke.go b/pkg/filter/http/grpcproxy/invoke.go index e7b5d9e17..cffaf817b 100644 --- a/pkg/filter/http/grpcproxy/invoke.go +++ b/pkg/filter/http/grpcproxy/invoke.go @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + package grpcproxy import ( diff --git a/samples/http/grpc/pixiu/api_config.yaml b/samples/http/grpc/pixiu/api_config.yaml new file mode 100644 index 000000000..db91c8252 --- /dev/null +++ b/samples/http/grpc/pixiu/api_config.yaml @@ -0,0 +1,35 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# +name: pixiu +description: pixiu sample +resources: + - path: '/api/v1/provider.UserProvider/GetUser' + type: restful + description: user + methods: + - httpVerb: GET + enable: true + timeout: 1000ms + inboundRequest: + requestType: http + integrationRequest: + requestType: grpc + group: "test" + version: 1.0.0 + clusterName: "test_grpc" \ No newline at end of file diff --git a/samples/http/grpc/pixiu/conf.yaml b/samples/http/grpc/pixiu/conf.yaml new file mode 100644 index 000000000..19e3ec9bf --- /dev/null +++ b/samples/http/grpc/pixiu/conf.yaml @@ -0,0 +1,72 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# +--- +static_resources: + listeners: + - name: "net/http" + address: + socket_address: + protocol_type: "HTTP" + address: "0.0.0.0" + port: 8881 + filter_chains: + - filter_chain_match: + domains: + - api.dubbo.com + - api.pixiu.com + filters: + - name: dgp.filter.httpconnectionmanager + config: + route_config: + routes: + - match: + prefix: "/api/v1" + route: + cluster: "test-grpc" + cluster_not_found_response_code: 505 + http_filters: + - name: dgp.filter.http.apiconfig + config: + path: /mnt/d/WorkSpace/GoLandProjects/dubbo-go-pixiu/samples/http/grpc/pixiu/api_config.yaml + - name: dgp.filter.http.grpcproxy + config: + path: /mnt/d/WorkSpace/GoLandProjects/dubbo-go-pixiu/samples/http/grpc/proto + - name: dgp.filter.http.response + config: + server_name: "test-http-grpc" + generate_request_id: false + config: + idle_timeout: 5s + read_timeout: 5s + write_timeout: 5s + clusters: + - name: "test-grpc" + lb_policy: "RoundRobin" + endpoints: + - socket_address: + address: 127.0.0.1 + port: 50001 + protocol_type: "GRPC" + timeout_config: + connect_timeout: "5s" + request_timeout: "10s" + shutdown_config: + timeout: "60s" + step_timeout: "10s" + reject_policy: "immediacy" \ No newline at end of file diff --git a/samples/http/grpc/proto/hello_grpc.pb.go b/samples/http/grpc/proto/hello_grpc.pb.go new file mode 100644 index 000000000..ac6a8d32d --- /dev/null +++ b/samples/http/grpc/proto/hello_grpc.pb.go @@ -0,0 +1,320 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You 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. + +// protoc --proto_path=. --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative .\hello_grpc.proto + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.20.1 +// protoc v3.15.0 +// source: hello_grpc.proto + +package proto + +import ( + proto "github.com/golang/protobuf/proto" + 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) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type GetUserRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId int32 `protobuf:"varint,1,opt,name=userId,proto3" json:"userId,omitempty"` +} + +func (x *GetUserRequest) Reset() { + *x = GetUserRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_hello_grpc_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUserRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserRequest) ProtoMessage() {} + +func (x *GetUserRequest) ProtoReflect() protoreflect.Message { + mi := &file_hello_grpc_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 GetUserRequest.ProtoReflect.Descriptor instead. +func (*GetUserRequest) Descriptor() ([]byte, []int) { + return file_hello_grpc_proto_rawDescGZIP(), []int{0} +} + +func (x *GetUserRequest) GetUserId() int32 { + if x != nil { + return x.UserId + } + return 0 +} + +type GetUserResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Users []*User `protobuf:"bytes,2,rep,name=users,proto3" json:"users,omitempty"` +} + +func (x *GetUserResponse) Reset() { + *x = GetUserResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_hello_grpc_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetUserResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetUserResponse) ProtoMessage() {} + +func (x *GetUserResponse) ProtoReflect() protoreflect.Message { + mi := &file_hello_grpc_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 GetUserResponse.ProtoReflect.Descriptor instead. +func (*GetUserResponse) Descriptor() ([]byte, []int) { + return file_hello_grpc_proto_rawDescGZIP(), []int{1} +} + +func (x *GetUserResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *GetUserResponse) GetUsers() []*User { + if x != nil { + return x.Users + } + return nil +} + +type User struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + UserId int32 `protobuf:"varint,1,opt,name=userId,proto3" json:"userId,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *User) Reset() { + *x = User{} + if protoimpl.UnsafeEnabled { + mi := &file_hello_grpc_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_hello_grpc_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 User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_hello_grpc_proto_rawDescGZIP(), []int{2} +} + +func (x *User) GetUserId() int32 { + if x != nil { + return x.UserId + } + return 0 +} + +func (x *User) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +var File_hello_grpc_proto protoreflect.FileDescriptor + +var file_hello_grpc_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x5f, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x22, 0x28, 0x0a, 0x0e, + 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, + 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, + 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x51, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x05, 0x75, 0x73, 0x65, 0x72, 0x73, 0x22, 0x32, 0x0a, 0x04, 0x55, 0x73, 0x65, + 0x72, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x32, 0x4e, 0x0a, + 0x0c, 0x55, 0x73, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x3e, 0x0a, + 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x2e, 0x47, 0x65, + 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3a, 0x5a, + 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, + 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x67, 0x6f, 0x2d, 0x70, 0x69, 0x78, 0x69, + 0x75, 0x2f, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x67, + 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, +} + +var ( + file_hello_grpc_proto_rawDescOnce sync.Once + file_hello_grpc_proto_rawDescData = file_hello_grpc_proto_rawDesc +) + +func file_hello_grpc_proto_rawDescGZIP() []byte { + file_hello_grpc_proto_rawDescOnce.Do(func() { + file_hello_grpc_proto_rawDescData = protoimpl.X.CompressGZIP(file_hello_grpc_proto_rawDescData) + }) + return file_hello_grpc_proto_rawDescData +} + +var file_hello_grpc_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_hello_grpc_proto_goTypes = []interface{}{ + (*GetUserRequest)(nil), // 0: provider.GetUserRequest + (*GetUserResponse)(nil), // 1: provider.GetUserResponse + (*User)(nil), // 2: provider.User +} +var file_hello_grpc_proto_depIdxs = []int32{ + 2, // 0: provider.GetUserResponse.users:type_name -> provider.User + 0, // 1: provider.UserProvider.GetUser:input_type -> provider.GetUserRequest + 1, // 2: provider.UserProvider.GetUser:output_type -> provider.GetUserResponse + 2, // [2:3] is the sub-list for method output_type + 1, // [1:2] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_hello_grpc_proto_init() } +func file_hello_grpc_proto_init() { + if File_hello_grpc_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_hello_grpc_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUserRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_hello_grpc_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetUserResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_hello_grpc_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); 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_hello_grpc_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_hello_grpc_proto_goTypes, + DependencyIndexes: file_hello_grpc_proto_depIdxs, + MessageInfos: file_hello_grpc_proto_msgTypes, + }.Build() + File_hello_grpc_proto = out.File + file_hello_grpc_proto_rawDesc = nil + file_hello_grpc_proto_goTypes = nil + file_hello_grpc_proto_depIdxs = nil +} diff --git a/samples/http/grpc/proto/hello_grpc.proto b/samples/http/grpc/proto/hello_grpc.proto new file mode 100644 index 000000000..f3d4bcba6 --- /dev/null +++ b/samples/http/grpc/proto/hello_grpc.proto @@ -0,0 +1,40 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You 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. + + +// protoc --proto_path=. --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative .\hello_grpc.proto +syntax = "proto3"; + +option go_package = "github.com/apache/dubbo-go-pixiu/samples/http/grpc/proto"; + +package provider; + +service UserProvider { + rpc GetUser (GetUserRequest) returns (GetUserResponse); +} + +message GetUserRequest { + int32 userId = 1; +} + +message GetUserResponse { + string message = 1; + repeated User users = 2; +} + +message User { + int32 userId = 1; + string name = 2; +} \ No newline at end of file diff --git a/samples/http/grpc/proto/hello_grpc_grpc.pb.go b/samples/http/grpc/proto/hello_grpc_grpc.pb.go new file mode 100644 index 000000000..9931b2d77 --- /dev/null +++ b/samples/http/grpc/proto/hello_grpc_grpc.pb.go @@ -0,0 +1,97 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion7 + +// UserProviderClient is the client API for UserProvider 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 UserProviderClient interface { + GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) +} + +type userProviderClient struct { + cc grpc.ClientConnInterface +} + +func NewUserProviderClient(cc grpc.ClientConnInterface) UserProviderClient { + return &userProviderClient{cc} +} + +func (c *userProviderClient) GetUser(ctx context.Context, in *GetUserRequest, opts ...grpc.CallOption) (*GetUserResponse, error) { + out := new(GetUserResponse) + err := c.cc.Invoke(ctx, "/provider.UserProvider/GetUser", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// UserProviderServer is the server API for UserProvider service. +// All implementations must embed UnimplementedUserProviderServer +// for forward compatibility +type UserProviderServer interface { + GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) + mustEmbedUnimplementedUserProviderServer() +} + +// UnimplementedUserProviderServer must be embedded to have forward compatible implementations. +type UnimplementedUserProviderServer struct { +} + +func (UnimplementedUserProviderServer) GetUser(context.Context, *GetUserRequest) (*GetUserResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented") +} +func (UnimplementedUserProviderServer) mustEmbedUnimplementedUserProviderServer() {} + +// UnsafeUserProviderServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to UserProviderServer will +// result in compilation errors. +type UnsafeUserProviderServer interface { + mustEmbedUnimplementedUserProviderServer() +} + +func RegisterUserProviderServer(s *grpc.Server, srv UserProviderServer) { + s.RegisterService(&_UserProvider_serviceDesc, srv) +} + +func _UserProvider_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetUserRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(UserProviderServer).GetUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/provider.UserProvider/GetUser", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(UserProviderServer).GetUser(ctx, req.(*GetUserRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _UserProvider_serviceDesc = grpc.ServiceDesc{ + ServiceName: "provider.UserProvider", + HandlerType: (*UserProviderServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetUser", + Handler: _UserProvider_GetUser_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "hello_grpc.proto", +} diff --git a/samples/http/grpc/server/main.go b/samples/http/grpc/server/main.go new file mode 100644 index 000000000..2ce84b468 --- /dev/null +++ b/samples/http/grpc/server/main.go @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package main + +import ( + "context" + "net" +) + +import ( + "google.golang.org/grpc" +) + +import ( + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/samples/http/grpc/proto" +) + +const ( + MsgUserNotFound = "user not found" + MsgUserQuerySuccessfully = "user(s) query successfully" +) + +type server struct { + users map[int32]*proto.User + proto.UnimplementedUserProviderServer +} + +func (s *server) GetUser(ctx context.Context, request *proto.GetUserRequest) (*proto.GetUserResponse, error) { + us := make([]*proto.User, 0) + if request.GetUserId() == 0 { + for _, user := range s.users { + us = append(us, user) + } + } else { + u, ok := s.users[request.GetUserId()] + if !ok { + return &proto.GetUserResponse{Message: MsgUserNotFound}, nil + } + us = append(us, u) + } + return &proto.GetUserResponse{Message: MsgUserQuerySuccessfully, Users: us}, nil +} + +func main() { + l, err := net.Listen("tcp", ":50001") + if err != nil { + panic(err) + } + + s := &server{users: make(map[int32]*proto.User, 0)} + initUsers(s) + + gs := grpc.NewServer() + proto.RegisterUserProviderServer(gs, s) + logger.Info("grpc test server is now running...") + err = gs.Serve(l) + if err != nil { + panic(err) + } +} + +func initUsers(s *server) { + s.users[1] = &proto.User{UserId: 1, Name: "Kenway"} + s.users[2] = &proto.User{UserId: 2, Name: "Ken"} +} From fd6e8b5012a44d7f53452e9d256dcac27e8a66a9 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Mon, 6 Sep 2021 15:08:23 +0800 Subject: [PATCH 08/22] feat: remove consul support --- go.mod | 6 +- go.sum | 35 ++++++++++ pkg/registry/consul.go | 123 ------------------------------------ pkg/registry/consul_test.go | 84 ------------------------ 4 files changed, 37 insertions(+), 211 deletions(-) delete mode 100644 pkg/registry/consul.go delete mode 100644 pkg/registry/consul_test.go diff --git a/go.mod b/go.mod index 66ae94310..2f1523ffe 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,6 @@ require ( github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9 github.com/golang/protobuf v1.5.2 github.com/google/uuid v1.2.0 // indirect - github.com/hashicorp/consul/api v1.5.0 github.com/jhump/protoreflect v1.9.0 github.com/mitchellh/mapstructure v1.4.1 github.com/pkg/errors v0.9.1 @@ -35,7 +34,7 @@ require ( go.uber.org/zap v1.16.0 golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc // indirect golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect - google.golang.org/grpc v1.27.0 + google.golang.org/grpc v1.40.0 google.golang.org/protobuf v1.27.1 gopkg.in/yaml.v2 v2.4.0 ) @@ -43,5 +42,4 @@ require ( replace ( github.com/envoyproxy/go-control-plane => github.com/envoyproxy/go-control-plane v0.8.0 google.golang.org/api => google.golang.org/api v0.13.0 - google.golang.org/grpc => google.golang.org/grpc v1.27.0 -) +) \ No newline at end of file diff --git a/go.sum b/go.sum index 6e31c9698..6ca26d4d3 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= @@ -152,6 +153,9 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E= @@ -375,6 +379,7 @@ github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2 h1:AtvtonGEH/fZK0X github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -576,6 +581,7 @@ github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-b github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE= github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= @@ -960,6 +966,7 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -1043,6 +1050,7 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVs golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1125,9 +1133,11 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= @@ -1184,6 +1194,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1225,8 +1236,30 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1236,6 +1269,7 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= @@ -1278,6 +1312,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/registry/consul.go b/pkg/registry/consul.go deleted file mode 100644 index f64d04cd5..000000000 --- a/pkg/registry/consul.go +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -package registry - -import ( - "net/url" - "strconv" - "strings" -) - -import ( - "github.com/apache/dubbo-go/common" - "github.com/apache/dubbo-go/common/constant" - - consul "github.com/hashicorp/consul/api" - - perrors "github.com/pkg/errors" -) - -import ( - "github.com/apache/dubbo-go-pixiu/pkg/logger" -) - -func init() { - var _ Loader = new(ConsulRegistryLoad) -} - -const ( - dubboAPIFilter = "dubbo in Tags" -) - -// ConsulRegistryLoad load dubbo apis from consul registry -type ConsulRegistryLoad struct { - Address string - // Consul client. - client *consul.Client - cluster string -} - -func newConsulRegistryLoad(address, cluster string) (Loader, error) { - config := &consul.Config{Address: address} - client, err := consul.NewClient(config) - if err != nil { - return nil, err - } - - r := &ConsulRegistryLoad{ - Address: address, - client: client, - cluster: cluster, - } - - return r, nil -} - -// nolint -func (crl *ConsulRegistryLoad) GetCluster() (string, error) { - return crl.cluster, nil -} - -func (crl *ConsulRegistryLoad) transfer2Url(service consul.AgentService) (*common.URL, error) { - params := url.Values{} - var protocol string - - for _, tag := range service.Tags { - kv := strings.Split(tag, "=") - if len(kv) != 2 { - continue - } - params.Add(kv[0], kv[1]) - } - - if url, ok := service.Meta["url"]; ok { - protocol = strings.Split(url, ":")[0] - } - - methodsParam := strings.Split(params.Get(constant.METHODS_KEY), ",") - methods := make([]string, len(methodsParam)) - for _, method := range methodsParam { - if method != "" { - methods = append(methods, method) - } - } - url := common.NewURLWithOptions(common.WithPort(strconv.Itoa(service.Port)), - common.WithProtocol(protocol), common.WithMethods(methods), - common.WithIp(service.Address), common.WithParams(params)) - - return url, nil -} - -// LoadAllServices load all services from consul registry -func (crl *ConsulRegistryLoad) LoadAllServices() ([]*common.URL, error) { - agentServices, err := crl.client.Agent().ServicesWithFilter(dubboAPIFilter) - if err != nil { - logger.Error("consul load all apis error:%v", err) - return nil, perrors.Wrap(err, "consul load all apis") - } - var urls []*common.URL - for _, service := range agentServices { - url, err := crl.transfer2Url(*service) - if err != nil { - logger.Warnf("consul transfer service to url error:%v", err) - continue - } - urls = append(urls, url) - } - return urls, nil -} diff --git a/pkg/registry/consul_test.go b/pkg/registry/consul_test.go deleted file mode 100644 index f9c8cab96..000000000 --- a/pkg/registry/consul_test.go +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -package registry - -import ( - "net/url" - "strconv" - "strings" - "testing" -) - -import ( - "github.com/apache/dubbo-go/common" - "github.com/apache/dubbo-go/common/constant" - "github.com/apache/dubbo-go/common/extension" - _ "github.com/apache/dubbo-go/registry/consul" - "github.com/apache/dubbo-go/remoting/consul" - - "github.com/stretchr/testify/assert" -) - -var ( - registryHost = "localhost" - registryPort = 8500 - providerHost = "localhost" - providerPort = 8000 - consumerHost = "localhost" - consumerPort = 8001 - service = "HelloWorld" - protocol = "tcp" - cluster = "test_cluster" -) - -func TestConsulRegistryLoad_GetCluster(t *testing.T) { - loader, err := newConsulRegistryLoad(registryHost+":"+strconv.Itoa(registryPort), "test_cluster") - assert.Nil(t, err) - consulCluster, err := loader.GetCluster() - assert.Nil(t, err) - assert.Equal(t, cluster, consulCluster) -} - -func TestConsulRegistryLoad_LoadAllServices(t *testing.T) { - consulAgent := consul.NewConsulAgent(t, registryPort) - defer consulAgent.Shutdown() - registryURL, _ := common.NewURL(protocol + "://" + providerHost + ":" + strconv.Itoa(providerPort) + "/" + service + "?anyhost=true&" + - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×tamp=1556509797245") - - registry, err := extension.GetRegistry("consul", common.NewURLWithOptions(common.WithParams(url.Values{}), common.WithIp("localhost"), common.WithPort("8500"), common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)))) - assert.Nil(t, err) - err = registry.Register(registryURL) - assert.Nil(t, err) - defer registry.UnRegister(registryURL) - loader, err := newConsulRegistryLoad(registryHost+":"+strconv.Itoa(registryPort), "test_cluster") - assert.Nil(t, err) - services, err := loader.LoadAllServices() - assert.Nil(t, err) - assert.Len(t, services, 1) - assert.Contains(t, services[0].Methods, "GetUser") - assert.Equal(t, services[0].GetParams(), registryURL.GetParams()) -} - -func TestName(t *testing.T) { - s := "1,,,1" - right := strings.TrimRight(s, ",") - t.Log(right) -} From 59ea8777c627631dcf8bdeafdd17e8bfa442a858 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Mon, 6 Sep 2021 22:50:49 +0800 Subject: [PATCH 09/22] style: change protocol type name and add explanation --- pkg/model/base.go | 25 ++++++++++++++----------- pkg/server/listener.go | 4 ++-- pkg/server/listener_test.go | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/pkg/model/base.go b/pkg/model/base.go index 26776a7af..4e0820c69 100644 --- a/pkg/model/base.go +++ b/pkg/model/base.go @@ -42,10 +42,11 @@ const ( ) const ( - HTTP ProtocolType = 0 + iota // support for 1.0 - TCP - UDP - HTTPS + ProtocolTypeHTTP ProtocolType = 0 + iota // support for 1.0 + ProtocolTypeTCP + ProtocolTypeUDP + ProtocolTypeHTTPS + ProtocolTypeGRPC ) const ( @@ -55,9 +56,9 @@ const ( ) const ( - REST api.ApiType = 0 + iota // support for 1.0 - GRPC - DUBBO + ApiTypeREST api.ApiType = 0 + iota // support for 1.0 + ApiTypeGRPC + ApiTypeDUBBO ) var ( @@ -73,20 +74,22 @@ var ( "Unknown": 2, } - // ProtocolTypeName + // ProtocolTypeName enum seq to protocol type name ProtocolTypeName = map[int32]string{ 0: "HTTP", 1: "TCP", 2: "UDP", 3: "HTTPS", + 4: "GRPC", } - // ProtocolTypeValue + // ProtocolTypeValue protocol type name to enum seq ProtocolTypeValue = map[string]int32{ "HTTP": 0, "TCP": 1, "UDP": 2, "HTTPS": 3, + "GRPC": 4, } ApiTypeName = map[int32]string{ @@ -103,7 +106,7 @@ var ( ) type ( - // ProtocolType + // ProtocolType protocol type enum ProtocolType int32 // Address the address @@ -112,7 +115,7 @@ type ( Name string `yaml:"name" json:"name" mapstructure:"name"` } - // Address specify either a logical or physical address and port, which are + // SocketAddress specify either a logical or physical address and port, which are // used to tell server where to bind/listen, connect to upstream and find // management servers SocketAddress struct { diff --git a/pkg/server/listener.go b/pkg/server/listener.go index 0a41281ba..ed2baeed5 100644 --- a/pkg/server/listener.go +++ b/pkg/server/listener.go @@ -60,9 +60,9 @@ func CreateListenerService(lc *model.Listener, bs *model.Bootstrap) *ListenerSer func (ls *ListenerService) Start() { sa := ls.cfg.Address.SocketAddress switch sa.Protocol { - case model.HTTP: + case model.ProtocolTypeHTTP: ls.httpListener() - case model.HTTPS: + case model.ProtocolTypeHTTPS: ls.httpsListener() default: panic("unsupported protocol start: " + sa.ProtocolStr) diff --git a/pkg/server/listener_test.go b/pkg/server/listener_test.go index 28fca8ebc..4220242f6 100644 --- a/pkg/server/listener_test.go +++ b/pkg/server/listener_test.go @@ -32,7 +32,7 @@ func getTestContext() *ctxHttp.HttpContext { Name: "test", Address: model.Address{ SocketAddress: model.SocketAddress{ - Protocol: model.HTTP, + Protocol: model.ProtocolTypeHTTP, Address: "0.0.0.0", Port: 8888, }, From 85be1eecbfa4f747cb2c3aec9459caab37eee74c Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 00:18:03 +0800 Subject: [PATCH 10/22] feat: handle request header && response header fix: make sync.Pool concerning with cluster name fix: init file source style: format import --- .../http/grpcproxy/descriptor_operation.go | 3 + pkg/filter/http/grpcproxy/grpc.go | 124 ++++++++++++++---- pkg/filter/http/grpcproxy/invoke.go | 13 +- .../network/httpconnectionmanager/plugin.go | 1 - pkg/pluginregistry/registry.go | 1 + 5 files changed, 107 insertions(+), 35 deletions(-) diff --git a/pkg/filter/http/grpcproxy/descriptor_operation.go b/pkg/filter/http/grpcproxy/descriptor_operation.go index 1e1db3cfe..d8aa82099 100644 --- a/pkg/filter/http/grpcproxy/descriptor_operation.go +++ b/pkg/filter/http/grpcproxy/descriptor_operation.go @@ -20,7 +20,9 @@ package grpcproxy import ( "fmt" "sync" +) +import ( "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/desc/protoparse" "github.com/jhump/protoreflect/dynamic" @@ -50,6 +52,7 @@ func (af *Filter) initFromFileDescriptor(importPaths []string, fileNames ...stri return fmt.Errorf("could not parse given files: %v", err) } + fsrc.files = make(map[string]*desc.FileDescriptor) for _, fd := range fds { name := fd.GetName() fsrc.files[name] = fd diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index ef3ff810d..3a3b51522 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -18,9 +18,17 @@ package grpcproxy import ( - "context" "fmt" - "github.com/apache/dubbo-go-pixiu/pkg/logger" + "io" + "io/fs" + "io/ioutil" + stdHttp "net/http" + "path/filepath" + "strings" + "sync" +) + +import ( "github.com/gogo/protobuf/proto" "github.com/golang/protobuf/jsonpb" "github.com/jhump/protoreflect/desc" @@ -29,16 +37,16 @@ import ( perrors "github.com/pkg/errors" "google.golang.org/grpc" "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" - "io" - "io/fs" - "path/filepath" - "strings" - "sync" +) +import ( "github.com/apache/dubbo-go-pixiu/pkg/common/constant" "github.com/apache/dubbo-go-pixiu/pkg/common/extension/filter" "github.com/apache/dubbo-go-pixiu/pkg/context/http" + "github.com/apache/dubbo-go-pixiu/pkg/logger" + "github.com/apache/dubbo-go-pixiu/pkg/server" ) const ( @@ -65,7 +73,7 @@ type ( Filter struct { cfg *Config // hold grpc.ClientConns - pool sync.Pool + pools map[string]*sync.Pool } // Config describe the config of AccessFilter @@ -97,19 +105,30 @@ func (af *Filter) PrepareFilterChain(ctx *http.HttpContext) error { return nil } +// getServiceAndMethod first return value is package.service, second one is method name +func getServiceAndMethod(path string) (string, string) { + pos := strings.LastIndex(path, "/") + if pos < 0 { + return "", "" + } + + mth := path[pos+1:] + prefix := strings.TrimSuffix(path, "/"+mth) + + pos = strings.LastIndex(prefix, "/") + if pos < 0 { + return "", "" + } + + svc := prefix[pos+1:] + return svc, mth +} + // Handle use the default http to grpc transcoding strategy https://cloud.google.com/endpoints/docs/grpc/transcoding func (af *Filter) Handle(c *http.HttpContext) { - paths := strings.Split(c.Request.URL.Path, "/") - if len(paths) < 4 { - logger.Errorf("%s err {%s}", loggerHeader, "request path invalid") - c.Err = perrors.New("request path invalid") - c.Next() - return - } - svc := paths[2] - mth := paths[3] + svc, mth := getServiceAndMethod(c.Request.URL.Path) - dscp, err := fsrc.FindSymbol(svc + "." + mth) + dscp, err := fsrc.FindSymbol(svc) if err != nil { logger.Errorf("%s err {%s}", loggerHeader, "request path invalid") c.Err = perrors.New("method not allow") @@ -150,23 +169,31 @@ func (af *Filter) Handle(c *http.HttpContext) { grpcReq := msgFac.NewMessage(mthDesc.GetInputType()) err = jsonToProtoMsg(c.Request.Body, grpcReq) - if err != nil { + if err != nil && err != io.EOF { logger.Errorf("%s err {failed to convert json to proto msg, %s}", loggerHeader, err.Error()) c.Err = err c.Next() return } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - var clientConn *grpc.ClientConn re := c.GetRouteEntry() logger.Debugf("%s client choose endpoint from cluster :%v", loggerHeader, re.Cluster) - clientConn = af.pool.Get().(*grpc.ClientConn) - if clientConn == nil { + p, ok := af.pools[re.Cluster] + if !ok { + p = &sync.Pool{} + } + clientConn, ok = p.Get().(*grpc.ClientConn) + if !ok || clientConn == nil { // TODO(Kenway): Support Credential and TLS - clientConn, err = grpc.DialContext(ctx, re.Cluster, grpc.WithInsecure()) + e := server.GetClusterManager().PickEndpoint(re.Cluster) + if e == nil { + logger.Errorf("%s err {cluster not exists}", loggerHeader) + c.Err = perrors.New("cluster not exists") + c.Next() + return + } + clientConn, err = grpc.DialContext(c.Ctx, e.Address.GetAddress(), grpc.WithInsecure()) if err != nil || clientConn == nil { logger.Errorf("%s err {failed to connect to grpc service provider}", loggerHeader) c.Err = err @@ -177,7 +204,15 @@ func (af *Filter) Handle(c *http.HttpContext) { stub := grpcdynamic.NewStubWithMessageFactory(clientConn, msgFac) - resp, err := Invoke(ctx, stub, mthDesc, grpcReq) + // metadata in grpc has the same feature in http + md := mapHeaderToMetadata(c.Request.Header) + ctx := metadata.NewOutgoingContext(c.Ctx, md) + + md = metadata.MD{} + t := metadata.MD{} + + resp, err := Invoke(ctx, stub, mthDesc, grpcReq, grpc.Header(&md), grpc.Trailer(&t)) + // judge err is server side error or not if st, ok := status.FromError(err); !ok || isServerError(st) { logger.Error("%s err {failed to invoke grpc service provider, %s}", loggerHeader, err.Error()) c.Err = err @@ -193,9 +228,18 @@ func (af *Filter) Handle(c *http.HttpContext) { return } + h := mapMetadataToHeader(md) + th := mapMetadataToHeader(t) + // let response filter handle resp - c.SourceResp = res - af.pool.Put(clientConn) + c.SourceResp = &stdHttp.Response{ + StatusCode: stdHttp.StatusOK, + Header: h, + Body: ioutil.NopCloser(strings.NewReader(res)), + Trailer: th, + Request: c.Request, + } + p.Put(clientConn) c.Next() } @@ -229,8 +273,30 @@ func RegisterExtension(extReg *dynamic.ExtensionRegistry, msgDesc *desc.MessageD return nil } +func mapHeaderToMetadata(header stdHttp.Header) metadata.MD { + md := metadata.MD{} + for key, val := range header { + md.Append(key, val...) + } + return md +} + +func mapMetadataToHeader(md metadata.MD) stdHttp.Header { + h := stdHttp.Header{} + for key, val := range md { + for _, v := range val { + h.Add(key, v) + } + } + return h +} + func jsonToProtoMsg(reader io.Reader, msg proto.Message) error { - return jsonpb.Unmarshal(reader, msg) + body, err := ioutil.ReadAll(reader) + if err != nil { + return err + } + return jsonpb.UnmarshalString(string(body), msg) } func protoMsgToJson(msg proto.Message) (string, error) { diff --git a/pkg/filter/http/grpcproxy/invoke.go b/pkg/filter/http/grpcproxy/invoke.go index cffaf817b..8f9441b87 100644 --- a/pkg/filter/http/grpcproxy/invoke.go +++ b/pkg/filter/http/grpcproxy/invoke.go @@ -19,17 +19,20 @@ package grpcproxy import ( "context" +) + +import ( "github.com/golang/protobuf/proto" "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/dynamic/grpcdynamic" perrors "github.com/pkg/errors" + "google.golang.org/grpc" "google.golang.org/grpc/status" ) -func Invoke(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message) (proto.Message, error) { +func Invoke(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message, opts ...grpc.CallOption) (proto.Message, error) { var resp proto.Message var err error - // TODO(Kenway): use a better way to use bi-direction stream // Bi-direction Stream if mthDesc.IsServerStreaming() && mthDesc.IsClientStreaming() { err = perrors.New("currently not support bi-direction stream") @@ -41,7 +44,7 @@ func Invoke(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDesc err = perrors.New("currently not support server side stream") //resp, err = invokeServerStream(ctx, stub, mthDesc, grpcReq) } else { - resp, err = invokeUnary(ctx, stub, mthDesc, grpcReq) + resp, err = invokeUnary(ctx, stub, mthDesc, grpcReq, opts...) } return resp, err @@ -87,6 +90,6 @@ func invokeServerStream(ctx context.Context, stub grpcdynamic.Stub, mthDesc *des return stream.RecvMsg() } -func invokeUnary(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message) (proto.Message, error) { - return stub.InvokeRpc(ctx, mthDesc, grpcReq) +func invokeUnary(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message, opts ...grpc.CallOption) (proto.Message, error) { + return stub.InvokeRpc(ctx, mthDesc, grpcReq, opts...) } diff --git a/pkg/filter/network/httpconnectionmanager/plugin.go b/pkg/filter/network/httpconnectionmanager/plugin.go index 773e94f72..bb1c0e52f 100644 --- a/pkg/filter/network/httpconnectionmanager/plugin.go +++ b/pkg/filter/network/httpconnectionmanager/plugin.go @@ -41,7 +41,6 @@ func (hp *HttpConnectionManagerPlugin) Kind() string { } func (hp *HttpConnectionManagerPlugin) CreateFilter(config interface{}, bs *model.Bootstrap) (filter.NetworkFilter, error) { - hcmc := config.(*model.HttpConnectionManager) return http.CreateHttpConnectionManager(hcmc, bs), nil } diff --git a/pkg/pluginregistry/registry.go b/pkg/pluginregistry/registry.go index 7274836e0..e308c4230 100644 --- a/pkg/pluginregistry/registry.go +++ b/pkg/pluginregistry/registry.go @@ -26,6 +26,7 @@ import ( _ "github.com/apache/dubbo-go-pixiu/pkg/filter/header" _ "github.com/apache/dubbo-go-pixiu/pkg/filter/host" _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/apiconfig" + _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/grpcproxy" _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/httpproxy" _ "github.com/apache/dubbo-go-pixiu/pkg/filter/http/remote" _ "github.com/apache/dubbo-go-pixiu/pkg/filter/metric" From b27f0538bc043e78edf63ddab52a0fdb0363aa0b Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 00:22:54 +0800 Subject: [PATCH 11/22] fix: go simple style: add license header --- samples/http/grpc/proto/hello_grpc.pb.go | 31 ++++++++++--------- samples/http/grpc/proto/hello_grpc_grpc.pb.go | 16 ++++++++++ samples/http/grpc/server/main.go | 4 +-- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/samples/http/grpc/proto/hello_grpc.pb.go b/samples/http/grpc/proto/hello_grpc.pb.go index ac6a8d32d..f5a1603d7 100644 --- a/samples/http/grpc/proto/hello_grpc.pb.go +++ b/samples/http/grpc/proto/hello_grpc.pb.go @@ -1,18 +1,19 @@ -// Licensed to the Apache Software Foundation (ASF) under one or more -// contributor license agreements. See the NOTICE file distributed with -// this work for additional information regarding copyright ownership. -// The ASF licenses this file to You 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. - +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ // protoc --proto_path=. --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative .\hello_grpc.proto // Code generated by protoc-gen-go. DO NOT EDIT. diff --git a/samples/http/grpc/proto/hello_grpc_grpc.pb.go b/samples/http/grpc/proto/hello_grpc_grpc.pb.go index 9931b2d77..61e9734a6 100644 --- a/samples/http/grpc/proto/hello_grpc_grpc.pb.go +++ b/samples/http/grpc/proto/hello_grpc_grpc.pb.go @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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-grpc. DO NOT EDIT. package proto diff --git a/samples/http/grpc/server/main.go b/samples/http/grpc/server/main.go index 2ce84b468..3a4f5c26e 100644 --- a/samples/http/grpc/server/main.go +++ b/samples/http/grpc/server/main.go @@ -58,12 +58,12 @@ func (s *server) GetUser(ctx context.Context, request *proto.GetUserRequest) (*p } func main() { - l, err := net.Listen("tcp", ":50001") + l, err := net.Listen("tcp", ":50001") //nolint:gosec if err != nil { panic(err) } - s := &server{users: make(map[int32]*proto.User, 0)} + s := &server{users: make(map[int32]*proto.User)} initUsers(s) gs := grpc.NewServer() From 6a6370f7a21d356083123cbe9236fa0857525888 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 00:51:54 +0800 Subject: [PATCH 12/22] fix: remove io/fs usage --- pkg/filter/http/grpcproxy/grpc.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index 3a3b51522..f3b2cc1ad 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -20,10 +20,8 @@ package grpcproxy import ( "fmt" "io" - "io/fs" "io/ioutil" stdHttp "net/http" - "path/filepath" "strings" "sync" ) @@ -316,20 +314,21 @@ func (af *Filter) Config() interface{} { func (af *Filter) Apply() error { gc := af.cfg fileLists := make([]string, 0) - err := filepath.Walk(gc.Path, func(path string, info fs.FileInfo, err error) error { - if err != nil { - return err - } + items, err := ioutil.ReadDir(gc.Path) + if err != nil { + return err + } - if !info.IsDir() { - sp := strings.Split(info.Name(), ".") + for _, item := range items { + if !item.IsDir() { + sp := strings.Split(item.Name(), ".") length := len(sp) if length >= 2 && sp[length-1] == "proto" { - fileLists = append(fileLists, info.Name()) + fileLists = append(fileLists, item.Name()) } } - return nil - }) + } + if err != nil { return err } From 344d025744e916b304cbb71543bf794672575bed Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 01:10:47 +0800 Subject: [PATCH 13/22] fix: add nolint --- pkg/filter/http/grpcproxy/grpc.go | 8 ++++---- pkg/filter/http/grpcproxy/invoke.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index f3b2cc1ad..58339b20f 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -27,8 +27,8 @@ import ( ) import ( - "github.com/gogo/protobuf/proto" - "github.com/golang/protobuf/jsonpb" + "github.com/golang/protobuf/jsonpb" //nolint + "github.com/golang/protobuf/proto" //nolint "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/dynamic" "github.com/jhump/protoreflect/dynamic/grpcdynamic" @@ -77,7 +77,7 @@ type ( // Config describe the config of AccessFilter Config struct { Path string `yaml:"path" json:"path"` - rules []*Rule `yaml:"rules" json:"rules"` + rules []*Rule `yaml:"rules" json:"rules"` //nolint } Rule struct { @@ -86,7 +86,7 @@ type ( } Match struct { - method string `yaml:"method" json:"method"` + method string `yaml:"method" json:"method"` //nolint } ) diff --git a/pkg/filter/http/grpcproxy/invoke.go b/pkg/filter/http/grpcproxy/invoke.go index 8f9441b87..d56742273 100644 --- a/pkg/filter/http/grpcproxy/invoke.go +++ b/pkg/filter/http/grpcproxy/invoke.go @@ -22,7 +22,7 @@ import ( ) import ( - "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/proto" //nolint "github.com/jhump/protoreflect/desc" "github.com/jhump/protoreflect/dynamic/grpcdynamic" perrors "github.com/pkg/errors" @@ -36,13 +36,13 @@ func Invoke(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDesc // Bi-direction Stream if mthDesc.IsServerStreaming() && mthDesc.IsClientStreaming() { err = perrors.New("currently not support bi-direction stream") - //resp, err = invokeBiDirectionStream(ctx, stub, mthDesc, grpcReq) + // resp, err = invokeBiDirectionStream(ctx, stub, mthDesc, grpcReq) } else if mthDesc.IsClientStreaming() { err = perrors.New("currently not support client side stream") - //resp, err = invokeClientStream(ctx, stub, mthDesc, grpcReq) + // resp, err = invokeClientStream(ctx, stub, mthDesc, grpcReq) } else if mthDesc.IsServerStreaming() { err = perrors.New("currently not support server side stream") - //resp, err = invokeServerStream(ctx, stub, mthDesc, grpcReq) + // resp, err = invokeServerStream(ctx, stub, mthDesc, grpcReq) } else { resp, err = invokeUnary(ctx, stub, mthDesc, grpcReq, opts...) } From 8dfb3184311dda0ef7e71c76b9c6ed89dcb2d205 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 01:20:40 +0800 Subject: [PATCH 14/22] feat: update proto-gen-go version --- samples/http/grpc/proto/hello_grpc.pb.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/samples/http/grpc/proto/hello_grpc.pb.go b/samples/http/grpc/proto/hello_grpc.pb.go index f5a1603d7..47b9066b4 100644 --- a/samples/http/grpc/proto/hello_grpc.pb.go +++ b/samples/http/grpc/proto/hello_grpc.pb.go @@ -18,14 +18,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.20.1 -// protoc v3.15.0 +// protoc-gen-go v1.27.1 +// protoc v3.15.0--rc2 // source: hello_grpc.proto package proto import ( - proto "github.com/golang/protobuf/proto" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -39,10 +38,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// This is a compile-time assertion that a sufficiently up-to-date version -// of the legacy proto package is being used. -const _ = proto.ProtoPackageIsVersion4 - type GetUserRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache From c06eef23dbf9325e7ec57d11afec52b64169bf5a Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 01:25:04 +0800 Subject: [PATCH 15/22] feat: add test cases --- samples/http/grpc/pixiu/api_config.yaml | 14 ++++++++++++++ samples/http/grpc/server/main.go | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/samples/http/grpc/pixiu/api_config.yaml b/samples/http/grpc/pixiu/api_config.yaml index db91c8252..3ecd3118a 100644 --- a/samples/http/grpc/pixiu/api_config.yaml +++ b/samples/http/grpc/pixiu/api_config.yaml @@ -24,6 +24,20 @@ resources: description: user methods: - httpVerb: GET + enable: true + timeout: 1000ms + inboundRequest: + requestType: http + integrationRequest: + requestType: grpc + group: "test" + version: 1.0.0 + clusterName: "test_grpc" + - path: '/api/v1/provider.UserProvider/GetUser' + type: restful + description: user + methods: + - httpVerb: POST enable: true timeout: 1000ms inboundRequest: diff --git a/samples/http/grpc/server/main.go b/samples/http/grpc/server/main.go index 3a4f5c26e..d1faea1e6 100644 --- a/samples/http/grpc/server/main.go +++ b/samples/http/grpc/server/main.go @@ -36,6 +36,10 @@ const ( MsgUserQuerySuccessfully = "user(s) query successfully" ) +// Test Cases +// curl http://127.0.0.1:8881/api/v1/provider.UserProvider/GetUser +// curl http://127.0.0.1:8881/api/v1/provider.UserProvider/GetUser -X POST -d '{"userId":1}' + type server struct { users map[int32]*proto.User proto.UnimplementedUserProviderServer From df887f747ef463a90133e033fd028de147c635bf Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 12:49:10 +0800 Subject: [PATCH 16/22] fix: remove gogo/proto from go.mod --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index 2f1523ffe..b0efeb279 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/dubbogo/gost v1.11.14 github.com/emirpasic/gods v1.12.0 github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 - github.com/gogo/protobuf v1.3.2 github.com/goinggo/mapstructure v0.0.0-20140717182941-194205d9b4a9 github.com/golang/protobuf v1.5.2 github.com/google/uuid v1.2.0 // indirect From 4a0ac42e7d5f8eaeab2f5780459c90ac9d773cf2 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 13:17:32 +0800 Subject: [PATCH 17/22] fix: make sync.Pool associate with endpoints --- pkg/filter/http/grpcproxy/grpc.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index 58339b20f..10b72da93 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -177,21 +177,26 @@ func (af *Filter) Handle(c *http.HttpContext) { var clientConn *grpc.ClientConn re := c.GetRouteEntry() logger.Debugf("%s client choose endpoint from cluster :%v", loggerHeader, re.Cluster) - p, ok := af.pools[re.Cluster] + + e := server.GetClusterManager().PickEndpoint(re.Cluster) + if e == nil { + logger.Errorf("%s err {cluster not exists}", loggerHeader) + c.Err = perrors.New("cluster not exists") + c.Next() + return + } + + ep := e.Address.GetAddress() + + p, ok := af.pools[strings.Join([]string{re.Cluster, ep}, ".")] if !ok { p = &sync.Pool{} } + clientConn, ok = p.Get().(*grpc.ClientConn) if !ok || clientConn == nil { // TODO(Kenway): Support Credential and TLS - e := server.GetClusterManager().PickEndpoint(re.Cluster) - if e == nil { - logger.Errorf("%s err {cluster not exists}", loggerHeader) - c.Err = perrors.New("cluster not exists") - c.Next() - return - } - clientConn, err = grpc.DialContext(c.Ctx, e.Address.GetAddress(), grpc.WithInsecure()) + clientConn, err = grpc.DialContext(c.Ctx, ep, grpc.WithInsecure()) if err != nil || clientConn == nil { logger.Errorf("%s err {failed to connect to grpc service provider}", loggerHeader) c.Err = err From 91620798ef7234a2e15084dcfb3d28e3101c5af8 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 13:31:27 +0800 Subject: [PATCH 18/22] feat: cache extension registry --- .../http/grpcproxy/descriptor_operation.go | 2 +- pkg/filter/http/grpcproxy/grpc.go | 64 ++++++++++--------- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/pkg/filter/http/grpcproxy/descriptor_operation.go b/pkg/filter/http/grpcproxy/descriptor_operation.go index d8aa82099..815a7b489 100644 --- a/pkg/filter/http/grpcproxy/descriptor_operation.go +++ b/pkg/filter/http/grpcproxy/descriptor_operation.go @@ -37,7 +37,7 @@ type fileSource struct { // initFromFileDescriptor // protoparse.ResolveFilenames(importPaths, fileNames...) // rel: https://pkg.go.dev/github.com/jhump/protoreflect/desc/protoparse?utm_source=godoc#ResolveFilenames -func (af *Filter) initFromFileDescriptor(importPaths []string, fileNames ...string) error { +func (f *Filter) initFromFileDescriptor(importPaths []string, fileNames ...string) error { fileNames, err := protoparse.ResolveFilenames(importPaths, fileNames...) if err != nil { return err diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index 10b72da93..58e76fcfb 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -18,6 +18,7 @@ package grpcproxy import ( + "errors" "fmt" "io" "io/ioutil" @@ -70,14 +71,17 @@ type ( // Filter is grpc filter instance Filter struct { cfg *Config - // hold grpc.ClientConns + // hold grpc.ClientConns, key format: cluster name + "." + endpoint pools map[string]*sync.Pool + + extReg dynamic.ExtensionRegistry + registered map[string]bool } // Config describe the config of AccessFilter Config struct { Path string `yaml:"path" json:"path"` - rules []*Rule `yaml:"rules" json:"rules"` //nolint + Rules []*Rule `yaml:"rules" json:"rules"` //nolint } Rule struct { @@ -86,20 +90,20 @@ type ( } Match struct { - method string `yaml:"method" json:"method"` //nolint + Method string `yaml:"method" json:"method"` //nolint } ) -func (ap *Plugin) Kind() string { +func (p *Plugin) Kind() string { return Kind } -func (ap *Plugin) CreateFilter() (filter.HttpFilter, error) { +func (p *Plugin) CreateFilter() (filter.HttpFilter, error) { return &Filter{cfg: &Config{}}, nil } -func (af *Filter) PrepareFilterChain(ctx *http.HttpContext) error { - ctx.AppendFilterFunc(af.Handle) +func (f *Filter) PrepareFilterChain(ctx *http.HttpContext) error { + ctx.AppendFilterFunc(f.Handle) return nil } @@ -123,7 +127,7 @@ func getServiceAndMethod(path string) (string, string) { } // Handle use the default http to grpc transcoding strategy https://cloud.google.com/endpoints/docs/grpc/transcoding -func (af *Filter) Handle(c *http.HttpContext) { +func (f *Filter) Handle(c *http.HttpContext) { svc, mth := getServiceAndMethod(c.Request.URL.Path) dscp, err := fsrc.FindSymbol(svc) @@ -144,30 +148,19 @@ func (af *Filter) Handle(c *http.HttpContext) { mthDesc := svcDesc.FindMethodByName(mth) - // TODO(Kenway): Can extension registry being cached ? - var extReg dynamic.ExtensionRegistry - registered := make(map[string]bool) - err = RegisterExtension(&extReg, mthDesc.GetInputType(), registered) - if err != nil { - logger.Errorf("%s err {%s}", loggerHeader, "register extension failed") - c.Err = perrors.New("register extension failed") - c.Next() - return - } - - err = RegisterExtension(&extReg, mthDesc.GetOutputType(), registered) + err = f.registerExtension(mthDesc) if err != nil { logger.Errorf("%s err {%s}", loggerHeader, "register extension failed") - c.Err = perrors.New("register extension failed") + c.Err = err c.Next() return } - msgFac := dynamic.NewMessageFactoryWithExtensionRegistry(&extReg) + msgFac := dynamic.NewMessageFactoryWithExtensionRegistry(&f.extReg) grpcReq := msgFac.NewMessage(mthDesc.GetInputType()) err = jsonToProtoMsg(c.Request.Body, grpcReq) - if err != nil && err != io.EOF { + if err != nil && !errors.Is(err, io.EOF) { logger.Errorf("%s err {failed to convert json to proto msg, %s}", loggerHeader, err.Error()) c.Err = err c.Next() @@ -188,7 +181,7 @@ func (af *Filter) Handle(c *http.HttpContext) { ep := e.Address.GetAddress() - p, ok := af.pools[strings.Join([]string{re.Cluster, ep}, ".")] + p, ok := f.pools[strings.Join([]string{re.Cluster, ep}, ".")] if !ok { p = &sync.Pool{} } @@ -246,6 +239,19 @@ func (af *Filter) Handle(c *http.HttpContext) { c.Next() } +func (f *Filter) registerExtension(mthDesc *desc.MethodDescriptor) error { + err := RegisterExtension(&f.extReg, mthDesc.GetInputType(), f.registered) + if err != nil { + return perrors.New("register extension failed") + } + + err = RegisterExtension(&f.extReg, mthDesc.GetOutputType(), f.registered) + if err != nil { + return perrors.New("register extension failed") + } + return nil +} + func RegisterExtension(extReg *dynamic.ExtensionRegistry, msgDesc *desc.MessageDescriptor, registered map[string]bool) error { msgType := msgDesc.GetFullyQualifiedName() if _, ok := registered[msgType]; ok { @@ -312,12 +318,12 @@ func isServerError(st *status.Status) bool { st.Code() == codes.Unavailable } -func (af *Filter) Config() interface{} { - return af.cfg +func (f *Filter) Config() interface{} { + return f.cfg } -func (af *Filter) Apply() error { - gc := af.cfg +func (f *Filter) Apply() error { + gc := f.cfg fileLists := make([]string, 0) items, err := ioutil.ReadDir(gc.Path) if err != nil { @@ -337,7 +343,7 @@ func (af *Filter) Apply() error { if err != nil { return err } - err = af.initFromFileDescriptor([]string{gc.Path}, fileLists...) + err = f.initFromFileDescriptor([]string{gc.Path}, fileLists...) if err != nil { return err } From 5abe7a660e38b8b26e678fa94216ae9f198943dc Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 16:33:32 +0800 Subject: [PATCH 19/22] feat: integrate test --- igt/Makefile | 4 +- .../grpc/server/{main.go => app/server.go} | 0 samples/http/grpc/test/pixiu_test.go | 73 +++++++++++++++++++ samples/http/simple/docker/docker-compose.yml | 27 ------- start_integrate_test.sh | 3 +- 5 files changed, 76 insertions(+), 31 deletions(-) rename samples/http/grpc/server/{main.go => app/server.go} (100%) create mode 100644 samples/http/grpc/test/pixiu_test.go delete mode 100644 samples/http/simple/docker/docker-compose.yml diff --git a/igt/Makefile b/igt/Makefile index 922349c3b..b6f9534bd 100644 --- a/igt/Makefile +++ b/igt/Makefile @@ -100,13 +100,13 @@ config: .PHONY: docker-up docker-up: $(info > Starting dependency services with $(PROJECT_DIR)/docker/docker-compose.yml) - @docker-compose -f $(PROJECT_DIR)/docker/docker-compose.yml up -d + @-test -f $(PROJECT_DIR)/docker/docker-compose.yml && docker-compose -f $(PROJECT_DIR)/docker/docker-compose.yml up -d ## docker-down: Shutdown dependency services on docker .PHONY: docker-down docker-down: $(info > Stopping dependency services with $(PROJECT_DIR)/docker/docker-compose.yml) - @docker-compose -f $(PROJECT_DIR)/docker/docker-compose.yml down + @-test -f $(PROJECT_DIR)/docker/docker-compose.yml && docker-compose -f $(PROJECT_DIR)/docker/docker-compose.yml down ## clean: Clean up the output and the binary of the application .PHONY: clean diff --git a/samples/http/grpc/server/main.go b/samples/http/grpc/server/app/server.go similarity index 100% rename from samples/http/grpc/server/main.go rename to samples/http/grpc/server/app/server.go diff --git a/samples/http/grpc/test/pixiu_test.go b/samples/http/grpc/test/pixiu_test.go new file mode 100644 index 000000000..57392f581 --- /dev/null +++ b/samples/http/grpc/test/pixiu_test.go @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package test + +import ( + "bytes" + "encoding/json" + "github.com/apache/dubbo-go-pixiu/samples/http/grpc/proto" + "github.com/stretchr/testify/assert" + "io/ioutil" + "net/http" + "strings" + "testing" + "time" +) + +const ( + url = "http://localhost:8881/api/v1/provider.UserProvider/GetUser" + data = "{\"userId\":1}" +) + +func TestGet(t *testing.T) { + c := http.Client{Timeout: 5 * time.Second} + req, err := http.NewRequest(http.MethodGet, url, bytes.NewReader([]byte{})) + assert.NoError(t, err) + resp, err := c.Do(req) + assert.NoError(t, err) + body, err := ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + assert.NotNil(t, body) + var r proto.GetUserResponse + err = json.Unmarshal(body, &r) + assert.NoError(t, err) + assert.Equal(t, "user(s) query successfully", r.Message) + assert.Equal(t, 2, len(r.Users)) + assert.Equal(t, int32(1), r.Users[0].UserId) + assert.Equal(t, "Kenway", r.Users[0].Name) + assert.Equal(t, int32(2), r.Users[1].UserId) + assert.Equal(t, "Ken", r.Users[1].Name) +} + +func TestPost(t *testing.T) { + c := http.Client{Timeout: 5 * time.Second} + req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(data)) + assert.NoError(t, err) + resp, err := c.Do(req) + assert.NoError(t, err) + body, err := ioutil.ReadAll(resp.Body) + assert.NoError(t, err) + assert.NotNil(t, body) + var r proto.GetUserResponse + err = json.Unmarshal(body, &r) + assert.NoError(t, err) + assert.Equal(t, "user(s) query successfully", r.Message) + assert.Equal(t, 1, len(r.Users)) + assert.Equal(t, int32(1), r.Users[0].UserId) + assert.Equal(t, "Kenway", r.Users[0].Name) +} diff --git a/samples/http/simple/docker/docker-compose.yml b/samples/http/simple/docker/docker-compose.yml deleted file mode 100644 index 7ca8ee583..000000000 --- a/samples/http/simple/docker/docker-compose.yml +++ /dev/null @@ -1,27 +0,0 @@ -# -# Licensed to Apache Software Foundation (ASF) under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Apache Software Foundation (ASF) licenses this file to you 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. -# - -version: '3' - -services: - zookeeper: - image: zookeeper - ports: - - 2181:2181 - restart: on-failure diff --git a/start_integrate_test.sh b/start_integrate_test.sh index 226a42e0b..d4f055e4c 100755 --- a/start_integrate_test.sh +++ b/start_integrate_test.sh @@ -23,10 +23,9 @@ array+=("samples/dubbogo/simple/uri") array+=("samples/dubbogo/http") #http +array+=("samples/http/grpc") array+=("samples/http/simple") - - for((i=0;i<${#array[*]};i++)) do sh ./integrate_test.sh ${array[i]} From 3af7065795c722407f1a0301fa68a4fc9d5bc578 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Tue, 7 Sep 2021 17:18:19 +0800 Subject: [PATCH 20/22] fix: integrate test --- igt/Makefile | 1 + pkg/filter/http/grpcproxy/grpc.go | 15 ++++++++++++++- samples/http/grpc/pixiu/conf.yaml | 4 ++-- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/igt/Makefile b/igt/Makefile index b6f9534bd..bb64db99a 100644 --- a/igt/Makefile +++ b/igt/Makefile @@ -91,6 +91,7 @@ config: $(info > Setting up config files) @mkdir -p $(OUT_DIR)/server @mkdir -p $(OUT_DIR)/pixiuconf + @mkdir -p $(OUT_DIR)/proto @-test -f $(PROJECT_DIR)/server/profiles/dev/log.yml && cat $(PROJECT_DIR)/server/profiles/dev/log.yml | sed "s#\$$HOST_IP#$(DOCKER_HOST_IP)#g" > $(OUT_DIR)/server/log.yml && echo " > $(OUT_DIR)/conf/log.yml" @-test -f $(PROJECT_DIR)/server/profiles/dev/server.yml && cat $(PROJECT_DIR)/server/profiles/dev/server.yml | sed "s#\$$HOST_IP#$(DOCKER_HOST_IP)#g" > $(OUT_DIR)/server/server.yml && echo " > $(OUT_DIR)/conf/server.yml" @-test -f $(PROJECT_DIR)/pixiu/api_config.yaml && cat $(PROJECT_DIR)/pixiu/api_config.yaml | sed "s#\$$HOST_IP#$(DOCKER_HOST_IP)#g" > $(OUT_DIR)/pixiuconf/api_config.yaml && echo " > $(OUT_DIR)/pixiuconf/api_config.yaml" diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index 58e76fcfb..7f33a8c2c 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -23,6 +23,8 @@ import ( "io" "io/ioutil" stdHttp "net/http" + "os" + "path/filepath" "strings" "sync" ) @@ -324,8 +326,19 @@ func (f *Filter) Config() interface{} { func (f *Filter) Apply() error { gc := f.cfg + + cur := gc.Path + if len(cur) != 0 && cur[0] != '/' { + ex, err := os.Executable() + if err != nil { + return err + } + cur = filepath.Dir(ex) + "/" + gc.Path + } + + logger.Infof("%s load proto files from %s", loggerHeader, cur) fileLists := make([]string, 0) - items, err := ioutil.ReadDir(gc.Path) + items, err := ioutil.ReadDir(cur) if err != nil { return err } diff --git a/samples/http/grpc/pixiu/conf.yaml b/samples/http/grpc/pixiu/conf.yaml index 19e3ec9bf..c69b99b07 100644 --- a/samples/http/grpc/pixiu/conf.yaml +++ b/samples/http/grpc/pixiu/conf.yaml @@ -43,10 +43,10 @@ static_resources: http_filters: - name: dgp.filter.http.apiconfig config: - path: /mnt/d/WorkSpace/GoLandProjects/dubbo-go-pixiu/samples/http/grpc/pixiu/api_config.yaml + path: $PROJECT_DIR/pixiu/api_config.yaml - name: dgp.filter.http.grpcproxy config: - path: /mnt/d/WorkSpace/GoLandProjects/dubbo-go-pixiu/samples/http/grpc/proto + path: $PROJECT_DIR/proto - name: dgp.filter.http.response config: server_name: "test-http-grpc" From cab427b96889a23f8680995525e70b044e4c15f0 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Wed, 8 Sep 2021 20:00:05 +0800 Subject: [PATCH 21/22] style: use context apis to get info --- pkg/filter/http/grpcproxy/grpc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/filter/http/grpcproxy/grpc.go b/pkg/filter/http/grpcproxy/grpc.go index 7f33a8c2c..04144d8e9 100644 --- a/pkg/filter/http/grpcproxy/grpc.go +++ b/pkg/filter/http/grpcproxy/grpc.go @@ -130,7 +130,7 @@ func getServiceAndMethod(path string) (string, string) { // Handle use the default http to grpc transcoding strategy https://cloud.google.com/endpoints/docs/grpc/transcoding func (f *Filter) Handle(c *http.HttpContext) { - svc, mth := getServiceAndMethod(c.Request.URL.Path) + svc, mth := getServiceAndMethod(c.GetUrl()) dscp, err := fsrc.FindSymbol(svc) if err != nil { @@ -203,7 +203,7 @@ func (f *Filter) Handle(c *http.HttpContext) { stub := grpcdynamic.NewStubWithMessageFactory(clientConn, msgFac) // metadata in grpc has the same feature in http - md := mapHeaderToMetadata(c.Request.Header) + md := mapHeaderToMetadata(c.AllHeaders()) ctx := metadata.NewOutgoingContext(c.Ctx, md) md = metadata.MD{} From 84bb6bb3779d69d8e465a9d53e074cd06d1a8148 Mon Sep 17 00:00:00 2001 From: MasterKenway Date: Sat, 18 Sep 2021 17:02:56 +0800 Subject: [PATCH 22/22] fix: remove unused functions --- pkg/filter/http/grpcproxy/invoke.go | 44 ----------------------------- 1 file changed, 44 deletions(-) diff --git a/pkg/filter/http/grpcproxy/invoke.go b/pkg/filter/http/grpcproxy/invoke.go index d56742273..305ef7762 100644 --- a/pkg/filter/http/grpcproxy/invoke.go +++ b/pkg/filter/http/grpcproxy/invoke.go @@ -27,7 +27,6 @@ import ( "github.com/jhump/protoreflect/dynamic/grpcdynamic" perrors "github.com/pkg/errors" "google.golang.org/grpc" - "google.golang.org/grpc/status" ) func Invoke(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message, opts ...grpc.CallOption) (proto.Message, error) { @@ -36,13 +35,10 @@ func Invoke(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDesc // Bi-direction Stream if mthDesc.IsServerStreaming() && mthDesc.IsClientStreaming() { err = perrors.New("currently not support bi-direction stream") - // resp, err = invokeBiDirectionStream(ctx, stub, mthDesc, grpcReq) } else if mthDesc.IsClientStreaming() { err = perrors.New("currently not support client side stream") - // resp, err = invokeClientStream(ctx, stub, mthDesc, grpcReq) } else if mthDesc.IsServerStreaming() { err = perrors.New("currently not support server side stream") - // resp, err = invokeServerStream(ctx, stub, mthDesc, grpcReq) } else { resp, err = invokeUnary(ctx, stub, mthDesc, grpcReq, opts...) } @@ -50,46 +46,6 @@ func Invoke(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDesc return resp, err } -func invokeBiDirectionStream(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message) (proto.Message, error) { - stream, err := stub.InvokeRpcBidiStream(ctx, mthDesc) - if _, ok := status.FromError(err); !ok { - return nil, err - } - defer func(stream *grpcdynamic.BidiStream) { - _ = stream.CloseSend() - }(stream) - - err = stream.SendMsg(grpcReq) - if _, ok := status.FromError(err); !ok { - return nil, err - } - - return stream.RecvMsg() -} - -func invokeClientStream(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message) (proto.Message, error) { - stream, err := stub.InvokeRpcClientStream(ctx, mthDesc) - if _, ok := status.FromError(err); !ok { - return nil, err - } - - err = stream.SendMsg(grpcReq) - if _, ok := status.FromError(err); !ok { - return nil, err - } - - return stream.CloseAndReceive() -} - -func invokeServerStream(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message) (proto.Message, error) { - stream, err := stub.InvokeRpcServerStream(ctx, mthDesc, grpcReq) - if _, ok := status.FromError(err); !ok { - return nil, err - } - - return stream.RecvMsg() -} - func invokeUnary(ctx context.Context, stub grpcdynamic.Stub, mthDesc *desc.MethodDescriptor, grpcReq proto.Message, opts ...grpc.CallOption) (proto.Message, error) { return stub.InvokeRpc(ctx, mthDesc, grpcReq, opts...) }