Lets say I have a protobuf / GRPC definition like this:
message GetFooRequest {
string bar
}
message GetFooResponse {
string baz
}
service FooService {
rpc GetFoo(GetFooRequest) returns (GetFooResponse) {
}
}
Then in go my service might have a function:
func (f *FooServer) GetFoo(ctx context.Context, req *pb.GetFooRequest) (*pb.GetFooResponse, error) {
if req == nil {
// is this reachable?
}
return &pb.GetFooResponse{}, nil
}
Now my question is: can req
actually be nil
? Is there (intentionally) a code path where a client can send a request to GetFoo
that will be decoded to a nil
body?
The generated code which invokes your unary RPC receiver will always provide a non-nil reference. The code which generates this method can be found here. This code will result in logic which looks like this hello world example
func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(HelloRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GreeterServer).SayHello(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Greeter_SayHello_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
}
return interceptor(ctx, in, info, handler)
}
The key takeaway here is in := new(HelloRequest)
, which always allocates a non-nil request.
That said, nested message type fields within the provided request struct may still be nil, due to how the go protobuf implementation handles unmarshalling default values vs absent values.