Дальнейшее расследование на примере искусственного сервера:
func (s *mygRPC) GetStatus(context.Context, *empty.Empty) (*pb.Status, error) {
log.Println("cli: GetStatus()")
//return &pb.Status{}, nil
return nil, nil // <- can server return a nil status message (with nil error)
}
и тестирование реакций клиент / сервер:
КЛИЕНТ:
ERROR: rpc error: code = Internal desc = grpc: error while marshaling: proto: Marshal called with nil
SERVER:
2019/05/14 16:09:50 cli: GetStatus()
ERROR: 2019/05/14 16:09:50 grpc: server failed to encode response: rpc error: code = Internal desc = grpc: error while marshaling: proto: Marshal called with nil
Таким образом, даже если кто-то хочет законно вернуть нулевое значение, транспорт gRPC
не допустит его.
Примечание: код на стороне сервера все еще выполняется - как и ожидалось, - но, что касается клиента, вызов gRPC
не удался.
Вывод: действительный (err==nil
) ответ сервера всегда вернет правильное (не nil
) сообщение.
РЕДАКТИРОВАТЬ:
Проверка источника gRPC
показывает, где перехвачено сообщение nil
:
server.go
func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options, comp encoding.Compressor) error {
data, err := encode(s.getCodec(stream.ContentSubtype()), msg)
if err != nil {
grpclog.Errorln("grpc: server failed to encode response: ", err)
return err
}
// ...
}
rpc_util.go
func encode(c baseCodec, msg interface{}) ([]byte, error) {
if msg == nil { // NOTE: typed nils will not be caught by this check
return nil, nil
}
b, err := c.Marshal(msg)
if err != nil {
return nil, status.Errorf(codes.Internal, "grpc: error while marshaling: %v", err.Error())
}
// ...
}
Комментарий в этой строке является ключевым:
if msg == nil { // NOTE: typed nils will not be caught by this check }
Так что, если бы кто-то использовал отражение на нашем набранном нуле, reflect.ValueOf(msg).IsNil()
вернул бы true
.Следующие c.Marshal(msg)
ошибок - и вызов не может отправить ответное сообщение клиенту.