Access message request in first gRPC interceptor before headers in second gRPC interceptor

Viewed 807

My question is similar to this post, but it seems to be old and inactive so I am reposting here.

Basically, I have a weird use case where I need to send my authorization token in the gRPC request body (instead of the header). My idea is to use a set of interceptors where the first one will read the gRPC message and set the Authorization header based on the token in the message. The second interceptor will be the normal authorization interceptor used and will read the token that was just set to the Authorization header. I was hoping to do it this way as that would allow me to reuse the already existing authorization interceptor code.

I am calling the interceptors as such:

ServerInterceptors.intercept(
    new MyResource(resource),
    new SecondInterceptorHeaderAuthorization(),
    new FirstInterceptorReadTokenFromMessageBody()
)

Where FirstInterceptorReadTokenFromMessageBody() looks like this:

public class FirstInterceptorReadTokenFromMessageBody implements ServerInterceptor {

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


    return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(
            next.startCall(call, headers)) {
          @Override
          public void onMessage(ReqT request) {

            var value = ((MyRequest) request).getAuthorization();
            Metadata.Key<String> key =
                Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER);
            headers.put(key, value);

            super.onMessage(request);
          }
        };
  }
}

And SecondInterceptorHeaderAuthorization() interceptor:

public class SecondInterceptorHeaderAuthorization implements ServerInterceptor {

  public <ReqT, RespT> Listener<ReqT> interceptCall(
      ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
      .... handle authorization here
}

The interceptors are getting triggered correctly just in the reverse order; the request ends up getting handled by SecondInterceptorHeaderAuthorization() with the header authorization first then by FirstInterceptorReadTokenFromMessageBody(). How can I have the message interceptor run first, then the header interceptor? Is this even possible? Is there a better approach to solving this problem I am missing?

1 Answers

I figured it out. Basically, I followed what was described in the thread here

My FirstInterceptorReadTokenFromMessageBody class now looks like this:

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

    final Listener<ReqT> authorizationLookUpCallListener =
        new ForwardingServerCallListener<ReqT>() {
          private final Listener<ReqT> NOOP = new Listener<ReqT>() {};
          private Listener<ReqT> delegate = NOOP;

          @Override
          protected Listener<ReqT> delegate() {
            return delegate;
          }

          @Override
          public void onMessage(ReqT message) {
            // ensures that this interceptor only run first
            if (delegate == NOOP) {
              if (message instanceof MyRequest) {
                String auth_token = ((MyRequest) message).getAuthToken();
                headers.put(
                    Metadata.Key.of("my-auth-token-header", Metadata.ASCII_STRING_MARSHALLER),
                    auth_token);
              }
              delegate = next.startCall(call, headers);
            }
            super.onMessage(message);
          }
        };

    ServerCallHandler<ReqT, RespT> handler =
        (call1, headers1) -> {
          call1.request(1);
          return authorizationLookUpCallListener;
        };

    return handler.startCall(call, headers);
  }
}
Related