Here's what I want to do at a high-level
- Capture some http headers in a WebFilter
- In the Controller method, I make a grpc call
- I'd like to propagate the http headers as grpc Metadata headers
Currently, my working implementation is
- WebFilter captures http headers and writes to reactor Context
- Controller method extracts reactor Context and passes it into a Grpc ClientInterceptor
- Grpc ClientInterceptor extracts http headers from Context and injects into Grpc Metadata headers
But I'd like to avoid having the Controller method doing any work (step 2 above).
Here's an implementation but looking for a way to get the http headers to Grpc Metadata without explicitly passing them in from Controller method.
WebFilter
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {    return chain.filter(exchange)      .contextWrite(Context.of("my-header", "header-value"));  }  Controller method
@GetMapping  public Mono<String> testHeaderPropagation() throws Exception {    return Mono.deferContextual(reactorContext -> {      Response response = grpcStub        .withInterceptors(new GrpcClientInterceptor(reactorContext))        .call(request);      return Mono.just(response.getMessage());    });  }  GrpcClientInterceptor
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {    final ClientCall<ReqT, RespT> call = next.newCall(method, callOptions);    return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(call) {      @Override      public void start(Listener<RespT> responseListener, Metadata headers) {        Metadata.Key < String > key =            Metadata.Key.of("filter-context", Metadata.ASCII_STRING_MARSHALLER);        headers.put(key, context.get("filter-context"));          delegate().start(responseListener, headers);      }    };  }  I'd like simplify my Controller method (remove the explicit passing in of the reactor Context into a clientInterceptor)
grpcStub.call(request)  I believe Spring Sleuth has a way of doing this but not sure how to adapt its approach. What clever thing am I missing?
Edit
The reason I'm pushing for a version that involves minimal controller method code is because other developers will be writing the controller and methods. I want to establish a pattern that doesn't require extra wiring if possible, otherwise there's a chance someone forgets to do it or does it wrong.
Edit
Follow-up question. Instead of having the controller method pass in the Context to the clientInterceptor, I tried to get the Context inside the Grpc ClientInterceptor but that doesn't seem to work.
Here's is what I tried to do
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {    final ClientCall<ReqT, RespT> call = next.newCall(method, callOptions);      return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(call) {      @Override      public void start(Listener<RespT> responseListener, Metadata headers) {          Mono.deferContextual(context -> {          Metadata.Key < String > key =              Metadata.Key.of("CONTEXT-HEADER", Metadata.ASCII_STRING_MARSHALLER);          headers.put(key, context.get("CONTEXT-HEADER"));            delegate().start(responseListener, headers);            return Mono.empty();        }).subscribe();          }    };  }  But I get an error
reactor.core.Exceptions$ErrorCallbackNotImplemented: java.util.NoSuchElementException: Context does not contain key CONTEXT-HEADER
Trying to understand why the reactor pipeline breaks here
https://stackoverflow.com/questions/66880712/can-i-pass-reactor-context-into-grpc-clientinterceptor-implicitly March 31, 2021 at 10:09AM 
没有评论:
发表评论