• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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