From 4cc4bbe7a585811db45d763106643304294515a7 Mon Sep 17 00:00:00 2001 From: kingluo Date: Sun, 22 Jan 2023 19:12:24 +0800 Subject: [PATCH 01/10] fix: grpc-transcode plugin: fix map data population --- apisix/plugins/grpc-transcode/util.lua | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/apisix/plugins/grpc-transcode/util.lua b/apisix/plugins/grpc-transcode/util.lua index dc4526195639..092779ff6ee0 100644 --- a/apisix/plugins/grpc-transcode/util.lua +++ b/apisix/plugins/grpc-transcode/util.lua @@ -131,7 +131,7 @@ local function get_from_request(request_table, name, kind) end -function _M.map_message(field, default_values, request_table) +function _M.map_message(field, default_values, request_table, real_key) if not pb.type(field) then return nil, "Field " .. field .. " is not defined" end @@ -164,8 +164,23 @@ function _M.map_message(field, default_values, request_table) end sub = sub_array else - sub, err = _M.map_message(field_type, default_values and default_values[name], - request_table[name]) + if ty == "map" then + local tbl + for k, v in pairs(request_table[name]) do + tbl, err = _M.map_message(field_type, + default_values and default_values[name], + request_table[name], k) + if not sub then + sub = {} + end + sub[k]= tbl[k] + end + else + sub, err = _M.map_message(field_type, + default_values and default_values[name], + request_table[name]) + end + if err then return nil, err end @@ -173,6 +188,9 @@ function _M.map_message(field, default_values, request_table) request[name] = sub else + if real_key then + name = real_key + end request[name] = get_from_request(request_table, name, field_type) or (default_values and default_values[name]) end From 3f1246fc3d872a526bac457e3dae2bdb40e170b7 Mon Sep 17 00:00:00 2001 From: kingluo Date: Sat, 28 Jan 2023 16:07:54 +0800 Subject: [PATCH 02/10] add test --- apisix/plugins/grpc-transcode/util.lua | 1 + t/grpc_server_example/go.mod | 1 + t/grpc_server_example/helloworld.pb | Bin 0 -> 2424 bytes t/grpc_server_example/main.go | 9 + t/grpc_server_example/proto/helloworld.pb.go | 358 ++++++++++++------ t/grpc_server_example/proto/helloworld.proto | 11 + .../proto/helloworld_grpc.pb.go | 38 +- t/plugin/grpc-transcode-struct.t | 109 ++++++ 8 files changed, 417 insertions(+), 110 deletions(-) create mode 100644 t/grpc_server_example/helloworld.pb create mode 100644 t/plugin/grpc-transcode-struct.t diff --git a/apisix/plugins/grpc-transcode/util.lua b/apisix/plugins/grpc-transcode/util.lua index 092779ff6ee0..e581a610b78e 100644 --- a/apisix/plugins/grpc-transcode/util.lua +++ b/apisix/plugins/grpc-transcode/util.lua @@ -22,6 +22,7 @@ local ngx = ngx local string = string local table = table local ipairs = ipairs +local pairs = pairs local tonumber = tonumber local type = type diff --git a/t/grpc_server_example/go.mod b/t/grpc_server_example/go.mod index 8d36bae682b7..480e4f0c6975 100644 --- a/t/grpc_server_example/go.mod +++ b/t/grpc_server_example/go.mod @@ -3,6 +3,7 @@ module github.com/api7/grpc_server_example go 1.11 require ( + github.com/golang/protobuf v1.5.0 // indirect golang.org/x/net v0.2.0 google.golang.org/grpc v1.32.0 google.golang.org/protobuf v1.27.1 diff --git a/t/grpc_server_example/helloworld.pb b/t/grpc_server_example/helloworld.pb new file mode 100644 index 0000000000000000000000000000000000000000..f03a778e517b3541208ef37b5a3c541fcfe448f9 GIT binary patch literal 2424 zcmbuA&uzmUHd)66fw|4R_jzyLd-I)L_`e9ZhEX)+YjD!H5AwCfx0CD_%V`&3cQFcDMFgw%co zJ7cC5$iHS&Ohh)zo?OyB=2$qq@X`3uXMC|&Fxk~jMO?t$2GZf6loKAW!qT9oW`{D3HisNKDX0cJ^jvm#x_nwe z>kqX~i=JFMe-T*Tm&bYRcbsPO(Xcfr1_>L*DDXUrUT^3YSVI;Ln5argt=6e~QKj2b z9?uYx9jmgcQd{;cs|)rF;hX}PipG3Ot56!TIF@DWCzo#NS6Aiy0Cty8;z z?-8HkuBTo!?t0D!lr<`AS_ez8c)}Ayo1OCyfwkvo4#u1TiosY6->?Cg?A4?ZBTZmC$PT z helloworld.Gender @@ -703,27 +815,31 @@ var file_proto_helloworld_proto_depIdxs = []int32{ 0, // 3: helloworld.MultipleHelloRequest.genders:type_name -> helloworld.Gender 1, // 4: helloworld.MultipleHelloRequest.persons:type_name -> helloworld.Person 0, // 5: helloworld.MultipleHelloReply.genders:type_name -> helloworld.Gender - 2, // 6: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest - 2, // 7: helloworld.Greeter.GetErrResp:input_type -> helloworld.HelloRequest - 4, // 8: helloworld.Greeter.Plus:input_type -> helloworld.PlusRequest - 2, // 9: helloworld.Greeter.SayHelloAfterDelay:input_type -> helloworld.HelloRequest - 6, // 10: helloworld.Greeter.SayMultipleHello:input_type -> helloworld.MultipleHelloRequest - 2, // 11: helloworld.Greeter.SayHelloServerStream:input_type -> helloworld.HelloRequest - 2, // 12: helloworld.Greeter.SayHelloClientStream:input_type -> helloworld.HelloRequest - 2, // 13: helloworld.Greeter.SayHelloBidirectionalStream:input_type -> helloworld.HelloRequest - 3, // 14: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply - 3, // 15: helloworld.Greeter.GetErrResp:output_type -> helloworld.HelloReply - 5, // 16: helloworld.Greeter.Plus:output_type -> helloworld.PlusReply - 3, // 17: helloworld.Greeter.SayHelloAfterDelay:output_type -> helloworld.HelloReply - 7, // 18: helloworld.Greeter.SayMultipleHello:output_type -> helloworld.MultipleHelloReply - 3, // 19: helloworld.Greeter.SayHelloServerStream:output_type -> helloworld.HelloReply - 3, // 20: helloworld.Greeter.SayHelloClientStream:output_type -> helloworld.HelloReply - 3, // 21: helloworld.Greeter.SayHelloBidirectionalStream:output_type -> helloworld.HelloReply - 14, // [14:22] is the sub-list for method output_type - 6, // [6:14] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 11, // 6: helloworld.StructRequest.data:type_name -> google.protobuf.Struct + 11, // 7: helloworld.StructReply.data:type_name -> google.protobuf.Struct + 2, // 8: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest + 2, // 9: helloworld.Greeter.GetErrResp:input_type -> helloworld.HelloRequest + 4, // 10: helloworld.Greeter.Plus:input_type -> helloworld.PlusRequest + 2, // 11: helloworld.Greeter.SayHelloAfterDelay:input_type -> helloworld.HelloRequest + 6, // 12: helloworld.Greeter.SayMultipleHello:input_type -> helloworld.MultipleHelloRequest + 9, // 13: helloworld.Greeter.EchoStruct:input_type -> helloworld.StructRequest + 2, // 14: helloworld.Greeter.SayHelloServerStream:input_type -> helloworld.HelloRequest + 2, // 15: helloworld.Greeter.SayHelloClientStream:input_type -> helloworld.HelloRequest + 2, // 16: helloworld.Greeter.SayHelloBidirectionalStream:input_type -> helloworld.HelloRequest + 3, // 17: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply + 3, // 18: helloworld.Greeter.GetErrResp:output_type -> helloworld.HelloReply + 5, // 19: helloworld.Greeter.Plus:output_type -> helloworld.PlusReply + 3, // 20: helloworld.Greeter.SayHelloAfterDelay:output_type -> helloworld.HelloReply + 7, // 21: helloworld.Greeter.SayMultipleHello:output_type -> helloworld.MultipleHelloReply + 10, // 22: helloworld.Greeter.EchoStruct:output_type -> helloworld.StructReply + 3, // 23: helloworld.Greeter.SayHelloServerStream:output_type -> helloworld.HelloReply + 3, // 24: helloworld.Greeter.SayHelloClientStream:output_type -> helloworld.HelloReply + 3, // 25: helloworld.Greeter.SayHelloBidirectionalStream:output_type -> helloworld.HelloReply + 17, // [17:26] is the sub-list for method output_type + 8, // [8:17] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_proto_helloworld_proto_init() } @@ -828,6 +944,30 @@ func file_proto_helloworld_proto_init() { return nil } } + file_proto_helloworld_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StructRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_helloworld_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StructReply); 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{ @@ -835,7 +975,7 @@ func file_proto_helloworld_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_helloworld_proto_rawDesc, NumEnums: 1, - NumMessages: 8, + NumMessages: 10, NumExtensions: 0, NumServices: 1, }, diff --git a/t/grpc_server_example/proto/helloworld.proto b/t/grpc_server_example/proto/helloworld.proto index 28f711a9037f..caf0d7250c1e 100644 --- a/t/grpc_server_example/proto/helloworld.proto +++ b/t/grpc_server_example/proto/helloworld.proto @@ -20,6 +20,8 @@ syntax = "proto3"; package helloworld; option go_package = "./proto"; +import "google/protobuf/struct.proto"; + service Greeter { // Unary RPC. rpc SayHello (HelloRequest) returns (HelloReply) {} @@ -27,6 +29,7 @@ service Greeter { rpc Plus (PlusRequest) returns (PlusReply) {} rpc SayHelloAfterDelay (HelloRequest) returns (HelloReply) {} rpc SayMultipleHello(MultipleHelloRequest) returns (MultipleHelloReply) {} + rpc EchoStruct (StructRequest) returns (StructReply) {} // Server side streaming. rpc SayHelloServerStream (HelloRequest) returns (stream HelloReply) {} @@ -90,3 +93,11 @@ message ErrorDetail { string message = 2; string type = 3; } + +message StructRequest { + google.protobuf.Struct data = 1; +} + +message StructReply { + google.protobuf.Struct data = 1; +} diff --git a/t/grpc_server_example/proto/helloworld_grpc.pb.go b/t/grpc_server_example/proto/helloworld_grpc.pb.go index 93147244c1f1..5fd396f23889 100644 --- a/t/grpc_server_example/proto/helloworld_grpc.pb.go +++ b/t/grpc_server_example/proto/helloworld_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v3.12.4 +// - protoc v3.6.1 // source: proto/helloworld.proto package proto @@ -28,6 +28,7 @@ type GreeterClient interface { Plus(ctx context.Context, in *PlusRequest, opts ...grpc.CallOption) (*PlusReply, error) SayHelloAfterDelay(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) SayMultipleHello(ctx context.Context, in *MultipleHelloRequest, opts ...grpc.CallOption) (*MultipleHelloReply, error) + EchoStruct(ctx context.Context, in *StructRequest, opts ...grpc.CallOption) (*StructReply, error) // Server side streaming. SayHelloServerStream(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (Greeter_SayHelloServerStreamClient, error) // Client side streaming. @@ -89,6 +90,15 @@ func (c *greeterClient) SayMultipleHello(ctx context.Context, in *MultipleHelloR return out, nil } +func (c *greeterClient) EchoStruct(ctx context.Context, in *StructRequest, opts ...grpc.CallOption) (*StructReply, error) { + out := new(StructReply) + err := c.cc.Invoke(ctx, "/helloworld.Greeter/EchoStruct", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *greeterClient) SayHelloServerStream(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (Greeter_SayHelloServerStreamClient, error) { stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[0], "/helloworld.Greeter/SayHelloServerStream", opts...) if err != nil { @@ -196,6 +206,7 @@ type GreeterServer interface { Plus(context.Context, *PlusRequest) (*PlusReply, error) SayHelloAfterDelay(context.Context, *HelloRequest) (*HelloReply, error) SayMultipleHello(context.Context, *MultipleHelloRequest) (*MultipleHelloReply, error) + EchoStruct(context.Context, *StructRequest) (*StructReply, error) // Server side streaming. SayHelloServerStream(*HelloRequest, Greeter_SayHelloServerStreamServer) error // Client side streaming. @@ -224,6 +235,9 @@ func (UnimplementedGreeterServer) SayHelloAfterDelay(context.Context, *HelloRequ func (UnimplementedGreeterServer) SayMultipleHello(context.Context, *MultipleHelloRequest) (*MultipleHelloReply, error) { return nil, status.Errorf(codes.Unimplemented, "method SayMultipleHello not implemented") } +func (UnimplementedGreeterServer) EchoStruct(context.Context, *StructRequest) (*StructReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method EchoStruct not implemented") +} func (UnimplementedGreeterServer) SayHelloServerStream(*HelloRequest, Greeter_SayHelloServerStreamServer) error { return status.Errorf(codes.Unimplemented, "method SayHelloServerStream not implemented") } @@ -336,6 +350,24 @@ func _Greeter_SayMultipleHello_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } +func _Greeter_EchoStruct_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StructRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GreeterServer).EchoStruct(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/helloworld.Greeter/EchoStruct", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GreeterServer).EchoStruct(ctx, req.(*StructRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Greeter_SayHelloServerStream_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(HelloRequest) if err := stream.RecvMsg(m); err != nil { @@ -436,6 +468,10 @@ var Greeter_ServiceDesc = grpc.ServiceDesc{ MethodName: "SayMultipleHello", Handler: _Greeter_SayMultipleHello_Handler, }, + { + MethodName: "EchoStruct", + Handler: _Greeter_EchoStruct_Handler, + }, }, Streams: []grpc.StreamDesc{ { diff --git a/t/plugin/grpc-transcode-struct.t b/t/plugin/grpc-transcode-struct.t new file mode 100644 index 000000000000..039de98d5f0c --- /dev/null +++ b/t/plugin/grpc-transcode-struct.t @@ -0,0 +1,109 @@ +# +# 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. +# +use t::APISIX 'no_plan'; + +no_long_string(); +no_shuffle(); +no_root_location(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set binary rule +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin") + local json = require("toolkit.json") + + local content = t.read_file("t/grpc_server_example/helloworld.pb") + local data = {content = ngx.encode_base64(content)} + local code, body = t.test('/apisix/admin/protos/1', + ngx.HTTP_PUT, + json.encode(data) + ) + + if code >= 300 then + ngx.status = code + ngx.say(body) + return + end + + local code, body = t.test('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/grpctest", + "plugins": { + "grpc-transcode": { + "proto_id": "1", + "service": "helloworld.Greeter", + "method": "EchoStruct" + } + }, + "upstream": { + "scheme": "grpc", + "type": "roundrobin", + "nodes": { + "127.0.0.1:50051": 1 + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: hit route +--- config +location /t { + content_by_lua_block { + local core = require "apisix.core" + local http = require "resty.http" + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/grpctest" + local body = [[{"data":{"fields":{"foo":{"string_value":"xxx"},"bar":{"number_value":666}}}}]] + local opt = {method = "POST", body = body, headers = {["Content-Type"] = "application/json"}, keepalive = false} + local httpc = http.new() + local res, err = httpc:request_uri(uri, opt) + if not res then + ngx.log(ngx.ERR, err) + return ngx.exit(500) + end + if res.status > 300 then + return ngx.exit(res.status) + else + local rsp = core.json.decode(res.body) + assert(core.table.deep_eq(rsp, core.json.decode(body))) + end + } +} From f21a7306d0e71db335f4d9159276e9460cb23d06 Mon Sep 17 00:00:00 2001 From: kingluo Date: Sat, 28 Jan 2023 16:24:05 +0800 Subject: [PATCH 03/10] fix(eclint): ignore end_of_line for *.pb --- .editorconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/.editorconfig b/.editorconfig index 79eed820e3e0..62357920aa2e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -50,3 +50,4 @@ indent_style = unset indent_style = unset insert_final_newline = unset trim_trailing_whitespace = unset +end_of_line = unset From 6180cc7d3d07ef31ebd3ca330a2baa54771c5a1d Mon Sep 17 00:00:00 2001 From: kingluo Date: Sat, 28 Jan 2023 17:15:04 +0800 Subject: [PATCH 04/10] add struct.proto --- .../proto/google/protobuf/struct.proto | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 t/grpc_server_example/proto/google/protobuf/struct.proto diff --git a/t/grpc_server_example/proto/google/protobuf/struct.proto b/t/grpc_server_example/proto/google/protobuf/struct.proto new file mode 100644 index 000000000000..c4ea6453eaec --- /dev/null +++ b/t/grpc_server_example/proto/google/protobuf/struct.proto @@ -0,0 +1,95 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/protobuf/types/known/structpb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "StructProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// `Struct` represents a structured data value, consisting of fields +// which map to dynamically typed values. In some languages, `Struct` +// might be supported by a native representation. For example, in +// scripting languages like JS a struct is represented as an +// object. The details of that representation are described together +// with the proto support for the language. +// +// The JSON representation for `Struct` is JSON object. +message Struct { + // Unordered map of dynamically typed values. + map fields = 1; +} + +// `Value` represents a dynamically typed value which can be either +// null, a number, a string, a boolean, a recursive struct value, or a +// list of values. A producer of value is expected to set one of these +// variants. Absence of any variant indicates an error. +// +// The JSON representation for `Value` is JSON value. +message Value { + // The kind of value. + oneof kind { + // Represents a null value. + NullValue null_value = 1; + // Represents a double value. + double number_value = 2; + // Represents a string value. + string string_value = 3; + // Represents a boolean value. + bool bool_value = 4; + // Represents a structured value. + Struct struct_value = 5; + // Represents a repeated `Value`. + ListValue list_value = 6; + } +} + +// `NullValue` is a singleton enumeration to represent the null value for the +// `Value` type union. +// +// The JSON representation for `NullValue` is JSON `null`. +enum NullValue { + // Null value. + NULL_VALUE = 0; +} + +// `ListValue` is a wrapper around a repeated field of values. +// +// The JSON representation for `ListValue` is JSON array. +message ListValue { + // Repeated field of dynamically typed values. + repeated Value values = 1; +} From 9751ae5f78f46ab0033f43f72aa057d8eae09552 Mon Sep 17 00:00:00 2001 From: kingluo Date: Sat, 28 Jan 2023 17:23:03 +0800 Subject: [PATCH 05/10] fix .licenserc.yaml --- .licenserc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.licenserc.yaml b/.licenserc.yaml index ea5863015302..72c4abcf1836 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -47,5 +47,6 @@ header: - 't/plugin/authz-casbin' - 't/coredns' - 'autodocs/' + - 't/grpc_server_example/proto/google' comment: on-failure From 7b5786e26d2d065054600fb2cc6364a3e2432278 Mon Sep 17 00:00:00 2001 From: kingluo Date: Sat, 28 Jan 2023 18:30:17 +0800 Subject: [PATCH 06/10] log body if failed --- t/plugin/grpc-transcode-struct.t | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/t/plugin/grpc-transcode-struct.t b/t/plugin/grpc-transcode-struct.t index 039de98d5f0c..0263918979ca 100644 --- a/t/plugin/grpc-transcode-struct.t +++ b/t/plugin/grpc-transcode-struct.t @@ -102,8 +102,11 @@ location /t { if res.status > 300 then return ngx.exit(res.status) else + local req = core.json.decode(body) local rsp = core.json.decode(res.body) - assert(core.table.deep_eq(rsp, core.json.decode(body))) + if not core.table.deep_eq(req, rsp) then + ngx.log(ngx.ERR, "failed: req=", body, ", rsp=", res.body) + end end } } From 30655f82e6168cb4bcdb4b031bc8836538ae75f9 Mon Sep 17 00:00:00 2001 From: kingluo Date: Sun, 29 Jan 2023 13:57:56 +0800 Subject: [PATCH 07/10] fix --- t/grpc_server_example/echo.pb | Bin 0 -> 997 bytes t/grpc_server_example/helloworld.pb | Bin 2424 -> 0 bytes t/grpc_server_example/main.go | 5 +- t/grpc_server_example/proto/echo.pb.go | 236 ++++++++++++ t/grpc_server_example/proto/echo.proto | 35 ++ t/grpc_server_example/proto/echo_grpc.pb.go | 105 +++++ .../proto/google/protobuf/struct.proto | 95 ----- t/grpc_server_example/proto/helloworld.pb.go | 358 ++++++------------ t/grpc_server_example/proto/helloworld.proto | 11 - .../proto/helloworld_grpc.pb.go | 38 +- t/plugin/grpc-transcode-struct.t | 17 +- t/plugin/grpc-web/client.js | 4 +- t/plugin/grpc-web/server.go | 2 + 13 files changed, 507 insertions(+), 399 deletions(-) create mode 100644 t/grpc_server_example/echo.pb delete mode 100644 t/grpc_server_example/helloworld.pb create mode 100644 t/grpc_server_example/proto/echo.pb.go create mode 100644 t/grpc_server_example/proto/echo.proto create mode 100644 t/grpc_server_example/proto/echo_grpc.pb.go delete mode 100644 t/grpc_server_example/proto/google/protobuf/struct.proto diff --git a/t/grpc_server_example/echo.pb b/t/grpc_server_example/echo.pb new file mode 100644 index 0000000000000000000000000000000000000000..3f82f2554acc2e57265cd567c055c6f0f2a03a25 GIT binary patch literal 997 zcmb7?U279T6oxaK&FY@5wN61x6qW5wsWO2TMA{;_5Gxo;Ty2Hklx#9xL#8`%vs=lP zf1}sE^wOW?ujoy|Garezp@LUCbLKhk`PdhJHQ-SuWhQuQT*y+U)p2W57FEAw`cChf zS98PH@Qi@>Q4@30g(}9K#VrP(7LL38d_KBrwrAgnJt1nT6`AaRr&Eft~=QQqJ0N5xK2Osh9zFU77&MDwb&;`Jr=z#R?qLAVY6oS+Q6Q?=FE zAH8YmmfCy>{(Oma8DMX}(>XeL)7fvY)Z*{3+LtHSXNdzdOjp^hOAcGsGno{JxU zq_woG&dqzmUHd)66fw|4R_jzyLd-I)L_`e9ZhEX)+YjD!H5AwCfx0CD_%V`&3cQFcDMFgw%co zJ7cC5$iHS&Ohh)zo?OyB=2$qq@X`3uXMC|&Fxk~jMO?t$2GZf6loKAW!qT9oW`{D3HisNKDX0cJ^jvm#x_nwe z>kqX~i=JFMe-T*Tm&bYRcbsPO(Xcfr1_>L*DDXUrUT^3YSVI;Ln5argt=6e~QKj2b z9?uYx9jmgcQd{;cs|)rF;hX}PipG3Ot56!TIF@DWCzo#NS6Aiy0Cty8;z z?-8HkuBTo!?t0D!lr<`AS_ez8c)}Ayo1OCyfwkvo4#u1TiosY6->?Cg?A4?ZBTZmC$PT z google.protobuf.Struct + 2, // 1: echo.StructReply.data:type_name -> google.protobuf.Struct + 0, // 2: echo.Echo.EchoStruct:input_type -> echo.StructRequest + 1, // 3: echo.Echo.EchoStruct:output_type -> echo.StructReply + 3, // [3:4] is the sub-list for method output_type + 2, // [2:3] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_proto_echo_proto_init() } +func file_proto_echo_proto_init() { + if File_proto_echo_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_proto_echo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StructRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_echo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StructReply); 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_proto_echo_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_proto_echo_proto_goTypes, + DependencyIndexes: file_proto_echo_proto_depIdxs, + MessageInfos: file_proto_echo_proto_msgTypes, + }.Build() + File_proto_echo_proto = out.File + file_proto_echo_proto_rawDesc = nil + file_proto_echo_proto_goTypes = nil + file_proto_echo_proto_depIdxs = nil +} diff --git a/t/grpc_server_example/proto/echo.proto b/t/grpc_server_example/proto/echo.proto new file mode 100644 index 000000000000..144c261b13f7 --- /dev/null +++ b/t/grpc_server_example/proto/echo.proto @@ -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. +// + +syntax = "proto3"; + +package echo; +option go_package = "./proto"; + +import "google/protobuf/struct.proto"; + +service Echo { + rpc EchoStruct (StructRequest) returns (StructReply) {} +} + +message StructRequest { + google.protobuf.Struct data = 1; +} + +message StructReply { + google.protobuf.Struct data = 1; +} diff --git a/t/grpc_server_example/proto/echo_grpc.pb.go b/t/grpc_server_example/proto/echo_grpc.pb.go new file mode 100644 index 000000000000..a546e7f3ae96 --- /dev/null +++ b/t/grpc_server_example/proto/echo_grpc.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.6.1 +// source: proto/echo.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// EchoClient is the client API for Echo 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 EchoClient interface { + EchoStruct(ctx context.Context, in *StructRequest, opts ...grpc.CallOption) (*StructReply, error) +} + +type echoClient struct { + cc grpc.ClientConnInterface +} + +func NewEchoClient(cc grpc.ClientConnInterface) EchoClient { + return &echoClient{cc} +} + +func (c *echoClient) EchoStruct(ctx context.Context, in *StructRequest, opts ...grpc.CallOption) (*StructReply, error) { + out := new(StructReply) + err := c.cc.Invoke(ctx, "/echo.Echo/EchoStruct", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// EchoServer is the server API for Echo service. +// All implementations must embed UnimplementedEchoServer +// for forward compatibility +type EchoServer interface { + EchoStruct(context.Context, *StructRequest) (*StructReply, error) + mustEmbedUnimplementedEchoServer() +} + +// UnimplementedEchoServer must be embedded to have forward compatible implementations. +type UnimplementedEchoServer struct { +} + +func (UnimplementedEchoServer) EchoStruct(context.Context, *StructRequest) (*StructReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method EchoStruct not implemented") +} +func (UnimplementedEchoServer) mustEmbedUnimplementedEchoServer() {} + +// UnsafeEchoServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to EchoServer will +// result in compilation errors. +type UnsafeEchoServer interface { + mustEmbedUnimplementedEchoServer() +} + +func RegisterEchoServer(s grpc.ServiceRegistrar, srv EchoServer) { + s.RegisterService(&Echo_ServiceDesc, srv) +} + +func _Echo_EchoStruct_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StructRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(EchoServer).EchoStruct(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/echo.Echo/EchoStruct", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(EchoServer).EchoStruct(ctx, req.(*StructRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Echo_ServiceDesc is the grpc.ServiceDesc for Echo service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Echo_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "echo.Echo", + HandlerType: (*EchoServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "EchoStruct", + Handler: _Echo_EchoStruct_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "proto/echo.proto", +} diff --git a/t/grpc_server_example/proto/google/protobuf/struct.proto b/t/grpc_server_example/proto/google/protobuf/struct.proto deleted file mode 100644 index c4ea6453eaec..000000000000 --- a/t/grpc_server_example/proto/google/protobuf/struct.proto +++ /dev/null @@ -1,95 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2008 Google Inc. All rights reserved. -// https://developers.google.com/protocol-buffers/ -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -syntax = "proto3"; - -package google.protobuf; - -option cc_enable_arenas = true; -option go_package = "google.golang.org/protobuf/types/known/structpb"; -option java_package = "com.google.protobuf"; -option java_outer_classname = "StructProto"; -option java_multiple_files = true; -option objc_class_prefix = "GPB"; -option csharp_namespace = "Google.Protobuf.WellKnownTypes"; - -// `Struct` represents a structured data value, consisting of fields -// which map to dynamically typed values. In some languages, `Struct` -// might be supported by a native representation. For example, in -// scripting languages like JS a struct is represented as an -// object. The details of that representation are described together -// with the proto support for the language. -// -// The JSON representation for `Struct` is JSON object. -message Struct { - // Unordered map of dynamically typed values. - map fields = 1; -} - -// `Value` represents a dynamically typed value which can be either -// null, a number, a string, a boolean, a recursive struct value, or a -// list of values. A producer of value is expected to set one of these -// variants. Absence of any variant indicates an error. -// -// The JSON representation for `Value` is JSON value. -message Value { - // The kind of value. - oneof kind { - // Represents a null value. - NullValue null_value = 1; - // Represents a double value. - double number_value = 2; - // Represents a string value. - string string_value = 3; - // Represents a boolean value. - bool bool_value = 4; - // Represents a structured value. - Struct struct_value = 5; - // Represents a repeated `Value`. - ListValue list_value = 6; - } -} - -// `NullValue` is a singleton enumeration to represent the null value for the -// `Value` type union. -// -// The JSON representation for `NullValue` is JSON `null`. -enum NullValue { - // Null value. - NULL_VALUE = 0; -} - -// `ListValue` is a wrapper around a repeated field of values. -// -// The JSON representation for `ListValue` is JSON array. -message ListValue { - // Repeated field of dynamically typed values. - repeated Value values = 1; -} diff --git a/t/grpc_server_example/proto/helloworld.pb.go b/t/grpc_server_example/proto/helloworld.pb.go index 26314612f36e..22af6b82d42e 100644 --- a/t/grpc_server_example/proto/helloworld.pb.go +++ b/t/grpc_server_example/proto/helloworld.pb.go @@ -17,14 +17,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.6.1 +// protoc-gen-go v1.27.1 +// protoc v3.12.4 // source: proto/helloworld.proto package proto import ( - _struct "github.com/golang/protobuf/ptypes/struct" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -575,209 +574,101 @@ func (x *ErrorDetail) GetType() string { return "" } -type StructRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Data *_struct.Struct `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` -} - -func (x *StructRequest) Reset() { - *x = StructRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_helloworld_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *StructRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StructRequest) ProtoMessage() {} - -func (x *StructRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_helloworld_proto_msgTypes[8] - 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 StructRequest.ProtoReflect.Descriptor instead. -func (*StructRequest) Descriptor() ([]byte, []int) { - return file_proto_helloworld_proto_rawDescGZIP(), []int{8} -} - -func (x *StructRequest) GetData() *_struct.Struct { - if x != nil { - return x.Data - } - return nil -} - -type StructReply struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Data *_struct.Struct `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` -} - -func (x *StructReply) Reset() { - *x = StructReply{} - if protoimpl.UnsafeEnabled { - mi := &file_proto_helloworld_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *StructReply) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StructReply) ProtoMessage() {} - -func (x *StructReply) ProtoReflect() protoreflect.Message { - mi := &file_proto_helloworld_proto_msgTypes[9] - 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 StructReply.ProtoReflect.Descriptor instead. -func (*StructReply) Descriptor() ([]byte, []int) { - return file_proto_helloworld_proto_rawDescGZIP(), []int{9} -} - -func (x *StructReply) GetData() *_struct.Struct { - if x != nil { - return x.Data - } - return nil -} - var File_proto_helloworld_proto protoreflect.FileDescriptor var file_proto_helloworld_proto_rawDesc = []byte{ 0x0a, 0x16, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 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, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x2e, 0x0a, 0x06, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61, - 0x67, 0x65, 0x22, 0x90, 0x01, 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, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2a, 0x0a, - 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, - 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x47, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x52, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x06, 0x70, 0x65, 0x72, - 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x6c, 0x6c, - 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x70, - 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x22, 0x68, 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, 0x12, 0x14, 0x0a, - 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x69, 0x74, - 0x65, 0x6d, 0x73, 0x12, 0x2a, 0x0a, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, - 0x2e, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x22, - 0x29, 0x0a, 0x0b, 0x50, 0x6c, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0c, - 0x0a, 0x01, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x01, 0x61, 0x12, 0x0c, 0x0a, 0x01, - 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x01, 0x62, 0x22, 0x23, 0x0a, 0x09, 0x50, 0x6c, - 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, - 0x9c, 0x01, 0x0a, 0x14, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 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, 0x12, 0x14, 0x0a, 0x05, - 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x69, 0x74, 0x65, - 0x6d, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, - 0x2e, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x07, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x50, - 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x07, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x73, 0x22, 0x72, - 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 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, 0x12, 0x14, + 0x6f, 0x72, 0x6c, 0x64, 0x22, 0x2e, 0x0a, 0x06, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x03, 0x61, 0x67, 0x65, 0x22, 0x90, 0x01, 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, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, + 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, + 0x2a, 0x0a, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x12, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x47, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x52, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x06, 0x70, + 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, + 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, + 0x06, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x22, 0x68, 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, 0x12, + 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, + 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2a, 0x0a, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, + 0x6c, 0x64, 0x2e, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, + 0x72, 0x22, 0x29, 0x0a, 0x0b, 0x50, 0x6c, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0c, 0x0a, 0x01, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x01, 0x61, 0x12, 0x0c, + 0x0a, 0x01, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x01, 0x62, 0x22, 0x23, 0x0a, 0x09, + 0x50, 0x6c, 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x22, 0x9c, 0x01, 0x0a, 0x14, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 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, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x07, 0x67, 0x65, 0x6e, 0x64, 0x65, - 0x72, 0x73, 0x22, 0x4f, 0x0a, 0x0b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, - 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x22, 0x3c, 0x0a, 0x0d, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x22, 0x3a, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x2a, 0x40, 0x0a, - 0x06, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x0e, 0x47, 0x45, 0x4e, 0x44, 0x45, - 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x47, - 0x45, 0x4e, 0x44, 0x45, 0x52, 0x5f, 0x4d, 0x41, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, - 0x47, 0x45, 0x4e, 0x44, 0x45, 0x52, 0x5f, 0x46, 0x45, 0x4d, 0x41, 0x4c, 0x45, 0x10, 0x02, 0x32, - 0x9e, 0x05, 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, 0x12, 0x40, 0x0a, 0x0a, 0x47, - 0x65, 0x74, 0x45, 0x72, 0x72, 0x52, 0x65, 0x73, 0x70, 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, 0x12, 0x38, 0x0a, - 0x04, 0x50, 0x6c, 0x75, 0x73, 0x12, 0x17, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x2e, 0x50, 0x6c, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, - 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x50, 0x6c, 0x75, 0x73, - 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x12, 0x53, 0x61, 0x79, 0x48, 0x65, - 0x6c, 0x6c, 0x6f, 0x41, 0x66, 0x74, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 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, 0x12, 0x56, 0x0a, 0x10, 0x53, 0x61, 0x79, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, - 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, - 0x6c, 0x64, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x48, 0x65, 0x6c, 0x6c, 0x6f, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, - 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x48, 0x65, 0x6c, - 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0a, 0x45, 0x63, 0x68, - 0x6f, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x12, 0x19, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, - 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, - 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x4c, 0x0a, - 0x14, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x18, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, + 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, + 0x2e, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x52, 0x07, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x73, + 0x22, 0x72, 0x0a, 0x12, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 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, + 0x12, 0x14, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, + 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x52, 0x07, 0x67, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x73, 0x22, 0x4f, 0x0a, 0x0b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x44, 0x65, 0x74, + 0x61, 0x69, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x40, 0x0a, 0x06, 0x47, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, + 0x12, 0x0a, 0x0e, 0x47, 0x45, 0x4e, 0x44, 0x45, 0x52, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x47, 0x45, 0x4e, 0x44, 0x45, 0x52, 0x5f, 0x4d, 0x41, + 0x4c, 0x45, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x44, 0x45, 0x52, 0x5f, 0x46, + 0x45, 0x4d, 0x41, 0x4c, 0x45, 0x10, 0x02, 0x32, 0xda, 0x04, 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, 0x12, 0x40, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x45, 0x72, 0x72, 0x52, 0x65, 0x73, + 0x70, 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, 0x12, 0x38, 0x0a, 0x04, 0x50, 0x6c, 0x75, 0x73, 0x12, 0x17, 0x2e, + 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x50, 0x6c, 0x75, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, + 0x72, 0x6c, 0x64, 0x2e, 0x50, 0x6c, 0x75, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, + 0x48, 0x0a, 0x12, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x41, 0x66, 0x74, 0x65, 0x72, + 0x44, 0x65, 0x6c, 0x61, 0x79, 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, 0x12, 0x4c, 0x0a, 0x14, 0x53, - 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 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, 0x12, 0x55, 0x0a, 0x1b, 0x53, 0x61, 0x79, - 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x42, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x61, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 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, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x56, 0x0a, 0x10, 0x53, 0x61, 0x79, + 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x12, 0x20, 0x2e, + 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, + 0x70, 0x6c, 0x65, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1e, 0x2e, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x2e, 0x4d, 0x75, 0x6c, + 0x74, 0x69, 0x70, 0x6c, 0x65, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x12, 0x4c, 0x0a, 0x14, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x53, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 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, 0x12, + 0x4c, 0x0a, 0x14, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 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, 0x12, 0x55, 0x0a, + 0x1b, 0x53, 0x61, 0x79, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x42, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 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, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -793,7 +684,7 @@ func file_proto_helloworld_proto_rawDescGZIP() []byte { } var file_proto_helloworld_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_proto_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_proto_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_proto_helloworld_proto_goTypes = []interface{}{ (Gender)(0), // 0: helloworld.Gender (*Person)(nil), // 1: helloworld.Person @@ -804,9 +695,6 @@ var file_proto_helloworld_proto_goTypes = []interface{}{ (*MultipleHelloRequest)(nil), // 6: helloworld.MultipleHelloRequest (*MultipleHelloReply)(nil), // 7: helloworld.MultipleHelloReply (*ErrorDetail)(nil), // 8: helloworld.ErrorDetail - (*StructRequest)(nil), // 9: helloworld.StructRequest - (*StructReply)(nil), // 10: helloworld.StructReply - (*_struct.Struct)(nil), // 11: google.protobuf.Struct } var file_proto_helloworld_proto_depIdxs = []int32{ 0, // 0: helloworld.HelloRequest.gender:type_name -> helloworld.Gender @@ -815,31 +703,27 @@ var file_proto_helloworld_proto_depIdxs = []int32{ 0, // 3: helloworld.MultipleHelloRequest.genders:type_name -> helloworld.Gender 1, // 4: helloworld.MultipleHelloRequest.persons:type_name -> helloworld.Person 0, // 5: helloworld.MultipleHelloReply.genders:type_name -> helloworld.Gender - 11, // 6: helloworld.StructRequest.data:type_name -> google.protobuf.Struct - 11, // 7: helloworld.StructReply.data:type_name -> google.protobuf.Struct - 2, // 8: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest - 2, // 9: helloworld.Greeter.GetErrResp:input_type -> helloworld.HelloRequest - 4, // 10: helloworld.Greeter.Plus:input_type -> helloworld.PlusRequest - 2, // 11: helloworld.Greeter.SayHelloAfterDelay:input_type -> helloworld.HelloRequest - 6, // 12: helloworld.Greeter.SayMultipleHello:input_type -> helloworld.MultipleHelloRequest - 9, // 13: helloworld.Greeter.EchoStruct:input_type -> helloworld.StructRequest - 2, // 14: helloworld.Greeter.SayHelloServerStream:input_type -> helloworld.HelloRequest - 2, // 15: helloworld.Greeter.SayHelloClientStream:input_type -> helloworld.HelloRequest - 2, // 16: helloworld.Greeter.SayHelloBidirectionalStream:input_type -> helloworld.HelloRequest - 3, // 17: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply - 3, // 18: helloworld.Greeter.GetErrResp:output_type -> helloworld.HelloReply - 5, // 19: helloworld.Greeter.Plus:output_type -> helloworld.PlusReply - 3, // 20: helloworld.Greeter.SayHelloAfterDelay:output_type -> helloworld.HelloReply - 7, // 21: helloworld.Greeter.SayMultipleHello:output_type -> helloworld.MultipleHelloReply - 10, // 22: helloworld.Greeter.EchoStruct:output_type -> helloworld.StructReply - 3, // 23: helloworld.Greeter.SayHelloServerStream:output_type -> helloworld.HelloReply - 3, // 24: helloworld.Greeter.SayHelloClientStream:output_type -> helloworld.HelloReply - 3, // 25: helloworld.Greeter.SayHelloBidirectionalStream:output_type -> helloworld.HelloReply - 17, // [17:26] is the sub-list for method output_type - 8, // [8:17] is the sub-list for method input_type - 8, // [8:8] is the sub-list for extension type_name - 8, // [8:8] is the sub-list for extension extendee - 0, // [0:8] is the sub-list for field type_name + 2, // 6: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest + 2, // 7: helloworld.Greeter.GetErrResp:input_type -> helloworld.HelloRequest + 4, // 8: helloworld.Greeter.Plus:input_type -> helloworld.PlusRequest + 2, // 9: helloworld.Greeter.SayHelloAfterDelay:input_type -> helloworld.HelloRequest + 6, // 10: helloworld.Greeter.SayMultipleHello:input_type -> helloworld.MultipleHelloRequest + 2, // 11: helloworld.Greeter.SayHelloServerStream:input_type -> helloworld.HelloRequest + 2, // 12: helloworld.Greeter.SayHelloClientStream:input_type -> helloworld.HelloRequest + 2, // 13: helloworld.Greeter.SayHelloBidirectionalStream:input_type -> helloworld.HelloRequest + 3, // 14: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply + 3, // 15: helloworld.Greeter.GetErrResp:output_type -> helloworld.HelloReply + 5, // 16: helloworld.Greeter.Plus:output_type -> helloworld.PlusReply + 3, // 17: helloworld.Greeter.SayHelloAfterDelay:output_type -> helloworld.HelloReply + 7, // 18: helloworld.Greeter.SayMultipleHello:output_type -> helloworld.MultipleHelloReply + 3, // 19: helloworld.Greeter.SayHelloServerStream:output_type -> helloworld.HelloReply + 3, // 20: helloworld.Greeter.SayHelloClientStream:output_type -> helloworld.HelloReply + 3, // 21: helloworld.Greeter.SayHelloBidirectionalStream:output_type -> helloworld.HelloReply + 14, // [14:22] is the sub-list for method output_type + 6, // [6:14] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_proto_helloworld_proto_init() } @@ -944,30 +828,6 @@ func file_proto_helloworld_proto_init() { return nil } } - file_proto_helloworld_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StructRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_proto_helloworld_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*StructReply); 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{ @@ -975,7 +835,7 @@ func file_proto_helloworld_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_helloworld_proto_rawDesc, NumEnums: 1, - NumMessages: 10, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/t/grpc_server_example/proto/helloworld.proto b/t/grpc_server_example/proto/helloworld.proto index caf0d7250c1e..28f711a9037f 100644 --- a/t/grpc_server_example/proto/helloworld.proto +++ b/t/grpc_server_example/proto/helloworld.proto @@ -20,8 +20,6 @@ syntax = "proto3"; package helloworld; option go_package = "./proto"; -import "google/protobuf/struct.proto"; - service Greeter { // Unary RPC. rpc SayHello (HelloRequest) returns (HelloReply) {} @@ -29,7 +27,6 @@ service Greeter { rpc Plus (PlusRequest) returns (PlusReply) {} rpc SayHelloAfterDelay (HelloRequest) returns (HelloReply) {} rpc SayMultipleHello(MultipleHelloRequest) returns (MultipleHelloReply) {} - rpc EchoStruct (StructRequest) returns (StructReply) {} // Server side streaming. rpc SayHelloServerStream (HelloRequest) returns (stream HelloReply) {} @@ -93,11 +90,3 @@ message ErrorDetail { string message = 2; string type = 3; } - -message StructRequest { - google.protobuf.Struct data = 1; -} - -message StructReply { - google.protobuf.Struct data = 1; -} diff --git a/t/grpc_server_example/proto/helloworld_grpc.pb.go b/t/grpc_server_example/proto/helloworld_grpc.pb.go index 5fd396f23889..93147244c1f1 100644 --- a/t/grpc_server_example/proto/helloworld_grpc.pb.go +++ b/t/grpc_server_example/proto/helloworld_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v3.6.1 +// - protoc v3.12.4 // source: proto/helloworld.proto package proto @@ -28,7 +28,6 @@ type GreeterClient interface { Plus(ctx context.Context, in *PlusRequest, opts ...grpc.CallOption) (*PlusReply, error) SayHelloAfterDelay(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) SayMultipleHello(ctx context.Context, in *MultipleHelloRequest, opts ...grpc.CallOption) (*MultipleHelloReply, error) - EchoStruct(ctx context.Context, in *StructRequest, opts ...grpc.CallOption) (*StructReply, error) // Server side streaming. SayHelloServerStream(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (Greeter_SayHelloServerStreamClient, error) // Client side streaming. @@ -90,15 +89,6 @@ func (c *greeterClient) SayMultipleHello(ctx context.Context, in *MultipleHelloR return out, nil } -func (c *greeterClient) EchoStruct(ctx context.Context, in *StructRequest, opts ...grpc.CallOption) (*StructReply, error) { - out := new(StructReply) - err := c.cc.Invoke(ctx, "/helloworld.Greeter/EchoStruct", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *greeterClient) SayHelloServerStream(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (Greeter_SayHelloServerStreamClient, error) { stream, err := c.cc.NewStream(ctx, &Greeter_ServiceDesc.Streams[0], "/helloworld.Greeter/SayHelloServerStream", opts...) if err != nil { @@ -206,7 +196,6 @@ type GreeterServer interface { Plus(context.Context, *PlusRequest) (*PlusReply, error) SayHelloAfterDelay(context.Context, *HelloRequest) (*HelloReply, error) SayMultipleHello(context.Context, *MultipleHelloRequest) (*MultipleHelloReply, error) - EchoStruct(context.Context, *StructRequest) (*StructReply, error) // Server side streaming. SayHelloServerStream(*HelloRequest, Greeter_SayHelloServerStreamServer) error // Client side streaming. @@ -235,9 +224,6 @@ func (UnimplementedGreeterServer) SayHelloAfterDelay(context.Context, *HelloRequ func (UnimplementedGreeterServer) SayMultipleHello(context.Context, *MultipleHelloRequest) (*MultipleHelloReply, error) { return nil, status.Errorf(codes.Unimplemented, "method SayMultipleHello not implemented") } -func (UnimplementedGreeterServer) EchoStruct(context.Context, *StructRequest) (*StructReply, error) { - return nil, status.Errorf(codes.Unimplemented, "method EchoStruct not implemented") -} func (UnimplementedGreeterServer) SayHelloServerStream(*HelloRequest, Greeter_SayHelloServerStreamServer) error { return status.Errorf(codes.Unimplemented, "method SayHelloServerStream not implemented") } @@ -350,24 +336,6 @@ func _Greeter_SayMultipleHello_Handler(srv interface{}, ctx context.Context, dec return interceptor(ctx, in, info, handler) } -func _Greeter_EchoStruct_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(StructRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(GreeterServer).EchoStruct(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/helloworld.Greeter/EchoStruct", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GreeterServer).EchoStruct(ctx, req.(*StructRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Greeter_SayHelloServerStream_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(HelloRequest) if err := stream.RecvMsg(m); err != nil { @@ -468,10 +436,6 @@ var Greeter_ServiceDesc = grpc.ServiceDesc{ MethodName: "SayMultipleHello", Handler: _Greeter_SayMultipleHello_Handler, }, - { - MethodName: "EchoStruct", - Handler: _Greeter_EchoStruct_Handler, - }, }, Streams: []grpc.StreamDesc{ { diff --git a/t/plugin/grpc-transcode-struct.t b/t/plugin/grpc-transcode-struct.t index 0263918979ca..99964c144a52 100644 --- a/t/plugin/grpc-transcode-struct.t +++ b/t/plugin/grpc-transcode-struct.t @@ -39,7 +39,7 @@ __DATA__ local t = require("lib.test_admin") local json = require("toolkit.json") - local content = t.read_file("t/grpc_server_example/helloworld.pb") + local content = t.read_file("t/grpc_server_example/echo.pb") local data = {content = ngx.encode_base64(content)} local code, body = t.test('/apisix/admin/protos/1', ngx.HTTP_PUT, @@ -59,7 +59,7 @@ __DATA__ "plugins": { "grpc-transcode": { "proto_id": "1", - "service": "helloworld.Greeter", + "service": "echo.Echo", "method": "EchoStruct" } }, @@ -104,8 +104,17 @@ location /t { else local req = core.json.decode(body) local rsp = core.json.decode(res.body) - if not core.table.deep_eq(req, rsp) then - ngx.log(ngx.ERR, "failed: req=", body, ", rsp=", res.body) + for k, v in pairs(req.data.fields) do + if rsp.data.fields[k] == nil then + ngx.log(ngx.ERR, "rsp missing field=", k, ", rsp: ", res.body) + else + for k1, v1 in pairs(v) do + if v1 ~= rsp.data.fields[k][k1] then + ngx.log(ngx.ERR, "rsp mismatch: k=", k1, + ", req=", v1, ", rsp=", rsp.data.fields[k][k1]) + end + end + end end end } diff --git a/t/plugin/grpc-web/client.js b/t/plugin/grpc-web/client.js index 3f10a80bc100..5d56a8109122 100644 --- a/t/plugin/grpc-web/client.js +++ b/t/plugin/grpc-web/client.js @@ -36,8 +36,8 @@ const types = [TYPE_UNARY, TYPE_STREAM]; class gRPCWebClient { constructor() { this.clients = {} - this.clients[MODE_BIN] = new RouteServiceBinProtocolClient("http://127.0.0.1:1984/grpc/web"); - this.clients[MODE_TEXT] = new RouteServiceTextProtocolClient("http://127.0.0.1:1984/grpc/web"); + this.clients[MODE_BIN] = new RouteServiceBinProtocolClient("http://127.0.0.1:9080/grpc/web"); + this.clients[MODE_TEXT] = new RouteServiceTextProtocolClient("http://127.0.0.1:9080/grpc/web"); }; unary(mode) { diff --git a/t/plugin/grpc-web/server.go b/t/plugin/grpc-web/server.go index 3eaaa750289f..4bd8240c6d3c 100644 --- a/t/plugin/grpc-web/server.go +++ b/t/plugin/grpc-web/server.go @@ -18,6 +18,7 @@ package main import ( + "time" "context" "encoding/json" "flag" @@ -61,6 +62,7 @@ func (rss *routeServiceServer) GetRoutes(req *pb.Query, srv pb.RouteService_GetR if err := srv.Send(savedRoute); err != nil { return err } + time.Sleep(3*time.Second) } return nil From 2a45eda76f2678e3905d1291961819e0c38839b2 Mon Sep 17 00:00:00 2001 From: kingluo Date: Sun, 29 Jan 2023 14:03:43 +0800 Subject: [PATCH 08/10] fix --- t/plugin/grpc-web/client.js | 4 ++-- t/plugin/grpc-web/server.go | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/t/plugin/grpc-web/client.js b/t/plugin/grpc-web/client.js index 5d56a8109122..3f10a80bc100 100644 --- a/t/plugin/grpc-web/client.js +++ b/t/plugin/grpc-web/client.js @@ -36,8 +36,8 @@ const types = [TYPE_UNARY, TYPE_STREAM]; class gRPCWebClient { constructor() { this.clients = {} - this.clients[MODE_BIN] = new RouteServiceBinProtocolClient("http://127.0.0.1:9080/grpc/web"); - this.clients[MODE_TEXT] = new RouteServiceTextProtocolClient("http://127.0.0.1:9080/grpc/web"); + this.clients[MODE_BIN] = new RouteServiceBinProtocolClient("http://127.0.0.1:1984/grpc/web"); + this.clients[MODE_TEXT] = new RouteServiceTextProtocolClient("http://127.0.0.1:1984/grpc/web"); }; unary(mode) { diff --git a/t/plugin/grpc-web/server.go b/t/plugin/grpc-web/server.go index 4bd8240c6d3c..3eaaa750289f 100644 --- a/t/plugin/grpc-web/server.go +++ b/t/plugin/grpc-web/server.go @@ -18,7 +18,6 @@ package main import ( - "time" "context" "encoding/json" "flag" @@ -62,7 +61,6 @@ func (rss *routeServiceServer) GetRoutes(req *pb.Query, srv pb.RouteService_GetR if err := srv.Send(savedRoute); err != nil { return err } - time.Sleep(3*time.Second) } return nil From a032090bd970975a1f53aa157a9afd079ccb30fe Mon Sep 17 00:00:00 2001 From: kingluo Date: Sun, 29 Jan 2023 14:12:12 +0800 Subject: [PATCH 09/10] fix --- .licenserc.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.licenserc.yaml b/.licenserc.yaml index 72c4abcf1836..ea5863015302 100644 --- a/.licenserc.yaml +++ b/.licenserc.yaml @@ -47,6 +47,5 @@ header: - 't/plugin/authz-casbin' - 't/coredns' - 'autodocs/' - - 't/grpc_server_example/proto/google' comment: on-failure From 58f3b4c5aa9825a92e6d86911442dcd83a763b3c Mon Sep 17 00:00:00 2001 From: kingluo Date: Sun, 29 Jan 2023 15:44:54 +0800 Subject: [PATCH 10/10] fix PR --- apisix/plugins/grpc-transcode/util.lua | 15 +-- t/grpc_server_example/go.mod | 2 +- t/plugin/grpc-transcode-struct.t | 121 ------------------------- t/plugin/grpc-transcode3.t | 90 ++++++++++++++++++ 4 files changed, 99 insertions(+), 129 deletions(-) delete mode 100644 t/plugin/grpc-transcode-struct.t diff --git a/apisix/plugins/grpc-transcode/util.lua b/apisix/plugins/grpc-transcode/util.lua index e581a610b78e..4a27c1d3b9df 100644 --- a/apisix/plugins/grpc-transcode/util.lua +++ b/apisix/plugins/grpc-transcode/util.lua @@ -166,24 +166,25 @@ function _M.map_message(field, default_values, request_table, real_key) sub = sub_array else if ty == "map" then - local tbl for k, v in pairs(request_table[name]) do - tbl, err = _M.map_message(field_type, + local tbl, err = _M.map_message(field_type, default_values and default_values[name], request_table[name], k) + if err then + return nil, err + end if not sub then sub = {} end - sub[k]= tbl[k] + sub[k] = tbl[k] end else sub, err = _M.map_message(field_type, default_values and default_values[name], request_table[name]) - end - - if err then - return nil, err + if err then + return nil, err + end end end diff --git a/t/grpc_server_example/go.mod b/t/grpc_server_example/go.mod index 480e4f0c6975..9b95e63fdeaa 100644 --- a/t/grpc_server_example/go.mod +++ b/t/grpc_server_example/go.mod @@ -3,7 +3,7 @@ module github.com/api7/grpc_server_example go 1.11 require ( - github.com/golang/protobuf v1.5.0 // indirect + github.com/golang/protobuf v1.5.0 golang.org/x/net v0.2.0 google.golang.org/grpc v1.32.0 google.golang.org/protobuf v1.27.1 diff --git a/t/plugin/grpc-transcode-struct.t b/t/plugin/grpc-transcode-struct.t deleted file mode 100644 index 99964c144a52..000000000000 --- a/t/plugin/grpc-transcode-struct.t +++ /dev/null @@ -1,121 +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. -# -use t::APISIX 'no_plan'; - -no_long_string(); -no_shuffle(); -no_root_location(); - -add_block_preprocessor(sub { - my ($block) = @_; - - if (!$block->request) { - $block->set_value("request", "GET /t"); - } -}); - -run_tests; - -__DATA__ - -=== TEST 1: set binary rule ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin") - local json = require("toolkit.json") - - local content = t.read_file("t/grpc_server_example/echo.pb") - local data = {content = ngx.encode_base64(content)} - local code, body = t.test('/apisix/admin/protos/1', - ngx.HTTP_PUT, - json.encode(data) - ) - - if code >= 300 then - ngx.status = code - ngx.say(body) - return - end - - local code, body = t.test('/apisix/admin/routes/1', - ngx.HTTP_PUT, - [[{ - "uri": "/grpctest", - "plugins": { - "grpc-transcode": { - "proto_id": "1", - "service": "echo.Echo", - "method": "EchoStruct" - } - }, - "upstream": { - "scheme": "grpc", - "type": "roundrobin", - "nodes": { - "127.0.0.1:50051": 1 - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 2: hit route ---- config -location /t { - content_by_lua_block { - local core = require "apisix.core" - local http = require "resty.http" - local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/grpctest" - local body = [[{"data":{"fields":{"foo":{"string_value":"xxx"},"bar":{"number_value":666}}}}]] - local opt = {method = "POST", body = body, headers = {["Content-Type"] = "application/json"}, keepalive = false} - local httpc = http.new() - local res, err = httpc:request_uri(uri, opt) - if not res then - ngx.log(ngx.ERR, err) - return ngx.exit(500) - end - if res.status > 300 then - return ngx.exit(res.status) - else - local req = core.json.decode(body) - local rsp = core.json.decode(res.body) - for k, v in pairs(req.data.fields) do - if rsp.data.fields[k] == nil then - ngx.log(ngx.ERR, "rsp missing field=", k, ", rsp: ", res.body) - else - for k1, v1 in pairs(v) do - if v1 ~= rsp.data.fields[k][k1] then - ngx.log(ngx.ERR, "rsp mismatch: k=", k1, - ", req=", v1, ", rsp=", rsp.data.fields[k][k1]) - end - end - end - end - end - } -} diff --git a/t/plugin/grpc-transcode3.t b/t/plugin/grpc-transcode3.t index 6ce4cc943323..0cdbe6e16bdd 100644 --- a/t/plugin/grpc-transcode3.t +++ b/t/plugin/grpc-transcode3.t @@ -435,3 +435,93 @@ failed to call pb.decode to decode details in grpc-status-details-bin --- error_log transform response error: failed to call pb.decode to decode details in grpc-status-details-bin, err: --- error_code: 503 + + + +=== TEST 11: set binary rule for EchoStruct +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin") + local json = require("toolkit.json") + + local content = t.read_file("t/grpc_server_example/echo.pb") + local data = {content = ngx.encode_base64(content)} + local code, body = t.test('/apisix/admin/protos/1', + ngx.HTTP_PUT, + json.encode(data) + ) + + if code >= 300 then + ngx.status = code + ngx.say(body) + return + end + + local code, body = t.test('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/grpctest", + "plugins": { + "grpc-transcode": { + "proto_id": "1", + "service": "echo.Echo", + "method": "EchoStruct" + } + }, + "upstream": { + "scheme": "grpc", + "type": "roundrobin", + "nodes": { + "127.0.0.1:50051": 1 + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 12: hit route to test EchoStruct +--- config +location /t { + content_by_lua_block { + local core = require "apisix.core" + local http = require "resty.http" + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/grpctest" + local body = [[{"data":{"fields":{"foo":{"string_value":"xxx"},"bar":{"number_value":666}}}}]] + local opt = {method = "POST", body = body, headers = {["Content-Type"] = "application/json"}, keepalive = false} + local httpc = http.new() + local res, err = httpc:request_uri(uri, opt) + if not res then + ngx.log(ngx.ERR, err) + return ngx.exit(500) + end + if res.status > 300 then + return ngx.exit(res.status) + else + local req = core.json.decode(body) + local rsp = core.json.decode(res.body) + for k, v in pairs(req.data.fields) do + if rsp.data.fields[k] == nil then + ngx.log(ngx.ERR, "rsp missing field=", k, ", rsp: ", res.body) + else + for k1, v1 in pairs(v) do + if v1 ~= rsp.data.fields[k][k1] then + ngx.log(ngx.ERR, "rsp mismatch: k=", k1, + ", req=", v1, ", rsp=", rsp.data.fields[k][k1]) + end + end + end + end + end + } +}