2

Grpc-node: How to edit metadata and send back edited metadata to client?

Below is currently what I have at the moment, and it seems like the metadata can be passed to the grpc-client RPC method ('greetmath'), but I can't edit the metadata in the server and send it back to the client. It can only send back metadata that was originally created.

Is there anyway I can edit the metadata in the grpc server and send that edited metadata to the grpc client?

Greeter_server.js

const path = require('path');
const PROTO_PATH = path.join(__dirname, '../proto/greet.proto');
// console.log("Proto path: ", PROTO_PATH);
const protoLoader = require('@grpc/proto-loader') //require('@grpc/proto-loader')
const grpc = require('grpc')


//grpc service definition for greet
const greetProtoDefinition = protoLoader.loadSync(PROTO_PATH, {
     keepCase: true,
      longs: String,
      enums: String,
      defaults: true,
      oneofs: true
});

const greetPackageDefinition = grpc.loadPackageDefinition(greetProtoDefinition).greet

function greetFunc(call, callback) {
    var firstName = call.request.greeting.first_name;
    var lastName = call.request.greeting.last_name;

    callback(null, {result: "Hello " + firstName + " " + lastName});
}


function greetMath(call, callback) {
    console.log("callback: ", call);
    console.log("Meta data: ", call.metadata._internal_repr.somekey);
    call.metadata._internal_repr.somekey.push('random');    

    var firstName = call.request.greeting.first_name;
    var lastName = call.request.greeting.last_name;
    let current = Number(process.hrtime.bigint());
    console.log("call obj: ", call);
    console.log("callback obj: ", callback);
    callback(null, {result: "Hello " + firstName + " " + lastName + " current: " + current});

}

function main() {
    const server = new grpc.Server()

    server.addService(greetPackageDefinition.GreetService.service, {
         greet: greetFunc,
         greetMath: greetMath
    });

    server.bind("127.0.0.1:4000", grpc.ServerCredentials.createInsecure());
    server.start();
    console.log("Server Running at http://127.0.0.1:50051")
}
main()

greet.proto

syntax = "proto3";

package greet;

service GreetService {
    //Unary API
    rpc Greet (GreetRequest) returns (GreetResponse) {};
    rpc GreetMath(GreetRequest) returns (GreetResponse) {};
}

message Greeting {
    string first_name = 1;
    string last_name = 2;
}

message GreetRequest {
     Greeting greeting = 1;
}

message GreetResponse {
     string result = 1;
}

greeter_client.js

const path = require('path');
const PROTO_PATH = path.join(__dirname, '../proto/greet.proto');
const protoLoader = require('@grpc/proto-loader') //require('@grpc/proto-loader')
const grpc = require('grpc')


//grpc service definition for greet
const greetProtoDefinition = protoLoader.loadSync(PROTO_PATH, {
     keepCase: true,
      longs: String,
      enums: String,
      defaults: true,
      oneofs: true
});

const greetPackageDefinition = grpc.loadPackageDefinition(greetProtoDefinition).greet

const client = new greetPackageDefinition.GreetService("localhost:4000",
        grpc.credentials.createInsecure()
)


function callGreetings() {
    var request = {
         greeting: {
              first_name: "Jerry",
              last_name: "Tom"
         }
    }
     
    client.greet(request, (error, response) => {
        if(!error) {
            console.log("Greeting Response: ", response.result);
        } else {
            console.error(error)
        }
    })
}


function callGreetingsLogger() {
    var request = {
        greeting: {
             first_name: "Jerry",
             last_name: "Tom"
        }
   }
   let meta = new grpc.Metadata();
   meta.add('someKey', 'someVal'); 
   let end;
   let start = Number(process.hrtime.bigint());

   client.greetMath(request, meta, (error, response) => {
        if(!error) {
           console.log("res: ", response);
           console.log("metadata: ", meta);
           console.log("Greeting Response: ", response.result);
           end = Number(process.hrtime.bigint());
    

           console.log("start: ", start);
           console.log("end: ", end);
           console.log("end - start", (end - start)/ 1000000, "ms");
           
        function getDateTime() {
            var date = new Date();
            var hour = date.getHours();
            hour = (hour < 10 ? "0" : "") + hour;
            var min  = date.getMinutes();
            min = (min < 10 ? "0" : "") + min;
            var sec  = date.getSeconds(); sec = (sec < 10 ? "0" : "") + sec; 
            var year = date.getFullYear();
            var month = date.getMonth() + 1; month = (month < 10 ? "0" : "") + month;
            var day  = date.getDate(); day = (day < 10 ? "0" : "") + day;
            return month + "/" + day + "/" + year + "  |  Time: " + hour + ":" + min + ":" + sec;
        }

        let currentTime = getDateTime();
           console.log("Current time: ", currentTime);
       
       } else {
           console.error(error)
       }
   });

}

function main() {
    callGreetings();
    callGreetingsLogger();
}

main()

TheRoadLessTaken
  • 531
  • 2
  • 7
  • 15
  • You say that you can create metadata on the server and send it, but you can't edit the metadata that you receive on the server and then send it back to the client. It would help to see what code you have to do each of those things to understand what the difference is between the two approaches. – murgatroid99 Jul 20 '20 at 17:38

2 Answers2

1

The question says "I can't edit the metadata in the server and send it back to the client". The code in the question edits the metadata, so I assume that that is the code you are trying to use to do that, but that is not sending the metadata because it never calls the sendMetadata method of the call. You need to call that method to send metadata back to the client.

In addition, you should not touch the _internal_repr of the metadata. The client code handles this correctly by calling meta.add(). The server code should do the same. So, instead of call.metadata._internal_repr.somekey you should write call.metadata.get('somekey') and instead of call.metadata._internal_repr.somekey.push('random') you should write call.metadata.add('somekey', 'random').

murgatroid99
  • 19,007
  • 10
  • 60
  • 95
0

I suspect that you're trying to change the gRPC's internal metadata (call.metadata._internal_repr) and this is not permitted.

In Go(lang) for example, the only methods exposed for Metadata are e.g. GetMetadata() and there's intentionally no public mechanism to change this data.

You have some options:

  • Create a message type that you carry within your messages for metadata.
  • Consider Custom Options and Extensions if you're trying to extend the capabilities of gRPC.
DazWilkin
  • 32,823
  • 5
  • 47
  • 88
  • This isn't the issue. The `call.metadata` object is a `Metadata` instance and it's supposed to be accessible to the user. The `_internal_repr` member is logically internal, but is accessible because of how JavaScript prototypes work. The user isn't supposed to touch it, but doing so shouldn't cause the problem described in the question. – murgatroid99 Jul 22 '20 at 16:59