1 /* 2 * Copyright 2014 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.stub; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import com.google.errorprone.annotations.InlineMe; 22 import io.grpc.CallOptions; 23 import io.grpc.Channel; 24 import io.grpc.ClientCall; 25 import io.grpc.ClientInterceptor; 26 import io.grpc.ExperimentalApi; 27 import io.grpc.ForwardingClientCall.SimpleForwardingClientCall; 28 import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener; 29 import io.grpc.Metadata; 30 import io.grpc.MethodDescriptor; 31 import io.grpc.Status; 32 import java.util.concurrent.atomic.AtomicReference; 33 34 /** 35 * Utility functions for binding and receiving headers. 36 */ 37 public final class MetadataUtils { 38 // Prevent instantiation MetadataUtils()39 private MetadataUtils() {} 40 41 /** 42 * Attaches a set of request headers to a stub. 43 * 44 * @param stub to bind the headers to. 45 * @param extraHeaders the headers to be passed by each call on the returned stub. 46 * @return an implementation of the stub with {@code extraHeaders} bound to each call. 47 * @deprecated Use {@code stub.withInterceptors(newAttachHeadersInterceptor(...))} instead. 48 */ 49 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1789") 50 @Deprecated 51 @InlineMe( 52 replacement = 53 "stub.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(extraHeaders))", 54 imports = "io.grpc.stub.MetadataUtils") attachHeaders(T stub, Metadata extraHeaders)55 public static <T extends AbstractStub<T>> T attachHeaders(T stub, Metadata extraHeaders) { 56 return stub.withInterceptors(newAttachHeadersInterceptor(extraHeaders)); 57 } 58 59 /** 60 * Returns a client interceptor that attaches a set of headers to requests. 61 * 62 * @param extraHeaders the headers to be passed by each call that is processed by the returned 63 * interceptor 64 */ newAttachHeadersInterceptor(Metadata extraHeaders)65 public static ClientInterceptor newAttachHeadersInterceptor(Metadata extraHeaders) { 66 return new HeaderAttachingClientInterceptor(extraHeaders); 67 } 68 69 private static final class HeaderAttachingClientInterceptor implements ClientInterceptor { 70 71 private final Metadata extraHeaders; 72 73 // Non private to avoid synthetic class HeaderAttachingClientInterceptor(Metadata extraHeaders)74 HeaderAttachingClientInterceptor(Metadata extraHeaders) { 75 this.extraHeaders = checkNotNull(extraHeaders, "extraHeaders"); 76 } 77 78 @Override interceptCall( MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next)79 public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall( 80 MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) { 81 return new HeaderAttachingClientCall<>(next.newCall(method, callOptions)); 82 } 83 84 private final class HeaderAttachingClientCall<ReqT, RespT> 85 extends SimpleForwardingClientCall<ReqT, RespT> { 86 87 // Non private to avoid synthetic class HeaderAttachingClientCall(ClientCall<ReqT, RespT> call)88 HeaderAttachingClientCall(ClientCall<ReqT, RespT> call) { 89 super(call); 90 } 91 92 @Override start(Listener<RespT> responseListener, Metadata headers)93 public void start(Listener<RespT> responseListener, Metadata headers) { 94 headers.merge(extraHeaders); 95 super.start(responseListener, headers); 96 } 97 } 98 } 99 100 /** 101 * Captures the last received metadata for a stub. Useful for testing 102 * 103 * @param stub to capture for 104 * @param headersCapture to record the last received headers 105 * @param trailersCapture to record the last received trailers 106 * @return an implementation of the stub that allows to access the last received call's 107 * headers and trailers via {@code headersCapture} and {@code trailersCapture}. 108 * @deprecated Use {@code stub.withInterceptors(newCaptureMetadataInterceptor())} instead. 109 */ 110 @ExperimentalApi("https://github.com/grpc/grpc-java/issues/1789") 111 @Deprecated 112 @InlineMe( 113 replacement = 114 "stub.withInterceptors(MetadataUtils.newCaptureMetadataInterceptor(headersCapture," 115 + " trailersCapture))", 116 imports = "io.grpc.stub.MetadataUtils") captureMetadata( T stub, AtomicReference<Metadata> headersCapture, AtomicReference<Metadata> trailersCapture)117 public static <T extends AbstractStub<T>> T captureMetadata( 118 T stub, 119 AtomicReference<Metadata> headersCapture, 120 AtomicReference<Metadata> trailersCapture) { 121 return stub.withInterceptors( 122 newCaptureMetadataInterceptor(headersCapture, trailersCapture)); 123 } 124 125 /** 126 * Captures the last received metadata on a channel. Useful for testing. 127 * 128 * @param headersCapture to record the last received headers 129 * @param trailersCapture to record the last received trailers 130 * @return an implementation of the channel with captures installed. 131 */ newCaptureMetadataInterceptor( AtomicReference<Metadata> headersCapture, AtomicReference<Metadata> trailersCapture)132 public static ClientInterceptor newCaptureMetadataInterceptor( 133 AtomicReference<Metadata> headersCapture, AtomicReference<Metadata> trailersCapture) { 134 return new MetadataCapturingClientInterceptor(headersCapture, trailersCapture); 135 } 136 137 private static final class MetadataCapturingClientInterceptor implements ClientInterceptor { 138 139 final AtomicReference<Metadata> headersCapture; 140 final AtomicReference<Metadata> trailersCapture; 141 142 // Non private to avoid synthetic class MetadataCapturingClientInterceptor( AtomicReference<Metadata> headersCapture, AtomicReference<Metadata> trailersCapture)143 MetadataCapturingClientInterceptor( 144 AtomicReference<Metadata> headersCapture, AtomicReference<Metadata> trailersCapture) { 145 this.headersCapture = checkNotNull(headersCapture, "headersCapture"); 146 this.trailersCapture = checkNotNull(trailersCapture, "trailersCapture"); 147 } 148 149 @Override interceptCall( MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next)150 public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall( 151 MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) { 152 return new MetadataCapturingClientCall<>(next.newCall(method, callOptions)); 153 } 154 155 private final class MetadataCapturingClientCall<ReqT, RespT> 156 extends SimpleForwardingClientCall<ReqT, RespT> { 157 158 // Non private to avoid synthetic class MetadataCapturingClientCall(ClientCall<ReqT, RespT> call)159 MetadataCapturingClientCall(ClientCall<ReqT, RespT> call) { 160 super(call); 161 } 162 163 @Override start(ClientCall.Listener<RespT> responseListener, Metadata headers)164 public void start(ClientCall.Listener<RespT> responseListener, Metadata headers) { 165 headersCapture.set(null); 166 trailersCapture.set(null); 167 super.start(new MetadataCapturingClientCallListener(responseListener), headers); 168 } 169 170 private final class MetadataCapturingClientCallListener 171 extends SimpleForwardingClientCallListener<RespT> { 172 MetadataCapturingClientCallListener(ClientCall.Listener<RespT> responseListener)173 MetadataCapturingClientCallListener(ClientCall.Listener<RespT> responseListener) { 174 super(responseListener); 175 } 176 177 @Override onHeaders(Metadata headers)178 public void onHeaders(Metadata headers) { 179 headersCapture.set(headers); 180 super.onHeaders(headers); 181 } 182 183 @Override onClose(Status status, Metadata trailers)184 public void onClose(Status status, Metadata trailers) { 185 trailersCapture.set(trailers); 186 super.onClose(status, trailers); 187 } 188 } 189 } 190 } 191 } 192