• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 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.cronet;
18 
19 import static com.google.common.base.Preconditions.checkArgument;
20 import static com.google.common.base.Preconditions.checkNotNull;
21 import static io.grpc.internal.GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE;
22 
23 import com.google.common.annotations.VisibleForTesting;
24 import com.google.common.base.Preconditions;
25 import com.google.common.util.concurrent.MoreExecutors;
26 import io.grpc.Attributes;
27 import io.grpc.ExperimentalApi;
28 import io.grpc.NameResolver;
29 import io.grpc.internal.AbstractManagedChannelImplBuilder;
30 import io.grpc.internal.ClientTransportFactory;
31 import io.grpc.internal.ConnectionClientTransport;
32 import io.grpc.internal.GrpcUtil;
33 import io.grpc.internal.ProxyParameters;
34 import io.grpc.internal.SharedResourceHolder;
35 import io.grpc.internal.TransportTracer;
36 import java.net.InetSocketAddress;
37 import java.net.SocketAddress;
38 import java.util.concurrent.Executor;
39 import java.util.concurrent.ScheduledExecutorService;
40 import javax.annotation.Nullable;
41 import org.chromium.net.BidirectionalStream;
42 import org.chromium.net.CronetEngine;
43 import org.chromium.net.ExperimentalBidirectionalStream;
44 import org.chromium.net.ExperimentalCronetEngine;
45 
46 /** Convenience class for building channels with the cronet transport. */
47 @ExperimentalApi("There is no plan to make this API stable, given transport API instability")
48 public final class CronetChannelBuilder extends
49     AbstractManagedChannelImplBuilder<CronetChannelBuilder> {
50 
51   /** BidirectionalStream.Builder factory used for getting the gRPC BidirectionalStream. */
52   public static abstract class StreamBuilderFactory {
newBidirectionalStreamBuilder( String url, BidirectionalStream.Callback callback, Executor executor)53     public abstract BidirectionalStream.Builder newBidirectionalStreamBuilder(
54         String url, BidirectionalStream.Callback callback, Executor executor);
55   }
56 
57   /** Creates a new builder for the given server host, port and CronetEngine. */
forAddress(String host, int port, CronetEngine cronetEngine)58   public static CronetChannelBuilder forAddress(String host, int port, CronetEngine cronetEngine) {
59     Preconditions.checkNotNull(cronetEngine, "cronetEngine");
60     return new CronetChannelBuilder(host, port, cronetEngine);
61   }
62 
63   /**
64    * Always fails.  Call {@link #forAddress(String, int, CronetEngine)} instead.
65    */
forTarget(String target)66   public static CronetChannelBuilder forTarget(String target) {
67     throw new UnsupportedOperationException("call forAddress() instead");
68   }
69 
70   /**
71    * Always fails.  Call {@link #forAddress(String, int, CronetEngine)} instead.
72    */
forAddress(String name, int port)73   public static CronetChannelBuilder forAddress(String name, int port) {
74     throw new UnsupportedOperationException("call forAddress(String, int, CronetEngine) instead");
75   }
76 
77   @Nullable
78   private ScheduledExecutorService scheduledExecutorService;
79 
80   private final CronetEngine cronetEngine;
81 
82   private boolean alwaysUsePut = false;
83 
84   private int maxMessageSize = DEFAULT_MAX_MESSAGE_SIZE;
85 
86   private boolean trafficStatsTagSet;
87   private int trafficStatsTag;
88   private boolean trafficStatsUidSet;
89   private int trafficStatsUid;
90 
CronetChannelBuilder(String host, int port, CronetEngine cronetEngine)91   private CronetChannelBuilder(String host, int port, CronetEngine cronetEngine) {
92     super(
93         InetSocketAddress.createUnresolved(host, port),
94         GrpcUtil.authorityFromHostAndPort(host, port));
95     this.cronetEngine = Preconditions.checkNotNull(cronetEngine, "cronetEngine");
96   }
97 
98   /**
99    * Sets the maximum message size allowed to be received on the channel. If not called,
100    * defaults to {@link io.grpc.internal.GrpcUtil#DEFAULT_MAX_MESSAGE_SIZE}.
101    */
maxMessageSize(int maxMessageSize)102   public final CronetChannelBuilder maxMessageSize(int maxMessageSize) {
103     checkArgument(maxMessageSize >= 0, "maxMessageSize must be >= 0");
104     this.maxMessageSize = maxMessageSize;
105     return this;
106   }
107 
108   /**
109    * Sets the Cronet channel to always use PUT instead of POST. Defaults to false.
110    */
alwaysUsePut(boolean enable)111   public final CronetChannelBuilder alwaysUsePut(boolean enable) {
112     this.alwaysUsePut = enable;
113     return this;
114   }
115 
116   /**
117    * Not supported for building cronet channel.
118    */
119   @Override
usePlaintext(boolean skipNegotiation)120   public final CronetChannelBuilder usePlaintext(boolean skipNegotiation) {
121     throw new IllegalArgumentException("Plaintext not currently supported");
122   }
123 
124   /**
125    * Sets {@link android.net.TrafficStats} tag to use when accounting socket traffic caused by this
126    * channel. See {@link android.net.TrafficStats} for more information. If no tag is set (e.g. this
127    * method isn't called), then Android accounts for the socket traffic caused by this channel as if
128    * the tag value were set to 0.
129    *
130    * <p><b>NOTE:</b>Setting a tag disallows sharing of sockets with channels with other tags, which
131    * may adversely effect performance by prohibiting connection sharing. In other words use of
132    * multiplexed sockets (e.g. HTTP/2 and QUIC) will only be allowed if all channels have the same
133    * socket tag.
134    *
135    * @param tag the tag value used to when accounting for socket traffic caused by this channel.
136    *     Tags between 0xFFFFFF00 and 0xFFFFFFFF are reserved and used internally by system services
137    *     like {@link android.app.DownloadManager} when performing traffic on behalf of an
138    *     application.
139    * @return the builder to facilitate chaining.
140    */
setTrafficStatsTag(int tag)141   public final CronetChannelBuilder setTrafficStatsTag(int tag) {
142     trafficStatsTagSet = true;
143     trafficStatsTag = tag;
144     return this;
145   }
146 
147   /**
148    * Sets specific UID to use when accounting socket traffic caused by this channel. See {@link
149    * android.net.TrafficStats} for more information. Designed for use when performing an operation
150    * on behalf of another application. Caller must hold {@link
151    * android.Manifest.permission#MODIFY_NETWORK_ACCOUNTING} permission. By default traffic is
152    * attributed to UID of caller.
153    *
154    * <p><b>NOTE:</b>Setting a UID disallows sharing of sockets with channels with other UIDs, which
155    * may adversely effect performance by prohibiting connection sharing. In other words use of
156    * multiplexed sockets (e.g. HTTP/2 and QUIC) will only be allowed if all channels have the same
157    * UID set.
158    *
159    * @param uid the UID to attribute socket traffic caused by this channel.
160    * @return the builder to facilitate chaining.
161    */
setTrafficStatsUid(int uid)162   public final CronetChannelBuilder setTrafficStatsUid(int uid) {
163     trafficStatsUidSet = true;
164     trafficStatsUid = uid;
165     return this;
166   }
167 
168   /**
169    * Provides a custom scheduled executor service.
170    *
171    * <p>It's an optional parameter. If the user has not provided a scheduled executor service when
172    * the channel is built, the builder will use a static cached thread pool.
173    *
174    * @return this
175    *
176    * @since 1.12.0
177    */
scheduledExecutorService( ScheduledExecutorService scheduledExecutorService)178   public final CronetChannelBuilder scheduledExecutorService(
179       ScheduledExecutorService scheduledExecutorService) {
180     this.scheduledExecutorService =
181         checkNotNull(scheduledExecutorService, "scheduledExecutorService");
182     return this;
183   }
184 
185   @Override
buildTransportFactory()186   protected final ClientTransportFactory buildTransportFactory() {
187     return new CronetTransportFactory(
188         new TaggingStreamFactory(
189             cronetEngine, trafficStatsTagSet, trafficStatsTag, trafficStatsUidSet, trafficStatsUid),
190         MoreExecutors.directExecutor(),
191         scheduledExecutorService,
192         maxMessageSize,
193         alwaysUsePut,
194         transportTracerFactory.create());
195   }
196 
197   @Override
getNameResolverParams()198   protected Attributes getNameResolverParams() {
199     return Attributes.newBuilder()
200         .set(NameResolver.Factory.PARAMS_DEFAULT_PORT, GrpcUtil.DEFAULT_PORT_SSL).build();
201   }
202 
203   @VisibleForTesting
204   static class CronetTransportFactory implements ClientTransportFactory {
205     private final ScheduledExecutorService timeoutService;
206     private final Executor executor;
207     private final int maxMessageSize;
208     private final boolean alwaysUsePut;
209     private final StreamBuilderFactory streamFactory;
210     private final TransportTracer transportTracer;
211     private final boolean usingSharedScheduler;
212 
CronetTransportFactory( StreamBuilderFactory streamFactory, Executor executor, @Nullable ScheduledExecutorService timeoutService, int maxMessageSize, boolean alwaysUsePut, TransportTracer transportTracer)213     private CronetTransportFactory(
214         StreamBuilderFactory streamFactory,
215         Executor executor,
216         @Nullable ScheduledExecutorService timeoutService,
217         int maxMessageSize,
218         boolean alwaysUsePut,
219         TransportTracer transportTracer) {
220       usingSharedScheduler = timeoutService == null;
221       this.timeoutService = usingSharedScheduler
222           ? SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE) : timeoutService;
223       this.maxMessageSize = maxMessageSize;
224       this.alwaysUsePut = alwaysUsePut;
225       this.streamFactory = streamFactory;
226       this.executor = Preconditions.checkNotNull(executor, "executor");
227       this.transportTracer = Preconditions.checkNotNull(transportTracer, "transportTracer");
228     }
229 
230     @Override
newClientTransport( SocketAddress addr, ClientTransportOptions options)231     public ConnectionClientTransport newClientTransport(
232         SocketAddress addr, ClientTransportOptions options) {
233       InetSocketAddress inetSocketAddr = (InetSocketAddress) addr;
234       return new CronetClientTransport(streamFactory, inetSocketAddr, options.getAuthority(),
235           options.getUserAgent(), executor, maxMessageSize, alwaysUsePut, transportTracer);
236     }
237 
238     @Override
getScheduledExecutorService()239     public ScheduledExecutorService getScheduledExecutorService() {
240       return timeoutService;
241     }
242 
243     @Override
close()244     public void close() {
245       if (usingSharedScheduler) {
246         SharedResourceHolder.release(GrpcUtil.TIMER_SERVICE, timeoutService);
247       }
248     }
249   }
250 
251   /**
252    * StreamBuilderFactory impl that applies TrafficStats tags to stream builders that are produced.
253    */
254   private static class TaggingStreamFactory extends StreamBuilderFactory {
255     private final CronetEngine cronetEngine;
256     private final boolean trafficStatsTagSet;
257     private final int trafficStatsTag;
258     private final boolean trafficStatsUidSet;
259     private final int trafficStatsUid;
260 
TaggingStreamFactory( CronetEngine cronetEngine, boolean trafficStatsTagSet, int trafficStatsTag, boolean trafficStatsUidSet, int trafficStatsUid)261     TaggingStreamFactory(
262         CronetEngine cronetEngine,
263         boolean trafficStatsTagSet,
264         int trafficStatsTag,
265         boolean trafficStatsUidSet,
266         int trafficStatsUid) {
267       this.cronetEngine = cronetEngine;
268       this.trafficStatsTagSet = trafficStatsTagSet;
269       this.trafficStatsTag = trafficStatsTag;
270       this.trafficStatsUidSet = trafficStatsUidSet;
271       this.trafficStatsUid = trafficStatsUid;
272     }
273 
274     @Override
newBidirectionalStreamBuilder( String url, BidirectionalStream.Callback callback, Executor executor)275     public BidirectionalStream.Builder newBidirectionalStreamBuilder(
276         String url, BidirectionalStream.Callback callback, Executor executor) {
277       ExperimentalBidirectionalStream.Builder builder =
278           ((ExperimentalCronetEngine) cronetEngine)
279               .newBidirectionalStreamBuilder(url, callback, executor);
280       if (trafficStatsTagSet) {
281         builder.setTrafficStatsTag(trafficStatsTag);
282       }
283       if (trafficStatsUidSet) {
284         builder.setTrafficStatsUid(trafficStatsUid);
285       }
286       return builder;
287     }
288   }
289 }
290