3

I am working with gRPC and Protobuf, using a C++ server and a C++ client, as well as a grpc-js client. Is there a way to get a read on all of the HTTP request/response headers from the transport layer in gRPC? I am looking for the sort of typical client/server HTTP headers - particularly, I would like to see what version of the protocol is being used (whether it is HTTP1.1/2). I know that gRPC is supposed to be using HTTP2, but I am trying to confirm it at a low level.

In a typical gRPC client implementation you have something like this:

class PingPongClient {
 public:
  PingPongClient(std::shared_ptr<Channel> channel)
      : stub_(PingPong::NewStub(channel)) {}

  // Assembles the client's payload, sends it and presents the response back
  // from the server.
  PingPongReply PingPong(PingPongRequest request) {
    // Container for the data we expect from the server.
    PingPongReply reply;

    // Context for the client. It could be used to convey extra information to
    // the server and/or tweak certain RPC behaviors.
    ClientContext context;

    // The actual RPC.
    Status status = stub_->Ping(&context, request, &reply);

    // Act upon its status.
    if (status.ok()) {
        return reply;
    } else {
      auto errorMsg = status.error_code() + ": " + status.error_message();
      std::cout << errorMsg << std::endl;
      throw std::runtime_error(errorMsg);
    }
  }

 private:
  std::unique_ptr<PingPong::Stub> stub_;
};

and on the serverside, something like:

class PingPongServiceImpl final : public PingPong::Service {
  Status Ping(
    ServerContext* context,
    const PingPongRequest* request,
    PingPongReply* reply
  ) override {
    std::cout << "PingPong" << std::endl;
    printContextClientMetadata(context->client_metadata());

    if (request->input_msg() == "hello") {
      reply->set_output_msg("world");
    } else {
      reply->set_output_msg("I can't pong unless you ping me 'hello'!");
    }

    std::cout << "Replying with " << reply->output_msg() << std::endl;

    return Status::OK;
  }
};

I would think that either ServerContext or the request object might have access to this information, but context seems to only provide an interface into metadata, which is custom.

None of the gRPC C++ examples give any indication that there is such an API, nor do any of the associated source/header files in the gRPC source code. I have exhausted my options here in terms of tutorials, blog posts, videos, and documentation - I asked a similar question on the grpc-io forum, but have gotten no takers. Hoping the SO crew has some insights here!

I should also note that I experimented with passing a variety of environment variables as flags to the running processes to see if I can get details about HTTP headers, but even with these flags enabled (the HTTP-related ones), I do not see basic HTTP headers.

netpoetica
  • 3,375
  • 4
  • 27
  • 37
  • I'm confident that you can enumerate request|response headers and HTTP version by hitting a server with [gRPCurl](https://github.com/fullstorydev/grpcurl). I don't have a client to hand but, IIRC, using verbose output. Hope that helps! – DazWilkin Mar 07 '21 at 06:02
  • @DazWilkin thanks for making me aware of this! In this case though, I think grpcurl is acting as the client, so it is sort of eliminating half of the equation by ignoring the C++/JS client that is actually making the request. It is a useful tool though – netpoetica Mar 08 '21 at 05:05

1 Answers1

5

First, the gRPC libraries absolutely do use HTTP/2. The protocol is explicitly defined in terms of HTTP/2.

The gRPC libraries do not directly expose the raw HTTP headers to the application. However, they do have trace logging options that can log a variety of information for debugging purposes, including headers. The tracers can be enabled by setting the environment variable GRPC_TRACE. The environment variable GRPC_VERBOSITY=DEBUG should also be set to make sure that all of the logs are output. More information can be found in this document describing how the library uses envinronment variables.

In the C++ library, the http tracer should log the raw headers. The grpc-js library has different internals and different tracer definitions, so you should use the call_stream tracer for that one. Those will also log other request information, but it should be pretty easy to pick out the headers.

murgatroid99
  • 19,007
  • 10
  • 60
  • 95
  • Does that call_stream flag apply only if you are using a NodeJS server, or is there a way to provide these flags to grpc-js clients as well? I could not find any mechanism for adding tracing on the client side. In my case, my server is a C++ server w/ JS clients, and call_stream is not available as an option for C++ gRPC server. I imagine you mean if I were using a JS server from grpc-js, but just verifying for future readers – netpoetica Mar 08 '21 at 22:20
  • 1
    These environment variables can be set for either the client or the server, and they're independent. It's just something that applies to the specific process. So, if you set the environment variables `GRPC_TRACE=call_stream` and `GRPC_VERBOSITY=DEBUG` for the grpc-js client, you will see those logs. And if you set the environment variables `GRPC_TRACE=http` and `GRPC_VERBOSITY=DEBUG` for the C++ server, you will see the corresponding logs. – murgatroid99 Mar 08 '21 at 22:24
  • call_stream tracer is undocumented in the gRPC environment vars here https://github.com/grpc/grpc/blob/master/doc/environment_variables.md#grpc-environment-variables and here https://grpc.github.io/grpc/node/index.html and there is no specific documentation for grpc-js. I found that I *had* to use exactly `GRPC_VERBOSITY="DEBUG" GRPC_TRACE=all npm test` - if you combine like `GRPC_VERBOSITY=DEBUG GRPC_TRACE=all npm test` no logs show up at all (some kind of bug in there). This command worked to get client logs: `GRPC_VERBOSITY="DEBUG" GRPC_TRACE="call_stream" npm test` – netpoetica Mar 08 '21 at 23:00