From a70ee32ebd99dc4a0b21e6eb808824929645c386 Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Sat, 31 Oct 2020 05:04:05 +0200 Subject: [PATCH] fix #1665 --- _examples/README.md | 1 + .../client/main.go | 49 ++ .../go.mod | 12 + .../helloworld.proto | 39 ++ .../helloworld/helloworld.pb.go | 242 ++++++++++ .../helloworld/helloworld_grpc.pb.go | 450 ++++++++++++++++++ .../server.crt | 17 + .../server.key | 27 ++ .../server/main.go | 50 ++ hero/binding.go | 25 +- mvc/grpc.go | 25 +- mvc/mvc.go | 5 +- 12 files changed, 927 insertions(+), 15 deletions(-) create mode 100644 _examples/mvc/grpc-compatible-bidirectional-stream/client/main.go create mode 100644 _examples/mvc/grpc-compatible-bidirectional-stream/go.mod create mode 100644 _examples/mvc/grpc-compatible-bidirectional-stream/helloworld.proto create mode 100644 _examples/mvc/grpc-compatible-bidirectional-stream/helloworld/helloworld.pb.go create mode 100644 _examples/mvc/grpc-compatible-bidirectional-stream/helloworld/helloworld_grpc.pb.go create mode 100644 _examples/mvc/grpc-compatible-bidirectional-stream/server.crt create mode 100644 _examples/mvc/grpc-compatible-bidirectional-stream/server.key create mode 100644 _examples/mvc/grpc-compatible-bidirectional-stream/server/main.go diff --git a/_examples/README.md b/_examples/README.md index 71839aa1..ee830ce6 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -251,6 +251,7 @@ * [Websocket Controller](mvc/websocket) * [Register Middleware](mvc/middleware) * [gRPC](mvc/grpc-compatible) + * [gRPC Bidirectional Stream](mvc/grpc-compatible-bidirectional-stream) * [Login (Repository and Service layers)](mvc/login) * [Login (Single Responsibility)](mvc/login-mvc-single-responsibility) * [Vue.js Todo App](mvc/vuejs-todo-mvc) diff --git a/_examples/mvc/grpc-compatible-bidirectional-stream/client/main.go b/_examples/mvc/grpc-compatible-bidirectional-stream/client/main.go new file mode 100644 index 00000000..9df86d65 --- /dev/null +++ b/_examples/mvc/grpc-compatible-bidirectional-stream/client/main.go @@ -0,0 +1,49 @@ +package main + +import ( + "context" + "time" + + pb "grpcexample/helloworld" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +func main() { + // Set up a connection to the server. + cred, err := credentials.NewClientTLSFromFile("../server.crt", "localhost") + if err != nil { + panic(err) + } + + conn, err := grpc.Dial("localhost:443", grpc.WithTransportCredentials(cred), grpc.WithBlock()) + defer conn.Close() + + client := pb.NewGreeterBidirectionalStreamClient(conn) + stream, err := client.SayHello(context.Background()) + if err != nil { + panic(err) + } + + waitCh := make(chan struct{}) + + // Implement the send channel. + // As an exercise you can implement the read channel one (reading from server, see the server/main.go). + go func() { + for { + println("Sleeping for 2 seconds...") + time.Sleep(2 * time.Second) + println("Sending a msg...") + msg := &pb.HelloRequest{Name: "test"} + + err = stream.Send(msg) + if err != nil { + panic("stream.Send: " + err.Error()) + } + } + }() + + <-waitCh + stream.CloseSend() +} diff --git a/_examples/mvc/grpc-compatible-bidirectional-stream/go.mod b/_examples/mvc/grpc-compatible-bidirectional-stream/go.mod new file mode 100644 index 00000000..4196b718 --- /dev/null +++ b/_examples/mvc/grpc-compatible-bidirectional-stream/go.mod @@ -0,0 +1,12 @@ +module grpcexample + +go 1.15 + +// replace github.com/kataras/iris/v12 => ../../../ + +require ( + github.com/golang/protobuf v1.4.2 + github.com/kataras/iris/v12 master + google.golang.org/grpc v1.33.1 + google.golang.org/protobuf v1.25.0 +) diff --git a/_examples/mvc/grpc-compatible-bidirectional-stream/helloworld.proto b/_examples/mvc/grpc-compatible-bidirectional-stream/helloworld.proto new file mode 100644 index 00000000..b61240f9 --- /dev/null +++ b/_examples/mvc/grpc-compatible-bidirectional-stream/helloworld.proto @@ -0,0 +1,39 @@ +syntax = "proto3"; + +package helloworld; + +option go_package = ".;helloworld"; + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The greeting service definition. (Server-side streaming RPC) +service GreeterServerSideSStream { + // Sends a greeting + rpc SayHello (HelloRequest) returns (stream HelloReply) {} +} + +// The greeting service definition. (Client-side streaming RPC) +service GreeterClientSideStream { + // Sends a greeting + rpc SayHello (stream HelloRequest) returns (HelloReply) {} +} + +// The greeting service definition. (Bidirectional streaming RPC) +service GreeterBidirectionalStream { + // Sends a greeting + rpc SayHello (stream HelloRequest) returns (stream HelloReply) {} +} diff --git a/_examples/mvc/grpc-compatible-bidirectional-stream/helloworld/helloworld.pb.go b/_examples/mvc/grpc-compatible-bidirectional-stream/helloworld/helloworld.pb.go new file mode 100644 index 00000000..2c84aaeb --- /dev/null +++ b/_examples/mvc/grpc-compatible-bidirectional-stream/helloworld/helloworld.pb.go @@ -0,0 +1,242 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.12.3 +// source: helloworld.proto + +package helloworld + +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 + +// The request message containing the user's name. +type HelloRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *HelloRequest) Reset() { + *x = HelloRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_helloworld_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloRequest) ProtoMessage() {} + +func (x *HelloRequest) ProtoReflect() protoreflect.Message { + mi := &file_helloworld_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 HelloRequest.ProtoReflect.Descriptor instead. +func (*HelloRequest) Descriptor() ([]byte, []int) { + return file_helloworld_proto_rawDescGZIP(), []int{0} +} + +func (x *HelloRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +// The response message containing the greetings +type HelloReply struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *HelloReply) Reset() { + *x = HelloReply{} + if protoimpl.UnsafeEnabled { + mi := &file_helloworld_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HelloReply) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HelloReply) ProtoMessage() {} + +func (x *HelloReply) ProtoReflect() protoreflect.Message { + mi := &file_helloworld_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 HelloReply.ProtoReflect.Descriptor instead. +func (*HelloReply) Descriptor() ([]byte, []int) { + return file_helloworld_proto_rawDescGZIP(), []int{1} +} + +func (x *HelloReply) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_helloworld_proto protoreflect.FileDescriptor + +var file_helloworld_proto_rawDesc = []byte{ + 0x0a, 0x10, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0a, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x22, 0x22, + 0x0a, 0x0c, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x22, 0x26, 0x0a, 0x0a, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, + 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, 0x32, 0x49, 0x0a, 0x07, 0x47, 0x72, + 0x65, 0x65, 0x74, 0x65, 0x72, 0x12, 0x3e, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, + 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, + 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, + 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x22, 0x00, 0x32, 0x5c, 0x0a, 0x18, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, + 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x69, 0x64, 0x65, 0x53, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x12, 0x40, 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, + 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, + 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x30, 0x01, 0x32, 0x5b, 0x0a, 0x17, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x53, 0x69, 0x64, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x40, + 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, + 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, + 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x28, 0x01, + 0x32, 0x60, 0x0a, 0x1a, 0x47, 0x72, 0x65, 0x65, 0x74, 0x65, 0x72, 0x42, 0x69, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x42, + 0x0a, 0x08, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, + 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, + 0x64, 0x2e, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x28, 0x01, + 0x30, 0x01, 0x42, 0x0e, 0x5a, 0x0c, 0x2e, 0x3b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_helloworld_proto_rawDescOnce sync.Once + file_helloworld_proto_rawDescData = file_helloworld_proto_rawDesc +) + +func file_helloworld_proto_rawDescGZIP() []byte { + file_helloworld_proto_rawDescOnce.Do(func() { + file_helloworld_proto_rawDescData = protoimpl.X.CompressGZIP(file_helloworld_proto_rawDescData) + }) + return file_helloworld_proto_rawDescData +} + +var file_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_helloworld_proto_goTypes = []interface{}{ + (*HelloRequest)(nil), // 0: helloworld.HelloRequest + (*HelloReply)(nil), // 1: helloworld.HelloReply +} +var file_helloworld_proto_depIdxs = []int32{ + 0, // 0: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest + 0, // 1: helloworld.GreeterServerSideSStream.SayHello:input_type -> helloworld.HelloRequest + 0, // 2: helloworld.GreeterClientSideStream.SayHello:input_type -> helloworld.HelloRequest + 0, // 3: helloworld.GreeterBidirectionalStream.SayHello:input_type -> helloworld.HelloRequest + 1, // 4: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply + 1, // 5: helloworld.GreeterServerSideSStream.SayHello:output_type -> helloworld.HelloReply + 1, // 6: helloworld.GreeterClientSideStream.SayHello:output_type -> helloworld.HelloReply + 1, // 7: helloworld.GreeterBidirectionalStream.SayHello:output_type -> helloworld.HelloReply + 4, // [4:8] is the sub-list for method output_type + 0, // [0:4] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_helloworld_proto_init() } +func file_helloworld_proto_init() { + if File_helloworld_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_helloworld_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_helloworld_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HelloReply); 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_helloworld_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 4, + }, + GoTypes: file_helloworld_proto_goTypes, + DependencyIndexes: file_helloworld_proto_depIdxs, + MessageInfos: file_helloworld_proto_msgTypes, + }.Build() + File_helloworld_proto = out.File + file_helloworld_proto_rawDesc = nil + file_helloworld_proto_goTypes = nil + file_helloworld_proto_depIdxs = nil +} diff --git a/_examples/mvc/grpc-compatible-bidirectional-stream/helloworld/helloworld_grpc.pb.go b/_examples/mvc/grpc-compatible-bidirectional-stream/helloworld/helloworld_grpc.pb.go new file mode 100644 index 00000000..1d9a7a2a --- /dev/null +++ b/_examples/mvc/grpc-compatible-bidirectional-stream/helloworld/helloworld_grpc.pb.go @@ -0,0 +1,450 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. + +package helloworld + +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 + +// GreeterClient is the client API for Greeter 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 GreeterClient interface { + // Sends a greeting + SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) +} + +type greeterClient struct { + cc grpc.ClientConnInterface +} + +func NewGreeterClient(cc grpc.ClientConnInterface) GreeterClient { + return &greeterClient{cc} +} + +func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { + out := new(HelloReply) + err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GreeterServer is the server API for Greeter service. +// All implementations must embed UnimplementedGreeterServer +// for forward compatibility +type GreeterServer interface { + // Sends a greeting + SayHello(context.Context, *HelloRequest) (*HelloReply, error) + mustEmbedUnimplementedGreeterServer() +} + +// UnimplementedGreeterServer must be embedded to have forward compatible implementations. +type UnimplementedGreeterServer struct { +} + +func (UnimplementedGreeterServer) SayHello(context.Context, *HelloRequest) (*HelloReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} +func (UnimplementedGreeterServer) mustEmbedUnimplementedGreeterServer() {} + +// UnsafeGreeterServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GreeterServer will +// result in compilation errors. +type UnsafeGreeterServer interface { + mustEmbedUnimplementedGreeterServer() +} + +func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) { + s.RegisterService(&_Greeter_serviceDesc, srv) +} + +func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GreeterServer).SayHello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/helloworld.Greeter/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Greeter_serviceDesc = grpc.ServiceDesc{ + ServiceName: "helloworld.Greeter", + HandlerType: (*GreeterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: _Greeter_SayHello_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "helloworld.proto", +} + +// GreeterServerSideSStreamClient is the client API for GreeterServerSideSStream 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 GreeterServerSideSStreamClient interface { + // Sends a greeting + SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (GreeterServerSideSStream_SayHelloClient, error) +} + +type greeterServerSideSStreamClient struct { + cc grpc.ClientConnInterface +} + +func NewGreeterServerSideSStreamClient(cc grpc.ClientConnInterface) GreeterServerSideSStreamClient { + return &greeterServerSideSStreamClient{cc} +} + +func (c *greeterServerSideSStreamClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (GreeterServerSideSStream_SayHelloClient, error) { + stream, err := c.cc.NewStream(ctx, &_GreeterServerSideSStream_serviceDesc.Streams[0], "/helloworld.GreeterServerSideSStream/SayHello", opts...) + if err != nil { + return nil, err + } + x := &greeterServerSideSStreamSayHelloClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type GreeterServerSideSStream_SayHelloClient interface { + Recv() (*HelloReply, error) + grpc.ClientStream +} + +type greeterServerSideSStreamSayHelloClient struct { + grpc.ClientStream +} + +func (x *greeterServerSideSStreamSayHelloClient) Recv() (*HelloReply, error) { + m := new(HelloReply) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// GreeterServerSideSStreamServer is the server API for GreeterServerSideSStream service. +// All implementations must embed UnimplementedGreeterServerSideSStreamServer +// for forward compatibility +type GreeterServerSideSStreamServer interface { + // Sends a greeting + SayHello(*HelloRequest, GreeterServerSideSStream_SayHelloServer) error + mustEmbedUnimplementedGreeterServerSideSStreamServer() +} + +// UnimplementedGreeterServerSideSStreamServer must be embedded to have forward compatible implementations. +type UnimplementedGreeterServerSideSStreamServer struct { +} + +func (UnimplementedGreeterServerSideSStreamServer) SayHello(*HelloRequest, GreeterServerSideSStream_SayHelloServer) error { + return status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} +func (UnimplementedGreeterServerSideSStreamServer) mustEmbedUnimplementedGreeterServerSideSStreamServer() { +} + +// UnsafeGreeterServerSideSStreamServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GreeterServerSideSStreamServer will +// result in compilation errors. +type UnsafeGreeterServerSideSStreamServer interface { + mustEmbedUnimplementedGreeterServerSideSStreamServer() +} + +func RegisterGreeterServerSideSStreamServer(s grpc.ServiceRegistrar, srv GreeterServerSideSStreamServer) { + s.RegisterService(&_GreeterServerSideSStream_serviceDesc, srv) +} + +func _GreeterServerSideSStream_SayHello_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(HelloRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(GreeterServerSideSStreamServer).SayHello(m, &greeterServerSideSStreamSayHelloServer{stream}) +} + +type GreeterServerSideSStream_SayHelloServer interface { + Send(*HelloReply) error + grpc.ServerStream +} + +type greeterServerSideSStreamSayHelloServer struct { + grpc.ServerStream +} + +func (x *greeterServerSideSStreamSayHelloServer) Send(m *HelloReply) error { + return x.ServerStream.SendMsg(m) +} + +var _GreeterServerSideSStream_serviceDesc = grpc.ServiceDesc{ + ServiceName: "helloworld.GreeterServerSideSStream", + HandlerType: (*GreeterServerSideSStreamServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "SayHello", + Handler: _GreeterServerSideSStream_SayHello_Handler, + ServerStreams: true, + }, + }, + Metadata: "helloworld.proto", +} + +// GreeterClientSideStreamClient is the client API for GreeterClientSideStream 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 GreeterClientSideStreamClient interface { + // Sends a greeting + SayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterClientSideStream_SayHelloClient, error) +} + +type greeterClientSideStreamClient struct { + cc grpc.ClientConnInterface +} + +func NewGreeterClientSideStreamClient(cc grpc.ClientConnInterface) GreeterClientSideStreamClient { + return &greeterClientSideStreamClient{cc} +} + +func (c *greeterClientSideStreamClient) SayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterClientSideStream_SayHelloClient, error) { + stream, err := c.cc.NewStream(ctx, &_GreeterClientSideStream_serviceDesc.Streams[0], "/helloworld.GreeterClientSideStream/SayHello", opts...) + if err != nil { + return nil, err + } + x := &greeterClientSideStreamSayHelloClient{stream} + return x, nil +} + +type GreeterClientSideStream_SayHelloClient interface { + Send(*HelloRequest) error + CloseAndRecv() (*HelloReply, error) + grpc.ClientStream +} + +type greeterClientSideStreamSayHelloClient struct { + grpc.ClientStream +} + +func (x *greeterClientSideStreamSayHelloClient) Send(m *HelloRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *greeterClientSideStreamSayHelloClient) CloseAndRecv() (*HelloReply, error) { + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + m := new(HelloReply) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// GreeterClientSideStreamServer is the server API for GreeterClientSideStream service. +// All implementations must embed UnimplementedGreeterClientSideStreamServer +// for forward compatibility +type GreeterClientSideStreamServer interface { + // Sends a greeting + SayHello(GreeterClientSideStream_SayHelloServer) error + mustEmbedUnimplementedGreeterClientSideStreamServer() +} + +// UnimplementedGreeterClientSideStreamServer must be embedded to have forward compatible implementations. +type UnimplementedGreeterClientSideStreamServer struct { +} + +func (UnimplementedGreeterClientSideStreamServer) SayHello(GreeterClientSideStream_SayHelloServer) error { + return status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} +func (UnimplementedGreeterClientSideStreamServer) mustEmbedUnimplementedGreeterClientSideStreamServer() { +} + +// UnsafeGreeterClientSideStreamServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GreeterClientSideStreamServer will +// result in compilation errors. +type UnsafeGreeterClientSideStreamServer interface { + mustEmbedUnimplementedGreeterClientSideStreamServer() +} + +func RegisterGreeterClientSideStreamServer(s grpc.ServiceRegistrar, srv GreeterClientSideStreamServer) { + s.RegisterService(&_GreeterClientSideStream_serviceDesc, srv) +} + +func _GreeterClientSideStream_SayHello_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(GreeterClientSideStreamServer).SayHello(&greeterClientSideStreamSayHelloServer{stream}) +} + +type GreeterClientSideStream_SayHelloServer interface { + SendAndClose(*HelloReply) error + Recv() (*HelloRequest, error) + grpc.ServerStream +} + +type greeterClientSideStreamSayHelloServer struct { + grpc.ServerStream +} + +func (x *greeterClientSideStreamSayHelloServer) SendAndClose(m *HelloReply) error { + return x.ServerStream.SendMsg(m) +} + +func (x *greeterClientSideStreamSayHelloServer) Recv() (*HelloRequest, error) { + m := new(HelloRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _GreeterClientSideStream_serviceDesc = grpc.ServiceDesc{ + ServiceName: "helloworld.GreeterClientSideStream", + HandlerType: (*GreeterClientSideStreamServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "SayHello", + Handler: _GreeterClientSideStream_SayHello_Handler, + ClientStreams: true, + }, + }, + Metadata: "helloworld.proto", +} + +// GreeterBidirectionalStreamClient is the client API for GreeterBidirectionalStream 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 GreeterBidirectionalStreamClient interface { + // Sends a greeting + SayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterBidirectionalStream_SayHelloClient, error) +} + +type greeterBidirectionalStreamClient struct { + cc grpc.ClientConnInterface +} + +func NewGreeterBidirectionalStreamClient(cc grpc.ClientConnInterface) GreeterBidirectionalStreamClient { + return &greeterBidirectionalStreamClient{cc} +} + +func (c *greeterBidirectionalStreamClient) SayHello(ctx context.Context, opts ...grpc.CallOption) (GreeterBidirectionalStream_SayHelloClient, error) { + stream, err := c.cc.NewStream(ctx, &_GreeterBidirectionalStream_serviceDesc.Streams[0], "/helloworld.GreeterBidirectionalStream/SayHello", opts...) + if err != nil { + return nil, err + } + x := &greeterBidirectionalStreamSayHelloClient{stream} + return x, nil +} + +type GreeterBidirectionalStream_SayHelloClient interface { + Send(*HelloRequest) error + Recv() (*HelloReply, error) + grpc.ClientStream +} + +type greeterBidirectionalStreamSayHelloClient struct { + grpc.ClientStream +} + +func (x *greeterBidirectionalStreamSayHelloClient) Send(m *HelloRequest) error { + return x.ClientStream.SendMsg(m) +} + +func (x *greeterBidirectionalStreamSayHelloClient) Recv() (*HelloReply, error) { + m := new(HelloReply) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// GreeterBidirectionalStreamServer is the server API for GreeterBidirectionalStream service. +// All implementations must embed UnimplementedGreeterBidirectionalStreamServer +// for forward compatibility +type GreeterBidirectionalStreamServer interface { + // Sends a greeting + SayHello(GreeterBidirectionalStream_SayHelloServer) error + mustEmbedUnimplementedGreeterBidirectionalStreamServer() +} + +// UnimplementedGreeterBidirectionalStreamServer must be embedded to have forward compatible implementations. +type UnimplementedGreeterBidirectionalStreamServer struct { +} + +func (UnimplementedGreeterBidirectionalStreamServer) SayHello(GreeterBidirectionalStream_SayHelloServer) error { + return status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} +func (UnimplementedGreeterBidirectionalStreamServer) mustEmbedUnimplementedGreeterBidirectionalStreamServer() { +} + +// UnsafeGreeterBidirectionalStreamServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to GreeterBidirectionalStreamServer will +// result in compilation errors. +type UnsafeGreeterBidirectionalStreamServer interface { + mustEmbedUnimplementedGreeterBidirectionalStreamServer() +} + +func RegisterGreeterBidirectionalStreamServer(s grpc.ServiceRegistrar, srv GreeterBidirectionalStreamServer) { + s.RegisterService(&_GreeterBidirectionalStream_serviceDesc, srv) +} + +func _GreeterBidirectionalStream_SayHello_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(GreeterBidirectionalStreamServer).SayHello(&greeterBidirectionalStreamSayHelloServer{stream}) +} + +type GreeterBidirectionalStream_SayHelloServer interface { + Send(*HelloReply) error + Recv() (*HelloRequest, error) + grpc.ServerStream +} + +type greeterBidirectionalStreamSayHelloServer struct { + grpc.ServerStream +} + +func (x *greeterBidirectionalStreamSayHelloServer) Send(m *HelloReply) error { + return x.ServerStream.SendMsg(m) +} + +func (x *greeterBidirectionalStreamSayHelloServer) Recv() (*HelloRequest, error) { + m := new(HelloRequest) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _GreeterBidirectionalStream_serviceDesc = grpc.ServiceDesc{ + ServiceName: "helloworld.GreeterBidirectionalStream", + HandlerType: (*GreeterBidirectionalStreamServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "SayHello", + Handler: _GreeterBidirectionalStream_SayHello_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "helloworld.proto", +} diff --git a/_examples/mvc/grpc-compatible-bidirectional-stream/server.crt b/_examples/mvc/grpc-compatible-bidirectional-stream/server.crt new file mode 100644 index 00000000..ad21f14b --- /dev/null +++ b/_examples/mvc/grpc-compatible-bidirectional-stream/server.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICwzCCAaugAwIBAgIJANAsSlhuqzW/MA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV +BAMTCWxvY2FsaG9zdDAeFw0yMDEwMzEwMjM4MTNaFw0zMDEwMjkwMjM4MTNaMBQx +EjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC +ggEBAKY8mFh0nvXa8V6Vx5TVVbfq9Zx81hiIKeXN977JaQGrPfCMRU61smaDJAzR +MBEFjHT37Zj5sZr6qvQmt3Dfst8bNcFBsZ7s3bBknuevD3gPJBy1r5KFwSkPZekH +T+0DF0Drq1/iIZQXE13udWzDnzxABUBOmIzEmn+rfp2j3xJJGLoO1O4Wu2YpXhtj +yNJfswoztGGie6u61mAN7Rw4JQUkDKa3hAKkYtfOoGHZBs6Y0RIKZQikjxlVpMMc +fCvUMHkY51GP8ycgZ8+0n/cjm13KkqfvoyRZ1rQqCd24YNIEl18t0FClpUAHkxD/ +aUPCqV3bqpFBbDPQcYybIGjfNIMCAwEAAaMYMBYwFAYDVR0RBA0wC4IJbG9jYWxo +b3N0MA0GCSqGSIb3DQEBBQUAA4IBAQAd6F6tG/jcUu9pm/5XeH1Iof5PB9tFuBvv +w9HFjBI5S7ItnTepneoOP26lx2iOE0GwkvrhseZcHkGDZPdncBn12PuCHUbcO2Ux +bEDyDMsAHooKLvmAfAWeiOmeJAdFl8DOTgD/lTHK1n/mEwCUOtGozmfm/Y0pnfc7 +nMgbQ+yNsj1X7HVJHRUdOfOCGjiWofo6v7V7YyZJC2jpn3K7O4126jic2ibWYKQl +fLLjgS0N5Wcun117e1mRYV41jtPOkeAvxJbJGf0IP7Os0VfG1UQhodwjkV4s8GcG +naB5qC4Y508cB7Lq58kbGgZ0nfMCcpnrKobliIZVEpH7aa0Po1lm +-----END CERTIFICATE----- diff --git a/_examples/mvc/grpc-compatible-bidirectional-stream/server.key b/_examples/mvc/grpc-compatible-bidirectional-stream/server.key new file mode 100644 index 00000000..5b7681d7 --- /dev/null +++ b/_examples/mvc/grpc-compatible-bidirectional-stream/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEApjyYWHSe9drxXpXHlNVVt+r1nHzWGIgp5c33vslpAas98IxF +TrWyZoMkDNEwEQWMdPftmPmxmvqq9Ca3cN+y3xs1wUGxnuzdsGSe568PeA8kHLWv +koXBKQ9l6QdP7QMXQOurX+IhlBcTXe51bMOfPEAFQE6YjMSaf6t+naPfEkkYug7U +7ha7ZileG2PI0l+zCjO0YaJ7q7rWYA3tHDglBSQMpreEAqRi186gYdkGzpjREgpl +CKSPGVWkwxx8K9QweRjnUY/zJyBnz7Sf9yObXcqSp++jJFnWtCoJ3bhg0gSXXy3Q +UKWlQAeTEP9pQ8KpXduqkUFsM9BxjJsgaN80gwIDAQABAoIBACDVNwHBhuPoKmQU +ESdEO3nn3jraLS8LNbs9wwDbpvG9cK5iBg5VtLaqkCQ37NZv0h4IGdVs+7cwazNt +si2JATsvlJ5m6z4IaoC8XuZDnTqJQwiomdTGti/16prr5s1ZHu6jnWWCtD8bj6et +wWOJ/5lWy7K300l6S0mMBaX9B8IEfGR+YPGF/bfsIlf+9iuVulBjH5NPIozwl4c9 +VUafESEZX0DM/KDHqFvvWdMaw9e+3QNi+l0t3VJz6JuEcUfRztT6lbzYbRGBYqKr +DmUNxYUThd717NJklxr9D+GMm5FnfrzY8vcJ16SILBQyDthsz1s6gdT0yeRFgjJf +afDObAECgYEA1WQdayAeqT7sE0T0jHAWHdVhRd9sGUTZ4LjVuGbMjbJWJVUPn/fz +ahBQh77xutmNCbDSUD0srT+zHtRRJUVhl+ojFS1CMyyL/UAfH24gDwUnXIyq/DPA +heXKD8NwGoTPbHKjdmy+89qVbes3oxBc5tDKpzAuBoPY4iBMVwfqSmECgYEAx24Y +i1lhxuG+g2YSjiIhmD/ytZSXamP3+KmnqWWXbEjJd2oYmCPr9yBCfdk9jCUXZJsg +tYsRduMajTjmTNkYmGyZNVtmjXfrq/vaJgf7bZhhLOFxWoNmLlKQfmbuJW5fkV1b +hY8oid+YbBob4xseFzKvm5j0aKwufi3v7XLUEWMCgYEAs7rqKFNaX9SWhDhc/Xhe +uGwDzRU8eCAMnwEvSWyUN3iQpEr7qRHvXFM3cM47zdP0vcfHrDuKSLXRSVMssYa5 +h3l2aRzAmFeZ5Qk/7XoU2HHP0FzOmzN/oYeE5DgJUNyx1DbORS2cu8lMeNNX/ikH +BoWvWpfy/BvK7dKkWd1Z0aECgYEAm1+QKcjqX5ty5UZ6AFhhGhAAVS2+Rfo6sHXl +FRn8PjX7GFkFbkrWRUPR6eB9jhk7v3sIocgGRDytbAc/jfG5ss8xEhvyqxcZ+nUO +QYEIhxsn4mKGAMHMsxxKTOB+e5UhScyVSFn/eGNGijpRLb/r0qD/pdcl3AMBefbq +LXG//QcCgYBrYTCaKYDg/TSMt6fTSZgJRaui8PvvlLjZy63vrVCexVUibiCpbUt9 +sHe9gbSDPr/uCyNgKnZjDbVabmZboVYqHBDQnXPUYYx9dBSL2DzE3JXA6Q8RVnOY +zwCPzVxYbE9h02ishAgc6k9w61XUaDnASmBZAnlxt9/8cB7+IZAlfg== +-----END RSA PRIVATE KEY----- diff --git a/_examples/mvc/grpc-compatible-bidirectional-stream/server/main.go b/_examples/mvc/grpc-compatible-bidirectional-stream/server/main.go new file mode 100644 index 00000000..fc484b90 --- /dev/null +++ b/_examples/mvc/grpc-compatible-bidirectional-stream/server/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "io" + + pb "grpcexample/helloworld" + + "github.com/kataras/iris/v12" + "github.com/kataras/iris/v12/mvc" + + "google.golang.org/grpc" +) + +type Greeter struct { + pb.UnimplementedGreeterBidirectionalStreamServer +} + +// SayHello implements the proto Bidirectional Stream Greeter service. +func (g *Greeter) SayHello(stream pb.GreeterBidirectionalStream_SayHelloServer) error { + for { + in, err := stream.Recv() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + println("Received input: " + in.Name) + // On client side you can implement the 'read' operation too. + stream.Send(&pb.HelloReply{Message: "Hello " + in.Name}) + } +} + +func main() { + app := iris.New() + + grpcServer := grpc.NewServer() + + myService := &Greeter{} + pb.RegisterGreeterBidirectionalStreamServer(grpcServer, myService) + + rootApp := mvc.New(app) + rootApp.Handle(myService, mvc.GRPC{ + Server: grpcServer, // Required. + ServiceName: "helloworld.GreeterBidirectionalStream", // Required. + Strict: true, // Set it to true on gRPC streaming. + }) + + app.Run(iris.TLS(":443", "../server.crt", "../server.key")) +} diff --git a/hero/binding.go b/hero/binding.go index 10318282..137743ff 100644 --- a/hero/binding.go +++ b/hero/binding.go @@ -248,8 +248,29 @@ func getBindingsForFunc(fn reflect.Value, dependencies []*Dependency, paramsCoun } bindings := getBindingsFor(inputs, dependencies, paramsCount) - if expected, got := n, len(bindings); expected > got { - panic(fmt.Sprintf("expected [%d] bindings (input parameters) but got [%d]", expected, got)) + if expected, got := n, len(bindings); expected != got { + expectedInputs := "" + missingInputs := "" + for i, in := range inputs { + pos := i + 1 + typName := in.String() + expectedInputs += fmt.Sprintf("\n - [%d] %s", pos, typName) + found := false + for _, b := range bindings { + if b.Input.Index == i { + found = true + break + } + } + + if !found { + missingInputs += fmt.Sprintf("\n - [%d] %s", pos, typName) + } + } + + fnName := context.HandlerName(fn) + panic(fmt.Sprintf("expected [%d] bindings (input parameters) but got [%d]\nFunction:\n - %s\nExpected:%s\nMissing:%s", + expected, got, fnName, expectedInputs, missingInputs)) } return bindings diff --git a/mvc/grpc.go b/mvc/grpc.go index e488a74c..e586a427 100644 --- a/mvc/grpc.go +++ b/mvc/grpc.go @@ -47,24 +47,25 @@ func (g GRPC) Apply(c *ControllerActivator) { return } - if g.Strict { - ctx.NotFound() - } else { - // Allow common HTTP clients, consumes and produces JSON. - ctx.Next() - } + // If strict was false, allow common HTTP clients, consumes and produces JSON. + ctx.Next() } for i := 0; i < c.Type.NumMethod(); i++ { m := c.Type.Method(i) path := path.Join(g.ServiceName, m.Name) - if route := c.Handle(http.MethodPost, path, m.Name, pre); route != nil { - bckp := route.Description - route.Description = "gRPC" - if g.Strict { - route.Description += "-only" + if g.Strict { + c.app.Router.HandleMany(http.MethodPost, path, pre) + } else { + if route := c.Handle(http.MethodPost, path, m.Name, pre); route != nil { + bckp := route.Description + route.Description = "gRPC" + if g.Strict { + route.Description += "-only" + } + route.Description += " " + bckp // e.g. "gRPC controller" } - route.Description += " " + bckp // e.g. "gRPC controller" } + } } diff --git a/mvc/mvc.go b/mvc/mvc.go index 1bd72814..204c8fb7 100644 --- a/mvc/mvc.go +++ b/mvc/mvc.go @@ -366,6 +366,10 @@ func logController(logger *golog.Logger, c *ControllerActivator) { return } + if c.injector == nil { // when no actual controller methods are registered. + return + } + /* [DBUG] controller.GreetController ╺ Service → ./service/greet_service.go:16 @@ -381,7 +385,6 @@ func logController(logger *golog.Logger, c *ControllerActivator) { logger.TimeFormat = "" printer := logger.Printer - reports := c.injector.Container.Reports ctrlName := c.RelName() ctrlScopeType := ""