1 /* 2 * Copyright 2020 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc; 18 19 import com.google.common.base.Preconditions; 20 import java.util.concurrent.Executor; 21 22 /** 23 * Uses multiple {@code CallCredentials} as if they were one. If the first credential fails, the 24 * second will not be used. Both must succeed to allow the RPC. 25 */ 26 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1914") 27 public final class CompositeCallCredentials extends CallCredentials { 28 private final CallCredentials credentials1; 29 private final CallCredentials credentials2; 30 CompositeCallCredentials(CallCredentials creds1, CallCredentials creds2)31 public CompositeCallCredentials(CallCredentials creds1, CallCredentials creds2) { 32 this.credentials1 = Preconditions.checkNotNull(creds1, "creds1"); 33 this.credentials2 = Preconditions.checkNotNull(creds2, "creds2"); 34 } 35 36 @Override applyRequestMetadata( RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier)37 public void applyRequestMetadata( 38 RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) { 39 credentials1.applyRequestMetadata(requestInfo, appExecutor, 40 new WrappingMetadataApplier(requestInfo, appExecutor, applier, Context.current())); 41 } 42 43 private final class WrappingMetadataApplier extends MetadataApplier { 44 private final RequestInfo requestInfo; 45 private final Executor appExecutor; 46 private final MetadataApplier delegate; 47 private final Context context; 48 WrappingMetadataApplier( RequestInfo requestInfo, Executor appExecutor, MetadataApplier delegate, Context context)49 public WrappingMetadataApplier( 50 RequestInfo requestInfo, Executor appExecutor, MetadataApplier delegate, Context context) { 51 this.requestInfo = requestInfo; 52 this.appExecutor = appExecutor; 53 this.delegate = Preconditions.checkNotNull(delegate, "delegate"); 54 this.context = Preconditions.checkNotNull(context, "context"); 55 } 56 57 @Override apply(Metadata headers)58 public void apply(Metadata headers) { 59 Preconditions.checkNotNull(headers, "headers"); 60 Context previous = context.attach(); 61 try { 62 credentials2.applyRequestMetadata( 63 requestInfo, appExecutor, new CombiningMetadataApplier(delegate, headers)); 64 } finally { 65 context.detach(previous); 66 } 67 } 68 69 @Override fail(Status status)70 public void fail(Status status) { 71 delegate.fail(status); 72 } 73 } 74 75 private static final class CombiningMetadataApplier extends MetadataApplier { 76 private final MetadataApplier delegate; 77 private final Metadata firstHeaders; 78 CombiningMetadataApplier(MetadataApplier delegate, Metadata firstHeaders)79 public CombiningMetadataApplier(MetadataApplier delegate, Metadata firstHeaders) { 80 this.delegate = delegate; 81 this.firstHeaders = firstHeaders; 82 } 83 84 @Override apply(Metadata headers)85 public void apply(Metadata headers) { 86 Preconditions.checkNotNull(headers, "headers"); 87 Metadata combined = new Metadata(); 88 combined.merge(firstHeaders); 89 combined.merge(headers); 90 delegate.apply(combined); 91 } 92 93 @Override fail(Status status)94 public void fail(Status status) { 95 delegate.fail(status); 96 } 97 } 98 } 99