question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to add a single retry for a specific scenario in interceptor?

See original GitHub issue

What version of gRPC are you using?

1.20.0

I am working on auth interceptor which responsible for auth tokens on every single call. Sometimes, when a token expired, I have to change auth metadata on the client side and make a retry for this call.

I have already implemented auth interceptor on start and not I’m trying to find out how to make recall when the interceptor found that token expired on onClose(…){…}

Could anybody give advice or other help with this issue?

Interceptor code:

@Override
  public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(final MethodDescriptor<ReqT, RespT> method,
      final CallOptions callOptions, final Channel next) {
    return new SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {

      // add a token into the headers
      @Override
      public void start(final Listener<RespT> responseListener, final Metadata headers) {
        if (jwtAuthHeader != null) {
          headers.put(ProtoConstants.REQ_HEADER_AUTH, jwtAuthHeader);
        } else if (basicAuthHeader != null) {
          headers.put(ProtoConstants.REQ_HEADER_AUTH, basicAuthHeader);
        }

        super.start(new SimpleForwardingClientCallListener<RespT>(responseListener) {
          // get jwt token
          @Override
          public void onHeaders(final Metadata headers) {
            final String jwtToken = headers.get(ProtoConstants.RSP_HEADER_JWT_TOKEN);
            if (jwtToken != null) {
              jwtAuthHeader = getAuthHeader(AUTH_SCHEME_JWT, jwtToken);
            }
            super.onHeaders(headers);
          }

          @Override
          public void onClose(final Status status, final Metadata trailers) {
            if (!status.isOk()) {
              switch (status.getCode()) {
              case UNAUTHENTICATED:
                if (ProtoConstants.STATUS_TOKEN_EXPIRED.equalsIgnoreCase(status.getDescription())) {
                  // TODO:
                  // here I am trying to do it
                  // TODO
                }
                break;
              default:
                break;
              }
            }
            super.onClose(status, trailers);
          }
        }, headers);
      }
    };
  }

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:13 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
sanjaypujarecommented, Jun 13, 2019

@dshylov My point is client-interceptor may not be the best place for this kind of logic given the constraints. Can you check the JWT “exp” field and if the token has expired or about to expire in X milliseconds can you use basic auth in the 1st attempt itself? This logic will be in your start() method.

0reactions
dapengzhang0commented, Jul 13, 2019

Hi @dshylov , I’m providing an interceptor that buffers the message and retries, you can add your logic in needToRetry(status, trailers). Hope this could solve your problem.

  class RetryAuthTokenInterceptor implements ClientInterceptor {

    @Override
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
        final MethodDescriptor<ReqT, RespT> method,
        final CallOptions callOptions,
        final Channel next) {

      class RetryingUnaryRequestClientCall<ReqT, RespT> extends ClientCall<ReqT, RespT> {

        Listener listener;
        Metadata metadata;
        ReqT msg;
        int req;
        ClientCall call;

        @Override
        public void start(Listener listener, Metadata metadata) {
          this.listener = listener;
          this.metadata = metadata;
        }

        @Override
        public void sendMessage(ReqT msg) {
          assert this.msg == null;
          this.msg = msg;
        }

        @Override
        public void request(int num) {
          req += num;
          assert this.msg == null;
        }

        @Override
        public boolean isReady() {
          return false;
        }

        @Override
        public void halfClose() {
          startCall(new CheckingListener());
        }

        private void startCall(Listener listener) {
          call = next.newCall(method, callOptions);
          Metadata headers = new Metadata();
          headers.merge(metadata);
          call.start(listener, headers);
          assert this.msg != null;
          call.request(req);
          call.sendMessage(msg);
          call.halfClose();
        }

        @Override
        public void cancel(String s, Throwable t) {
          if (call != null) { // need synchronization
            call.cancel(s, t);
          }
          // technically should use CallOptions.getExecutor() if set
          listener.onClose(Status.CANCELLED.withDescription(s).withCause(t), new Metadata());
        }

        class CheckingListener extends ForwardingClientCallListener {

          Listener<RespT> delegate;

          @Override
          protected Listener delegate() {
            if (delegate == null) {
              throw new IllegalStateException();
            }
            return delegate;
          }

          @Override
          public void onHeaders(Metadata headers) {
            delegate = listener;
            super.onHeaders(headers);
          }

          @Override
          public void onClose(Status status, Metadata trailers) {
            if (delegate != null) {
              super.onClose(status, trailers);
              return;
            }
            if (!needToRetry(status, trailers)) { // YOUR CODE HERE
              delegate = listener;
              super.onClose(status, trailers);
              return;
            }
            startCall(listener); // Only retry once
            // startCall(new CheckingListener()); // to allow multiple retries
          }
        }

      }
      return new RetryingUnaryRequestClientCall<>();
    }
  }
Read more comments on GitHub >

github_iconTop Results From Across the Web

How To Write Retry Interceptor For Data Stream In OkHttp?
Call call = client.newCall(new Request.Builder().url(url).get().build()); Response response = call.execute(); // PROBLEM DOES NOT OCCUR THERE // ...
Read more >
Implementing auto retry in Java EE applications
Define a simple annotation which represents the 'retry policy metadata' e.g. number of retries · Define an interceptor with implementation to ...
Read more >
How to Customize Feign's Retry Mechanism - Medium
In this post I will talk about enabling retry mechanism for feign Client. By instinct what we can do is update our business...
Read more >
Using the Spring RestTemplate Interceptor - Baeldung
Retrying the requests with a configurable back off strategy; Request denial based on certain request parameters; Altering the request URL ...
Read more >
Retrying HTTP Requests a Fixed Number of Times With a ...
When the request fails, the handleRetryError() in the RetryService is triggered. We are passing the “delay between the retries” as an argument for...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found