You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by sp...@apache.org on 2022/06/20 06:13:59 UTC

[apisix] 06/07: fix: grpc-transcode request support object array (#7231)

This is an automated email from the ASF dual-hosted git repository.

spacewander pushed a commit to branch release/2.13
in repository https://gitbox.apache.org/repos/asf/apisix.git

commit 314d13cff555fe0b8110ad0053f6f44bba99d129
Author: 余茂林 <10...@qq.com>
AuthorDate: Fri Jun 17 11:07:33 2022 +0800

    fix: grpc-transcode request support object array  (#7231)
    
    Co-authored-by: jon.yu <jo...@ambergroup.io>
---
 apisix/plugins/grpc-transcode/util.lua            |  16 ++
 t/grpc_server_example/main.go                     |  25 ++
 t/grpc_server_example/proto/helloworld.pb.go      | 321 +++++++++++++++++-----
 t/grpc_server_example/proto/helloworld.proto      |  15 +
 t/grpc_server_example/proto/helloworld_grpc.pb.go |  40 +++
 t/grpc_server_example/proto/import.pb.go          |  21 +-
 t/grpc_server_example/proto/src.pb.go             |  21 +-
 t/grpc_server_example/proto/src_grpc.pb.go        |   4 +
 t/plugin/grpc-transcode3.t                        | 124 +++++++++
 9 files changed, 518 insertions(+), 69 deletions(-)

diff --git a/apisix/plugins/grpc-transcode/util.lua b/apisix/plugins/grpc-transcode/util.lua
index 2a7bfb648..68d300203 100644
--- a/apisix/plugins/grpc-transcode/util.lua
+++ b/apisix/plugins/grpc-transcode/util.lua
@@ -110,6 +110,22 @@ function _M.map_message(field, default_values, request_table)
         if ty ~= "enum" and field_type:sub(1, 1) == "." then
             if request_table[name] == nil then
                 sub = default_values and default_values[name]
+            elseif core.table.isarray(request_table[name]) then
+                local sub_array = core.table.new(#request_table[name], 0)
+                for i, value in ipairs(request_table[name]) do
+                    local sub_array_obj
+                    if type(value) == "table" then
+                        sub_array_obj, err = _M.map_message(field_type,
+                                default_values and default_values[name], value)
+                        if err then
+                            return nil, err
+                        end
+                    else
+                        sub_array_obj = value
+                    end
+                    sub_array[i] = sub_array_obj
+                end
+                sub = sub_array
             else
                 sub, err = _M.map_message(field_type, default_values and default_values[name],
                                           request_table[name])
diff --git a/t/grpc_server_example/main.go b/t/grpc_server_example/main.go
index 18bda0536..1b533582c 100644
--- a/t/grpc_server_example/main.go
+++ b/t/grpc_server_example/main.go
@@ -172,6 +172,31 @@ func (s *server) SayHelloBidirectionalStream(stream pb.Greeter_SayHelloBidirecti
 	}
 }
 
+// SayMultipleHello implements helloworld.GreeterServer
+func (s *server) SayMultipleHello(ctx context.Context, in *pb.MultipleHelloRequest) (*pb.MultipleHelloReply, error) {
+	log.Printf("Received: %v", in.Name)
+	log.Printf("Enum Gender: %v", in.GetGenders())
+	msg := "Hello " + in.Name
+
+	persons := in.GetPersons()
+	if persons != nil {
+		for _, person := range persons {
+			if person.GetName() != "" {
+				msg += fmt.Sprintf(", name: %v", person.GetName())
+			}
+			if person.GetAge() != 0 {
+				msg += fmt.Sprintf(", age: %v", person.GetAge())
+			}
+		}
+	}
+
+	return &pb.MultipleHelloReply{
+		Message: msg,
+		Items:   in.GetItems(),
+		Genders: in.GetGenders(),
+	}, nil
+}
+
 func (s *server) Run(ctx context.Context, in *pb.Request) (*pb.Response, error) {
 	return &pb.Response{Body: in.User.Name + " " + in.Body}, nil
 }
diff --git a/t/grpc_server_example/proto/helloworld.pb.go b/t/grpc_server_example/proto/helloworld.pb.go
index 9cb209566..71b16a345 100644
--- a/t/grpc_server_example/proto/helloworld.pb.go
+++ b/t/grpc_server_example/proto/helloworld.pb.go
@@ -1,8 +1,10 @@
-// Copyright 2015 gRPC authors.
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
+// 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
 //
@@ -11,11 +13,12 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
+//
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.27.1
-// 	protoc        v3.6.1
+// 	protoc-gen-go v1.25.0-devel
+// 	protoc        v3.12.4
 // source: proto/helloworld.proto
 
 package proto
@@ -374,6 +377,140 @@ func (x *PlusReply) GetResult() int64 {
 	return 0
 }
 
+type MultipleHelloRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Name    string    `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	Items   []string  `protobuf:"bytes,2,rep,name=items,proto3" json:"items,omitempty"`
+	Genders []Gender  `protobuf:"varint,3,rep,packed,name=genders,proto3,enum=helloworld.Gender" json:"genders,omitempty"`
+	Persons []*Person `protobuf:"bytes,4,rep,name=persons,proto3" json:"persons,omitempty"`
+}
+
+func (x *MultipleHelloRequest) Reset() {
+	*x = MultipleHelloRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_helloworld_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MultipleHelloRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MultipleHelloRequest) ProtoMessage() {}
+
+func (x *MultipleHelloRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_helloworld_proto_msgTypes[5]
+	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 MultipleHelloRequest.ProtoReflect.Descriptor instead.
+func (*MultipleHelloRequest) Descriptor() ([]byte, []int) {
+	return file_proto_helloworld_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *MultipleHelloRequest) GetName() string {
+	if x != nil {
+		return x.Name
+	}
+	return ""
+}
+
+func (x *MultipleHelloRequest) GetItems() []string {
+	if x != nil {
+		return x.Items
+	}
+	return nil
+}
+
+func (x *MultipleHelloRequest) GetGenders() []Gender {
+	if x != nil {
+		return x.Genders
+	}
+	return nil
+}
+
+func (x *MultipleHelloRequest) GetPersons() []*Person {
+	if x != nil {
+		return x.Persons
+	}
+	return nil
+}
+
+type MultipleHelloReply struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Message string   `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+	Items   []string `protobuf:"bytes,2,rep,name=items,proto3" json:"items,omitempty"`
+	Genders []Gender `protobuf:"varint,3,rep,packed,name=genders,proto3,enum=helloworld.Gender" json:"genders,omitempty"`
+}
+
+func (x *MultipleHelloReply) Reset() {
+	*x = MultipleHelloReply{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_helloworld_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MultipleHelloReply) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MultipleHelloReply) ProtoMessage() {}
+
+func (x *MultipleHelloReply) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_helloworld_proto_msgTypes[6]
+	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 MultipleHelloReply.ProtoReflect.Descriptor instead.
+func (*MultipleHelloReply) Descriptor() ([]byte, []int) {
+	return file_proto_helloworld_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *MultipleHelloReply) GetMessage() string {
+	if x != nil {
+		return x.Message
+	}
+	return ""
+}
+
+func (x *MultipleHelloReply) GetItems() []string {
+	if x != nil {
+		return x.Items
+	}
+	return nil
+}
+
+func (x *MultipleHelloReply) GetGenders() []Gender {
+	if x != nil {
+		return x.Genders
+	}
+	return nil
+}
+
 var File_proto_helloworld_proto protoreflect.FileDescriptor
 
 var file_proto_helloworld_proto_rawDesc = []byte{
@@ -403,40 +540,63 @@ var file_proto_helloworld_proto_rawDesc = []byte{
 	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, 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, 0xc0, 0x03, 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,
-	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, 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,
+	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, 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, 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, 0x98, 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, 0x28, 0x01, 0x30, 0x01, 0x42, 0x09, 0x5a, 0x07, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	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, 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 (
@@ -452,36 +612,43 @@ 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, 5)
+var file_proto_helloworld_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
 var file_proto_helloworld_proto_goTypes = []interface{}{
-	(Gender)(0),          // 0: helloworld.Gender
-	(*Person)(nil),       // 1: helloworld.Person
-	(*HelloRequest)(nil), // 2: helloworld.HelloRequest
-	(*HelloReply)(nil),   // 3: helloworld.HelloReply
-	(*PlusRequest)(nil),  // 4: helloworld.PlusRequest
-	(*PlusReply)(nil),    // 5: helloworld.PlusReply
+	(Gender)(0),                  // 0: helloworld.Gender
+	(*Person)(nil),               // 1: helloworld.Person
+	(*HelloRequest)(nil),         // 2: helloworld.HelloRequest
+	(*HelloReply)(nil),           // 3: helloworld.HelloReply
+	(*PlusRequest)(nil),          // 4: helloworld.PlusRequest
+	(*PlusReply)(nil),            // 5: helloworld.PlusReply
+	(*MultipleHelloRequest)(nil), // 6: helloworld.MultipleHelloRequest
+	(*MultipleHelloReply)(nil),   // 7: helloworld.MultipleHelloReply
 }
 var file_proto_helloworld_proto_depIdxs = []int32{
-	0, // 0: helloworld.HelloRequest.gender:type_name -> helloworld.Gender
-	1, // 1: helloworld.HelloRequest.person:type_name -> helloworld.Person
-	0, // 2: helloworld.HelloReply.gender:type_name -> helloworld.Gender
-	2, // 3: helloworld.Greeter.SayHello:input_type -> helloworld.HelloRequest
-	4, // 4: helloworld.Greeter.Plus:input_type -> helloworld.PlusRequest
-	2, // 5: helloworld.Greeter.SayHelloAfterDelay:input_type -> helloworld.HelloRequest
-	2, // 6: helloworld.Greeter.SayHelloServerStream:input_type -> helloworld.HelloRequest
-	2, // 7: helloworld.Greeter.SayHelloClientStream:input_type -> helloworld.HelloRequest
-	2, // 8: helloworld.Greeter.SayHelloBidirectionalStream:input_type -> helloworld.HelloRequest
-	3, // 9: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply
-	5, // 10: helloworld.Greeter.Plus:output_type -> helloworld.PlusReply
-	3, // 11: helloworld.Greeter.SayHelloAfterDelay:output_type -> helloworld.HelloReply
-	3, // 12: helloworld.Greeter.SayHelloServerStream:output_type -> helloworld.HelloReply
-	3, // 13: helloworld.Greeter.SayHelloClientStream:output_type -> helloworld.HelloReply
-	3, // 14: helloworld.Greeter.SayHelloBidirectionalStream:output_type -> helloworld.HelloReply
-	9, // [9:15] is the sub-list for method output_type
-	3, // [3:9] is the sub-list for method input_type
-	3, // [3:3] is the sub-list for extension type_name
-	3, // [3:3] is the sub-list for extension extendee
-	0, // [0:3] is the sub-list for field type_name
+	0,  // 0: helloworld.HelloRequest.gender:type_name -> helloworld.Gender
+	1,  // 1: helloworld.HelloRequest.person:type_name -> helloworld.Person
+	0,  // 2: helloworld.HelloReply.gender:type_name -> helloworld.Gender
+	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
+	4,  // 7: helloworld.Greeter.Plus:input_type -> helloworld.PlusRequest
+	2,  // 8: helloworld.Greeter.SayHelloAfterDelay:input_type -> helloworld.HelloRequest
+	6,  // 9: helloworld.Greeter.SayMultipleHello:input_type -> helloworld.MultipleHelloRequest
+	2,  // 10: helloworld.Greeter.SayHelloServerStream:input_type -> helloworld.HelloRequest
+	2,  // 11: helloworld.Greeter.SayHelloClientStream:input_type -> helloworld.HelloRequest
+	2,  // 12: helloworld.Greeter.SayHelloBidirectionalStream:input_type -> helloworld.HelloRequest
+	3,  // 13: helloworld.Greeter.SayHello:output_type -> helloworld.HelloReply
+	5,  // 14: helloworld.Greeter.Plus:output_type -> helloworld.PlusReply
+	3,  // 15: helloworld.Greeter.SayHelloAfterDelay:output_type -> helloworld.HelloReply
+	7,  // 16: helloworld.Greeter.SayMultipleHello:output_type -> helloworld.MultipleHelloReply
+	3,  // 17: helloworld.Greeter.SayHelloServerStream:output_type -> helloworld.HelloReply
+	3,  // 18: helloworld.Greeter.SayHelloClientStream:output_type -> helloworld.HelloReply
+	3,  // 19: helloworld.Greeter.SayHelloBidirectionalStream:output_type -> helloworld.HelloReply
+	13, // [13:20] is the sub-list for method output_type
+	6,  // [6:13] 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() }
@@ -550,6 +717,30 @@ func file_proto_helloworld_proto_init() {
 				return nil
 			}
 		}
+		file_proto_helloworld_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MultipleHelloRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_proto_helloworld_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MultipleHelloReply); 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{
@@ -557,7 +748,7 @@ func file_proto_helloworld_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_proto_helloworld_proto_rawDesc,
 			NumEnums:      1,
-			NumMessages:   5,
+			NumMessages:   7,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/t/grpc_server_example/proto/helloworld.proto b/t/grpc_server_example/proto/helloworld.proto
index 2e18a467c..db056fade 100644
--- a/t/grpc_server_example/proto/helloworld.proto
+++ b/t/grpc_server_example/proto/helloworld.proto
@@ -25,6 +25,7 @@ service Greeter {
   rpc SayHello (HelloRequest) returns (HelloReply) {}
   rpc Plus (PlusRequest) returns (PlusReply) {}
   rpc SayHelloAfterDelay (HelloRequest) returns (HelloReply) {}
+  rpc SayMultipleHello(MultipleHelloRequest) returns (MultipleHelloReply) {}
 
   // Server side streaming.
   rpc SayHelloServerStream (HelloRequest) returns (stream HelloReply) {}
@@ -34,6 +35,7 @@ service Greeter {
 
   // Bidirectional streaming.
   rpc SayHelloBidirectionalStream (stream HelloRequest) returns (stream HelloReply) {}
+
 }
 
 enum Gender {
@@ -68,3 +70,16 @@ message PlusRequest {
 message PlusReply {
   int64 result = 1;
 }
+
+message MultipleHelloRequest {
+  string name = 1;
+  repeated string items = 2;
+  repeated Gender genders = 3;
+  repeated Person persons = 4;
+}
+
+message MultipleHelloReply{
+  string message = 1;
+  repeated string items = 2;
+  repeated Gender genders = 3;
+}
diff --git a/t/grpc_server_example/proto/helloworld_grpc.pb.go b/t/grpc_server_example/proto/helloworld_grpc.pb.go
index 7d6d8ef8b..c0527d754 100644
--- a/t/grpc_server_example/proto/helloworld_grpc.pb.go
+++ b/t/grpc_server_example/proto/helloworld_grpc.pb.go
@@ -1,4 +1,8 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.2.0
+// - protoc             v3.12.4
+// source: proto/helloworld.proto
 
 package proto
 
@@ -22,6 +26,7 @@ type GreeterClient interface {
 	SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
 	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)
 	// Server side streaming.
 	SayHelloServerStream(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (Greeter_SayHelloServerStreamClient, error)
 	// Client side streaming.
@@ -65,6 +70,15 @@ func (c *greeterClient) SayHelloAfterDelay(ctx context.Context, in *HelloRequest
 	return out, nil
 }
 
+func (c *greeterClient) SayMultipleHello(ctx context.Context, in *MultipleHelloRequest, opts ...grpc.CallOption) (*MultipleHelloReply, error) {
+	out := new(MultipleHelloReply)
+	err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayMultipleHello", 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 {
@@ -170,6 +184,7 @@ type GreeterServer interface {
 	SayHello(context.Context, *HelloRequest) (*HelloReply, error)
 	Plus(context.Context, *PlusRequest) (*PlusReply, error)
 	SayHelloAfterDelay(context.Context, *HelloRequest) (*HelloReply, error)
+	SayMultipleHello(context.Context, *MultipleHelloRequest) (*MultipleHelloReply, error)
 	// Server side streaming.
 	SayHelloServerStream(*HelloRequest, Greeter_SayHelloServerStreamServer) error
 	// Client side streaming.
@@ -192,6 +207,9 @@ func (UnimplementedGreeterServer) Plus(context.Context, *PlusRequest) (*PlusRepl
 func (UnimplementedGreeterServer) SayHelloAfterDelay(context.Context, *HelloRequest) (*HelloReply, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method SayHelloAfterDelay not implemented")
 }
+func (UnimplementedGreeterServer) SayMultipleHello(context.Context, *MultipleHelloRequest) (*MultipleHelloReply, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method SayMultipleHello not implemented")
+}
 func (UnimplementedGreeterServer) SayHelloServerStream(*HelloRequest, Greeter_SayHelloServerStreamServer) error {
 	return status.Errorf(codes.Unimplemented, "method SayHelloServerStream not implemented")
 }
@@ -268,6 +286,24 @@ func _Greeter_SayHelloAfterDelay_Handler(srv interface{}, ctx context.Context, d
 	return interceptor(ctx, in, info, handler)
 }
 
+func _Greeter_SayMultipleHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MultipleHelloRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(GreeterServer).SayMultipleHello(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/helloworld.Greeter/SayMultipleHello",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(GreeterServer).SayMultipleHello(ctx, req.(*MultipleHelloRequest))
+	}
+	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 {
@@ -360,6 +396,10 @@ var Greeter_ServiceDesc = grpc.ServiceDesc{
 			MethodName: "SayHelloAfterDelay",
 			Handler:    _Greeter_SayHelloAfterDelay_Handler,
 		},
+		{
+			MethodName: "SayMultipleHello",
+			Handler:    _Greeter_SayMultipleHello_Handler,
+		},
 	},
 	Streams: []grpc.StreamDesc{
 		{
diff --git a/t/grpc_server_example/proto/import.pb.go b/t/grpc_server_example/proto/import.pb.go
index 28fabf3f3..a5575fdbd 100644
--- a/t/grpc_server_example/proto/import.pb.go
+++ b/t/grpc_server_example/proto/import.pb.go
@@ -1,7 +1,24 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.27.1
-// 	protoc        v3.6.1
+// 	protoc-gen-go v1.25.0-devel
+// 	protoc        v3.12.4
 // source: proto/import.proto
 
 package proto
diff --git a/t/grpc_server_example/proto/src.pb.go b/t/grpc_server_example/proto/src.pb.go
index 8e6a32ae3..74fa884d1 100644
--- a/t/grpc_server_example/proto/src.pb.go
+++ b/t/grpc_server_example/proto/src.pb.go
@@ -1,7 +1,24 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.27.1
-// 	protoc        v3.6.1
+// 	protoc-gen-go v1.25.0-devel
+// 	protoc        v3.12.4
 // source: proto/src.proto
 
 package proto
diff --git a/t/grpc_server_example/proto/src_grpc.pb.go b/t/grpc_server_example/proto/src_grpc.pb.go
index 01fe1502d..d4015ed99 100644
--- a/t/grpc_server_example/proto/src_grpc.pb.go
+++ b/t/grpc_server_example/proto/src_grpc.pb.go
@@ -1,4 +1,8 @@
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.2.0
+// - protoc             v3.12.4
+// source: proto/src.proto
 
 package proto
 
diff --git a/t/plugin/grpc-transcode3.t b/t/plugin/grpc-transcode3.t
new file mode 100644
index 000000000..a027a84bd
--- /dev/null
+++ b/t/plugin/grpc-transcode3.t
@@ -0,0 +1,124 @@
+#
+# 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");
+    }
+
+    if ((!defined $block->error_log) && (!defined $block->no_error_log)) {
+        $block->set_value("no_error_log", "[error]");
+    }
+});
+
+run_tests;
+
+__DATA__
+
+=== TEST 1: set rule
+--- config
+    location /t {
+       content_by_lua_block {
+          local http = require "resty.http"
+          local t = require("lib.test_admin").test
+          local code, body = t('/apisix/admin/proto/1',
+                ngx.HTTP_PUT,
+                [[{
+                   "content" : "syntax = \"proto3\";
+                    package helloworld;
+                    service Greeter {
+                         rpc SayMultipleHello(MultipleHelloRequest) returns (MultipleHelloReply) {}
+                     }
+
+                     enum Gender {
+                           GENDER_UNKNOWN = 0;
+                           GENDER_MALE = 1;
+                           GENDER_FEMALE = 2;
+                      }
+
+                       message Person {
+                           string name = 1;
+                           int32 age = 2;
+                       }
+
+                      message MultipleHelloRequest {
+                          string name = 1;
+                          repeated string items = 2;
+                          repeated Gender genders = 3;
+                          repeated Person persons = 4;
+                    }
+
+                    message MultipleHelloReply{
+                          string message = 1;
+                    }"
+                }]]
+              )
+
+             if code >= 300 then
+                 ngx.say(body)
+                 return
+              end
+
+             local code, body = t('/apisix/admin/routes/1',
+                ngx.HTTP_PUT,
+                [[{
+                   "methods": ["POST"],
+                    "uri": "/grpctest",
+                    "plugins": {
+                        "grpc-transcode": {
+                            "proto_id": "1",
+                            "service": "helloworld.Greeter",
+                            "method": "SayMultipleHello"
+                       }
+                    },
+                    "upstream": {
+                        "scheme": "grpc",
+                            "type": "roundrobin",
+                            "nodes": {
+                            "127.0.0.1:50051": 1
+                        }
+                    }
+                }]]
+             )
+
+            if code >= 300 then
+                ngx.say(body)
+                return
+            end
+            ngx.say(body)
+        }
+    }
+--- response_body
+passed
+
+
+
+=== TEST 2: hit route
+--- request
+POST /grpctest
+{"name":"world","persons":[{"name":"Joe","age":1},{"name":"Jake","age":2}]}
+--- more_headers
+Content-Type: application/json
+--- response_body chomp
+{"message":"Hello world, name: Joe, age: 1, name: Jake, age: 2"}