Skip to content

kratos http parse pb message body panic #2782

@qjw

Description

@qjw

What happened:

使用下列的pb生成 http代码, 使用 json payload正常, 但使用pb序列化后, 解析 request panic

// 客户端
message Client {
  option (google.api.resource) = {
    type: "v1.greeter.test.example.com/Client"
    pattern: "clients/{client_int32}"
  };

  // 资源名称
  string name = 1;

  // id
  int32 id = 2;

  // 状态
  ClientStatus status = 3 [(google.api.field_behavior) = OUTPUT_ONLY];

  string display_name = 4 ;
}
service ExampleService {
  // 创建Client
  // (-- api-linter: core::0133::method-signature=disabled
  //     aip.dev/not-precedent: 顶层资源无需parent --)
  rpc CreateClient(CreateClientRequest) returns (CreateClientResponse) {
    option (google.api.method_signature) = "client";
    option (google.api.http) = {
      post: "/api/v1/clients"
      body: "client"
    };
  }
}

// 创建Client请求
message CreateClientRequest {
  // 创建Client
  Client client = 1 [(google.api.field_behavior) = REQUIRED];
}

// 创建Client响应
message CreateClientResponse {
  // 创建Client响应
  Client client = 1 [(google.api.field_behavior) = REQUIRED];
}

从json转成pb,后端无需修改代码, 前端修改 Content-Type : application/proto. 具体的代码实现在 github.com/go-kratos/kratos/v2/transport/http/codec.go

// CodecForRequest get encoding.Codec via http.Request
func CodecForRequest(r *http.Request, name string) (encoding.Codec, bool) {
	for _, accept := range r.Header[name] {
		codec := encoding.GetCodec(httputil.ContentSubtype(accept))
		if codec != nil {
			return codec, true
		}
	}
	return encoding.GetCodec("json"), false
}

原因

panic 日志

2023/04/12 14:16:11 http: panic serving 192.168.105.20:49928: interface conversion: **v1.Client is not protoreflect.ProtoMessage: missing method ProtoReflect
goroutine 107 [running]:
net/http.(*conn).serve.func1()
	/data/go1.19/src/net/http/server.go:1850 +0x148
panic({0x13f86a0, 0xc0004aa480})
	/data/go1.19/src/runtime/panic.go:890 +0x267
github.com/go-kratos/kratos/v2/encoding/proto.codec.Unmarshal({}, {0xc000570400, 0x13, 0x200}, {0x134db20, 0xc0004aa478})
	/data/common/kratos-layout/vendor/github.com/go-kratos/kratos/v2/encoding/proto/proto.go:26 +0x67
github.com/go-kratos/kratos/v2/transport/http.DefaultRequestDecoder(0xc00050cc00, {0x134db20, 0xc0004aa478})
	/data/common/kratos-layout/vendor/github.com/go-kratos/kratos/v2/transport/http/codec.go:72 +0x286
github.com/go-kratos/kratos/v2/transport/http.(*wrapper).Bind(0xc000518980, {0x134db20, 0xc0004aa478})
	/data/common/kratos-layout/vendor/github.com/go-kratos/kratos/v2/transport/http/context.go:99 +0x62
go.example.com/apis/lixin/test/kratos-layout/v1._KratoLayoutService_CreateClient0_HTTP_Handler.func1({0x7f03b05effc8, 0xc000518980})

github.com/go-kratos/kratos/v2/encoding/proto/proto.go

func (codec) Unmarshal(data []byte, v interface{}) error {
       // v 是一个二级指针 (**Client)
       // v 是一个二级指针 (**Client)
       // v 是一个二级指针 (**Client)
	return proto.Unmarshal(data, v.(proto.Message))
}

生成的Go代码如下

// 创建Client请求
type CreateClientRequest struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	// 注意这里是指针
	// 注意这里是指针
	// 注意这里是指针
	Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"`
}

func _KratoLayoutService_CreateClient0_HTTP_Handler(srv KratoLayoutServiceHTTPServer) func(ctx http.Context) error {
	return func(ctx http.Context) error {
		var in CreateClientRequest

                // 这里进一步取指针的地址
                // 这里进一步取指针的地址
                // 这里进一步取指针的地址
		if err := ctx.Bind(&in.Client); err != nil {
			return err
		}

                // 。。。。。。。
		return ctx.Result(200, reply)
	}
}

image

What you expected to happen:

How to reproduce it (as minimally and precisely as possible):

Anything else we need to know?:

Environment:

  • Kratos version (use kratos -v): github.com/go-kratos/kratos/v2 v2.5.3
  • Go version (use go version): 1.19
  • OS (e.g: cat /etc/os-release):

NAME="Ubuntu"
VERSION="20.04.3 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.3 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

  • Others:

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions