2

I am able to obtain the Metadata using an interceptor on the channel so I can tell it is being sent.

.intercept(
  new ServerInterceptor() {
    @Override
    public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
        ServerCall<ReqT, RespT> call,
        Metadata headers,
        ServerCallHandler<ReqT, RespT> next) {

      System.out.println(headers);
      return next.startCall(call, headers);
    }
  })

But I was wondering how do I access it from a service implementation? Am I supposed to do some round about ThreadLocal work to pass it down?

This is different from How to read Meta data in gRPC using Java at client side which talks about it on the client side.

Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265
  • Is this similar to https://stackoverflow.com/questions/40112374/how-do-i-access-request-metadata-for-a-java-grpc-service-i-am-defining/40113309 ? – Eric Anderson Aug 23 '22 at 16:16
  • The answer does not show it getting the meta data in the interceptor but that's minor I could forsee what needs to be used. Also it is grouped under python rather than Java – Archimedes Trajano Aug 23 '22 at 16:21

1 Answers1

0

As I noted in the OP I was thinking it had to be some round about way. I did get it to work, but does not seem "ergonomic"

Added two constants in my GrpcServer class. (Could be moved somewhere else)

  public static Metadata.Key<String> JWT_CLAIMS_KEY =
      Metadata.Key.of("jwtClaims", Metadata.ASCII_STRING_MARSHALLER);

  // The parameter value here is irrelevant and does not need to match the previous one, but useful for debugging.
  public static Context.Key<String> JWT_CLAIMS_CONTEXT_KEY = Context.key("jwtClaims");

The interceptor is added to the builder.

    .intercept(
      new ServerInterceptor() {
        @Override
        public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
            ServerCall<ReqT, RespT> call,
            Metadata headers,
            ServerCallHandler<ReqT, RespT> next) {
          final var context =
            Context.current()
              withValue(JWT_CLAIMS_CONTEXT_KEY, headers.get(JWT_CLAIMS_KEY));
          return Contexts.interceptCall(context, call, headers, next);
       }
     })

The service implementation uses the Context.get() method to obtain the value.

  @Override
  public void echo(
      EchoOuterClass.EchoRequest request,
      StreamObserver<EchoOuterClass.EchoResponse> responseObserver) {

    System.out.println(GrpcServer.JWT_CLAIMS_CONTEXT_KEY.get());
    responseObserver.onNext(
        EchoOuterClass.EchoResponse.newBuilder().setMessage(request.getMessage()).build());
    responseObserver.onCompleted();
  }

I guess I was looking for something like...

.intercept(Contexts.serverInterceptorBuilder()
  .withMetadataValue(JWT_CLAIMS_KEY, JWT_CONTEXT_CLAIMS_KEY)
  .build();

And which would be implemented as something like (note not tested, just writing from my head...)

ServerInterceptorBuilder serverInterceptorBuilder() {
  return new ServerInterceptorBuilder(Context.current());
}
private static class ServerInterceptorBuilder {
  private Context context;
  ServerInterceptorBuilder(Context context) {
    this.context = context;
  }
  public <X> ServerInterceptorBuilder withMetadataValue(
      Metadata.Key<X> metadataKey,
      Context.Key<X> contextKey) {
    context = context.withValue(contextKey, metadataKey);
    return this;
  }
  public ServerInterceptor build() {
    return new ServerInterceptor() {
      new ServerInterceptor() {
        @Override
        public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
            ServerCall<ReqT, RespT> call,
            Metadata headers,
            ServerCallHandler<ReqT, RespT> next) {
           
          return Contexts.interceptCall(context, call, headers, next);
       }
    };
  }
}
Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265