• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #20320

17 Jun 2026 05:11PM UTC coverage: 88.886% (+0.01%) from 88.874%
#20320

push

github

web-flow
api: Add Grpc.newChannelBuilder accepting NameResolverRegistry (#11901)

This introduces a new `Grpc.newChannelBuilder` overload that allows callers to pass an explicit `NameResolverRegistry`. Fixes #11055

36550 of 41120 relevant lines covered (88.89%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.28
/../netty/src/main/java/io/grpc/netty/NettyChannelBuilder.java
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.netty;
18

19
import static com.google.common.base.Preconditions.checkArgument;
20
import static com.google.common.base.Preconditions.checkNotNull;
21
import static com.google.common.base.Preconditions.checkState;
22
import static io.grpc.internal.GrpcUtil.DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
23
import static io.grpc.internal.GrpcUtil.KEEPALIVE_TIME_NANOS_DISABLED;
24

25
import com.google.common.annotations.VisibleForTesting;
26
import com.google.common.base.Optional;
27
import com.google.common.base.Ticker;
28
import com.google.errorprone.annotations.CanIgnoreReturnValue;
29
import com.google.errorprone.annotations.CheckReturnValue;
30
import com.google.errorprone.annotations.InlineMe;
31
import io.grpc.Attributes;
32
import io.grpc.CallCredentials;
33
import io.grpc.ChannelCredentials;
34
import io.grpc.ChannelLogger;
35
import io.grpc.EquivalentAddressGroup;
36
import io.grpc.ExperimentalApi;
37
import io.grpc.ForwardingChannelBuilder2;
38
import io.grpc.HttpConnectProxiedSocketAddress;
39
import io.grpc.Internal;
40
import io.grpc.ManagedChannelBuilder;
41
import io.grpc.NameResolverProvider;
42
import io.grpc.NameResolverRegistry;
43
import io.grpc.internal.AtomicBackoff;
44
import io.grpc.internal.ClientTransportFactory;
45
import io.grpc.internal.ConnectionClientTransport;
46
import io.grpc.internal.FixedObjectPool;
47
import io.grpc.internal.GrpcUtil;
48
import io.grpc.internal.KeepAliveManager;
49
import io.grpc.internal.ManagedChannelImplBuilder;
50
import io.grpc.internal.ManagedChannelImplBuilder.ChannelBuilderDefaultPortProvider;
51
import io.grpc.internal.ManagedChannelImplBuilder.ClientTransportFactoryBuilder;
52
import io.grpc.internal.ObjectPool;
53
import io.grpc.internal.SharedResourcePool;
54
import io.grpc.internal.TransportTracer;
55
import io.grpc.netty.ProtocolNegotiators.FromChannelCredentialsResult;
56
import io.netty.channel.Channel;
57
import io.netty.channel.ChannelFactory;
58
import io.netty.channel.ChannelOption;
59
import io.netty.channel.EventLoopGroup;
60
import io.netty.channel.ReflectiveChannelFactory;
61
import io.netty.channel.socket.nio.NioSocketChannel;
62
import io.netty.handler.ssl.SslContext;
63
import java.net.InetSocketAddress;
64
import java.net.SocketAddress;
65
import java.util.Collection;
66
import java.util.Collections;
67
import java.util.HashMap;
68
import java.util.Map;
69
import java.util.concurrent.Executor;
70
import java.util.concurrent.ScheduledExecutorService;
71
import java.util.concurrent.TimeUnit;
72
import javax.annotation.Nullable;
73
import javax.net.ssl.SSLException;
74

75
/**
76
 * A builder to help simplify construction of channels using the Netty transport.
77
 */
78
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1784")
79
@CheckReturnValue
80
public final class NettyChannelBuilder extends ForwardingChannelBuilder2<NettyChannelBuilder> {
81

82
  // 1MiB.
83
  public static final int DEFAULT_FLOW_CONTROL_WINDOW = 1024 * 1024;
84
  private static final boolean DEFAULT_AUTO_FLOW_CONTROL;
85

86
  private static final long AS_LARGE_AS_INFINITE = TimeUnit.DAYS.toNanos(1000L);
1✔
87

88
  private static final ChannelFactory<? extends Channel> DEFAULT_CHANNEL_FACTORY =
1✔
89
      new ReflectiveChannelFactory<>(Utils.DEFAULT_CLIENT_CHANNEL_TYPE);
90
  private static final ObjectPool<? extends EventLoopGroup> DEFAULT_EVENT_LOOP_GROUP_POOL =
1✔
91
      SharedResourcePool.forResource(Utils.DEFAULT_WORKER_EVENT_LOOP_GROUP);
1✔
92

93
  static {
94
    String autoFlowControl = System.getenv("GRPC_EXPERIMENTAL_AUTOFLOWCONTROL");
1✔
95
    if (autoFlowControl == null) {
1✔
96
      autoFlowControl = "true";
1✔
97
    }
98
    DEFAULT_AUTO_FLOW_CONTROL = Boolean.parseBoolean(autoFlowControl);
1✔
99
  }
1✔
100

101
  private final ManagedChannelImplBuilder managedChannelImplBuilder;
102
  private TransportTracer.Factory transportTracerFactory = TransportTracer.getDefaultFactory();
1✔
103
  private final Map<ChannelOption<?>, Object> channelOptions = new HashMap<>();
1✔
104
  private ChannelFactory<? extends Channel> channelFactory = DEFAULT_CHANNEL_FACTORY;
1✔
105
  private ObjectPool<? extends EventLoopGroup> eventLoopGroupPool = DEFAULT_EVENT_LOOP_GROUP_POOL;
1✔
106
  private boolean autoFlowControl = DEFAULT_AUTO_FLOW_CONTROL;
1✔
107
  private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW;
1✔
108
  private int maxHeaderListSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE;
1✔
109
  private int softLimitHeaderListSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE;
1✔
110
  private int maxInboundMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE;
1✔
111
  private long keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
1✔
112
  private long keepAliveTimeoutNanos = DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
1✔
113
  private boolean keepAliveWithoutCalls;
114
  private ProtocolNegotiator.ClientFactory protocolNegotiatorFactory
1✔
115
      = new DefaultProtocolNegotiator();
116
  private final boolean freezeProtocolNegotiatorFactory;
117
  private LocalSocketPicker localSocketPicker;
118

119
  /**
120
   * If true, indicates that the transport may use the GET method for RPCs, and may include the
121
   * request body in the query params.
122
   */
123
  private final boolean useGetForSafeMethods = false;
1✔
124

125
  private Class<? extends SocketAddress> transportSocketType = InetSocketAddress.class;
1✔
126

127
  /**
128
   * Creates a new builder with the given server address. This factory method is primarily intended
129
   * for using Netty Channel types other than SocketChannel. {@link #forAddress(String, int)} should
130
   * generally be preferred over this method, since that API permits delaying DNS lookups and
131
   * noticing changes to DNS. If an unresolved InetSocketAddress is passed in, then it will remain
132
   * unresolved.
133
   */
134
  public static NettyChannelBuilder forAddress(SocketAddress serverAddress) {
135
    return new NettyChannelBuilder(serverAddress);
1✔
136
  }
137

138
  /**
139
   * Creates a new builder with the given server address. This factory method is primarily intended
140
   * for using Netty Channel types other than SocketChannel.
141
   * {@link #forAddress(String, int, ChannelCredentials)} should generally be preferred over this
142
   * method, since that API permits delaying DNS lookups and noticing changes to DNS. If an
143
   * unresolved InetSocketAddress is passed in, then it will remain unresolved.
144
   */
145
  public static NettyChannelBuilder forAddress(SocketAddress serverAddress,
146
      ChannelCredentials creds) {
147
    FromChannelCredentialsResult result = ProtocolNegotiators.from(creds);
1✔
148
    if (result.error != null) {
1✔
149
      throw new IllegalArgumentException(result.error);
×
150
    }
151
    return new NettyChannelBuilder(serverAddress, creds, result.callCredentials, result.negotiator);
1✔
152
  }
153

154
  /**
155
   * Creates a new builder with the given host and port.
156
   */
157
  public static NettyChannelBuilder forAddress(String host, int port) {
158
    return forTarget(GrpcUtil.authorityFromHostAndPort(host, port));
1✔
159
  }
160

161
  /**
162
   * Creates a new builder with the given host and port.
163
   */
164
  public static NettyChannelBuilder forAddress(String host, int port, ChannelCredentials creds) {
165
    return forTarget(GrpcUtil.authorityFromHostAndPort(host, port), creds);
1✔
166
  }
167

168
  /**
169
   * Creates a new builder with the given target string that will be resolved by
170
   * {@link io.grpc.NameResolver}.
171
   */
172
  public static NettyChannelBuilder forTarget(String target) {
173
    return new NettyChannelBuilder(target);
1✔
174
  }
175

176
  /**
177
   * Creates a new builder with the given target string that will be resolved by
178
   * {@link io.grpc.NameResolver}.
179
   */
180
  public static NettyChannelBuilder forTarget(String target, ChannelCredentials creds) {
181
    FromChannelCredentialsResult result = ProtocolNegotiators.from(creds);
1✔
182
    if (result.error != null) {
1✔
183
      throw new IllegalArgumentException(result.error);
×
184
    }
185
    return new NettyChannelBuilder(target, creds, result.callCredentials, result.negotiator);
1✔
186
  }
187

188
  private final class NettyChannelTransportFactoryBuilder implements ClientTransportFactoryBuilder {
1✔
189
    @Override
190
    public ClientTransportFactory buildClientTransportFactory() {
191
      return buildTransportFactory();
1✔
192
    }
193
  }
194

195
  private final class NettyChannelDefaultPortProvider implements ChannelBuilderDefaultPortProvider {
1✔
196
    @Override
197
    public int getDefaultPort() {
198
      return protocolNegotiatorFactory.getDefaultPort();
1✔
199
    }
200
  }
201

202
  NettyChannelBuilder(String target) {
1✔
203
    managedChannelImplBuilder = new ManagedChannelImplBuilder(target,
1✔
204
        new NettyChannelTransportFactoryBuilder(),
205
        new NettyChannelDefaultPortProvider());
206
    this.freezeProtocolNegotiatorFactory = false;
1✔
207
  }
1✔
208

209
  NettyChannelBuilder(
210
      String target, ChannelCredentials channelCreds, CallCredentials callCreds,
211
      ProtocolNegotiator.ClientFactory negotiator) {
212
    this(target, channelCreds, callCreds, negotiator, null, null);
1✔
213
  }
1✔
214

215
  NettyChannelBuilder(
216
      String target, ChannelCredentials channelCreds, CallCredentials callCreds,
217
      ProtocolNegotiator.ClientFactory negotiator,
218
      NameResolverRegistry nameResolverRegistry,
219
      NameResolverProvider nameResolverProvider) {
1✔
220
    managedChannelImplBuilder = new ManagedChannelImplBuilder(
1✔
221
        target, channelCreds, callCreds,
222
        new NettyChannelTransportFactoryBuilder(),
223
        new NettyChannelDefaultPortProvider(),
224
        nameResolverRegistry,
225
        nameResolverProvider);
226
    this.protocolNegotiatorFactory = checkNotNull(negotiator, "negotiator");
1✔
227
    this.freezeProtocolNegotiatorFactory = true;
1✔
228
  }
1✔
229

230
  NettyChannelBuilder(SocketAddress address) {
1✔
231
    managedChannelImplBuilder = new ManagedChannelImplBuilder(address,
1✔
232
        getAuthorityFromAddress(address),
1✔
233
        new NettyChannelTransportFactoryBuilder(),
234
        new NettyChannelDefaultPortProvider());
235
    this.freezeProtocolNegotiatorFactory = false;
1✔
236
  }
1✔
237

238
  NettyChannelBuilder(
239
      SocketAddress address, ChannelCredentials channelCreds, CallCredentials callCreds,
240
      ProtocolNegotiator.ClientFactory negotiator) {
1✔
241
    managedChannelImplBuilder = new ManagedChannelImplBuilder(address,
1✔
242
        getAuthorityFromAddress(address),
1✔
243
        channelCreds, callCreds,
244
        new NettyChannelTransportFactoryBuilder(),
245
        new NettyChannelDefaultPortProvider());
246
    this.protocolNegotiatorFactory = checkNotNull(negotiator, "negotiator");
1✔
247
    this.freezeProtocolNegotiatorFactory = true;
1✔
248
  }
1✔
249

250
  @Internal
251
  @Override
252
  protected ManagedChannelBuilder<?> delegate() {
253
    return managedChannelImplBuilder;
1✔
254
  }
255

256
  private static String getAuthorityFromAddress(SocketAddress address) {
257
    if (address instanceof InetSocketAddress) {
1✔
258
      InetSocketAddress inetAddress = (InetSocketAddress) address;
1✔
259
      return GrpcUtil.authorityFromHostAndPort(inetAddress.getHostString(), inetAddress.getPort());
1✔
260
    } else {
261
      return address.toString();
1✔
262
    }
263
  }
264

265
  /**
266
   * Specifies the channel type to use, by default we use {@code EpollSocketChannel} if available,
267
   * otherwise using {@link NioSocketChannel}.
268
   *
269
   * <p>You either use this or {@link #channelFactory(io.netty.channel.ChannelFactory)} if your
270
   * {@link Channel} implementation has no no-args constructor.
271
   *
272
   * <p>It's an optional parameter. If the user has not provided an Channel type or ChannelFactory
273
   * when the channel is built, the builder will use the default one which is static.
274
   *
275
   * <p>You must also provide corresponding {@link #eventLoopGroup(EventLoopGroup)}. For example,
276
   * {@link NioSocketChannel} must use {@link io.netty.channel.nio.NioEventLoopGroup}, otherwise
277
   * your application won't start.
278
   */
279
  @CanIgnoreReturnValue
280
  public NettyChannelBuilder channelType(Class<? extends Channel> channelType) {
281
    return channelType(channelType, null);
×
282
  }
283

284
  /**
285
   * Similar to {@link #channelType(Class)} above but allows the
286
   * caller to specify the socket-type associated with the channelType.
287
   *
288
   * @param channelType the type of {@link Channel} to use.
289
   * @param transportSocketType the associated {@link SocketAddress} type. If {@code null}, then
290
   *     no compatibility check is performed between channel transport and name-resolver addresses.
291
   */
292
  @CanIgnoreReturnValue
293
  public NettyChannelBuilder channelType(Class<? extends Channel> channelType,
294
      @Nullable Class<? extends SocketAddress> transportSocketType) {
295
    checkNotNull(channelType, "channelType");
1✔
296
    return channelFactory(new ReflectiveChannelFactory<>(channelType),
1✔
297
        transportSocketType);
298
  }
299

300
  /**
301
   * Specifies the {@link ChannelFactory} to create {@link Channel} instances. This method is
302
   * usually only used if the specific {@code Channel} requires complex logic which requires
303
   * additional information to create the {@code Channel}. Otherwise, recommend to use {@link
304
   * #channelType(Class)}.
305
   *
306
   * <p>It's an optional parameter. If the user has not provided an Channel type or ChannelFactory
307
   * when the channel is built, the builder will use the default one which is static.
308
   *
309
   * <p>You must also provide corresponding {@link #eventLoopGroup(EventLoopGroup)}. For example,
310
   * {@link NioSocketChannel} based {@link ChannelFactory} must use {@link
311
   * io.netty.channel.nio.NioEventLoopGroup}, otherwise your application won't start.
312
   */
313
  @CanIgnoreReturnValue
314
  public NettyChannelBuilder channelFactory(ChannelFactory<? extends Channel> channelFactory) {
315
    return channelFactory(channelFactory, null);
1✔
316
  }
317

318
  /**
319
   * Similar to {@link #channelFactory(ChannelFactory)} above but allows the
320
   * caller to specify the socket-type associated with the channelFactory.
321
   *
322
   * @param channelFactory the {@link ChannelFactory} to use.
323
   * @param transportSocketType the associated {@link SocketAddress} type. If {@code null}, then
324
   *     no compatibility check is performed between channel transport and name-resolver addresses.
325
   */
326
  @CanIgnoreReturnValue
327
  public NettyChannelBuilder channelFactory(ChannelFactory<? extends Channel> channelFactory,
328
      @Nullable Class<? extends SocketAddress> transportSocketType) {
329
    this.channelFactory = checkNotNull(channelFactory, "channelFactory");
1✔
330
    this.transportSocketType = transportSocketType;
1✔
331
    return this;
1✔
332
  }
333

334
  /**
335
   * Specifies a channel option. As the underlying channel as well as network implementation may
336
   * ignore this value applications should consider it a hint.
337
   */
338
  @CanIgnoreReturnValue
339
  public <T> NettyChannelBuilder withOption(ChannelOption<T> option, T value) {
340
    channelOptions.put(option, value);
×
341
    return this;
×
342
  }
343

344
  /**
345
   * Sets the negotiation type for the HTTP/2 connection.
346
   *
347
   * <p>Default: <code>TLS</code>
348
   */
349
  @CanIgnoreReturnValue
350
  public NettyChannelBuilder negotiationType(NegotiationType type) {
351
    checkState(!freezeProtocolNegotiatorFactory,
1✔
352
               "Cannot change security when using ChannelCredentials");
353
    if (!(protocolNegotiatorFactory instanceof DefaultProtocolNegotiator)) {
1✔
354
      // Do nothing for compatibility
355
      return this;
×
356
    }
357
    ((DefaultProtocolNegotiator) protocolNegotiatorFactory).negotiationType = type;
1✔
358
    return this;
1✔
359
  }
360

361
  /**
362
   * Provides an EventGroupLoop to be used by the netty transport.
363
   *
364
   * <p>It's an optional parameter. If the user has not provided an EventGroupLoop when the channel
365
   * is built, the builder will use the default one which is static.
366
   *
367
   * <p>You must also provide corresponding {@link #channelType(Class)} or {@link
368
   * #channelFactory(ChannelFactory)} corresponding to the given {@code EventLoopGroup}. For
369
   * example, {@link io.netty.channel.nio.NioEventLoopGroup} requires {@link NioSocketChannel}
370
   *
371
   * <p>The channel won't take ownership of the given EventLoopGroup. It's caller's responsibility
372
   * to shut it down when it's desired.
373
   */
374
  @CanIgnoreReturnValue
375
  public NettyChannelBuilder eventLoopGroup(@Nullable EventLoopGroup eventLoopGroup) {
376
    if (eventLoopGroup != null) {
1✔
377
      return eventLoopGroupPool(new FixedObjectPool<>(eventLoopGroup));
1✔
378
    }
379
    return eventLoopGroupPool(DEFAULT_EVENT_LOOP_GROUP_POOL);
×
380
  }
381

382
  @CanIgnoreReturnValue
383
  NettyChannelBuilder eventLoopGroupPool(ObjectPool<? extends EventLoopGroup> eventLoopGroupPool) {
384
    this.eventLoopGroupPool = checkNotNull(eventLoopGroupPool, "eventLoopGroupPool");
1✔
385
    return this;
1✔
386
  }
387

388
  /**
389
   * SSL/TLS context to use instead of the system default. It must have been configured with {@link
390
   * GrpcSslContexts}, but options could have been overridden.
391
   */
392
  @CanIgnoreReturnValue
393
  public NettyChannelBuilder sslContext(SslContext sslContext) {
394
    checkState(!freezeProtocolNegotiatorFactory,
1✔
395
               "Cannot change security when using ChannelCredentials");
396
    if (sslContext != null) {
1✔
397
      checkArgument(sslContext.isClient(),
1✔
398
          "Server SSL context can not be used for client channel");
399
      GrpcSslContexts.ensureAlpnAndH2Enabled(sslContext.applicationProtocolNegotiator());
1✔
400
    }
401
    if (!(protocolNegotiatorFactory instanceof DefaultProtocolNegotiator)) {
1✔
402
      // Do nothing for compatibility
403
      return this;
×
404
    }
405
    ((DefaultProtocolNegotiator) protocolNegotiatorFactory).sslContext = sslContext;
1✔
406
    return this;
1✔
407
  }
408

409
  /**
410
   * Sets the initial flow control window in bytes. Setting initial flow control window enables auto
411
   * flow control tuning using bandwidth-delay product algorithm. To disable auto flow control
412
   * tuning, use {@link #flowControlWindow(int)}. By default, auto flow control is enabled with
413
   * initial flow control window size of {@link #DEFAULT_FLOW_CONTROL_WINDOW}.
414
   */
415
  @CanIgnoreReturnValue
416
  public NettyChannelBuilder initialFlowControlWindow(int initialFlowControlWindow) {
417
    checkArgument(initialFlowControlWindow > 0, "initialFlowControlWindow must be positive");
1✔
418
    this.flowControlWindow = initialFlowControlWindow;
1✔
419
    this.autoFlowControl = true;
1✔
420
    return this;
1✔
421
  }
422

423
  /**
424
   * Sets the flow control window in bytes. Setting flowControlWindow disables auto flow control
425
   * tuning; use {@link #initialFlowControlWindow(int)} to enable auto flow control tuning. If not
426
   * called, the default value is {@link #DEFAULT_FLOW_CONTROL_WINDOW}) with auto flow control
427
   * tuning.
428
   */
429
  @CanIgnoreReturnValue
430
  public NettyChannelBuilder flowControlWindow(int flowControlWindow) {
431
    checkArgument(flowControlWindow > 0, "flowControlWindow must be positive");
1✔
432
    this.flowControlWindow = flowControlWindow;
1✔
433
    this.autoFlowControl = false;
1✔
434
    return this;
1✔
435
  }
436

437
  /**
438
   * Sets the maximum size of header list allowed to be received. This is cumulative size of the
439
   * headers with some overhead, as defined for
440
   * <a href="http://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2">
441
   * HTTP/2's SETTINGS_MAX_HEADER_LIST_SIZE</a>. The default is 8 KiB.
442
   *
443
   * @deprecated Use {@link #maxInboundMetadataSize} instead
444
   */
445
  @CanIgnoreReturnValue
446
  @Deprecated
447
  @InlineMe(replacement = "this.maxInboundMetadataSize(maxHeaderListSize)")
448
  public NettyChannelBuilder maxHeaderListSize(int maxHeaderListSize) {
449
    return maxInboundMetadataSize(maxHeaderListSize);
×
450
  }
451

452
  /**
453
   * Sets the maximum size of metadata allowed to be received. This is cumulative size of the
454
   * entries with some overhead, as defined for
455
   * <a href="http://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2">
456
   * HTTP/2's SETTINGS_MAX_HEADER_LIST_SIZE</a>. The default is 8 KiB.
457
   *
458
   * @param bytes the maximum size of received metadata
459
   * @return this
460
   * @throws IllegalArgumentException if bytes is non-positive
461
   * @since 1.17.0
462
   */
463
  @CanIgnoreReturnValue
464
  @Override
465
  public NettyChannelBuilder maxInboundMetadataSize(int bytes) {
466
    checkArgument(bytes > 0, "maxInboundMetadataSize must be > 0");
×
467
    this.maxHeaderListSize = bytes;
×
468
    // Clear the soft limit setting, by setting soft limit to maxInboundMetadataSize. The
469
    // maxInboundMetadataSize will take precedence be applied before soft limit check.
470
    this.softLimitHeaderListSize = bytes;
×
471
    return this;
×
472
  }
473

474
  /**
475
   * Sets the size of metadata that clients are advised to not exceed. When a metadata with size
476
   * larger than the soft limit is encountered there will be a probability the RPC will fail. The
477
   * chance of failing increases as the metadata size approaches the hard limit.
478
   * {@code Integer.MAX_VALUE} disables the enforcement. The default is implementation-dependent,
479
   * but is not generally less than 8 KiB and may be unlimited.
480
   *
481
   * <p>This is cumulative size of the metadata. The precise calculation is
482
   * implementation-dependent, but implementations are encouraged to follow the calculation used
483
   * for
484
   * <a href="http://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2">HTTP/2's
485
   * SETTINGS_MAX_HEADER_LIST_SIZE</a>. It sums the bytes from each entry's key and value, plus 32
486
   * bytes of overhead per entry.
487
   *
488
   * @param soft the soft size limit of received metadata
489
   * @param max the hard size limit of received metadata
490
   * @return this
491
   * @throws IllegalArgumentException if soft and/or max is non-positive, or max smaller than
492
   *     soft
493
   * @since 1.68.0
494
   */
495
  @CanIgnoreReturnValue
496
  public NettyChannelBuilder maxInboundMetadataSize(int soft, int max) {
497
    checkArgument(soft > 0, "softLimitHeaderListSize must be > 0");
×
498
    checkArgument(max > soft,
×
499
        "maxInboundMetadataSize must be greater than softLimitHeaderListSize");
500
    this.softLimitHeaderListSize = soft;
×
501
    this.maxHeaderListSize = max;
×
502
    return this;
×
503
  }
504

505
  /**
506
   * Equivalent to using {@link #negotiationType(NegotiationType)} with {@code PLAINTEXT}.
507
   */
508
  @CanIgnoreReturnValue
509
  @Override
510
  public NettyChannelBuilder usePlaintext() {
511
    negotiationType(NegotiationType.PLAINTEXT);
1✔
512
    return this;
1✔
513
  }
514

515
  /**
516
   * Equivalent to using {@link #negotiationType(NegotiationType)} with {@code TLS}.
517
   */
518
  @CanIgnoreReturnValue
519
  @Override
520
  public NettyChannelBuilder useTransportSecurity() {
521
    negotiationType(NegotiationType.TLS);
×
522
    return this;
×
523
  }
524

525
  /**
526
   * {@inheritDoc}
527
   *
528
   * @since 1.3.0
529
   */
530
  @CanIgnoreReturnValue
531
  @Override
532
  public NettyChannelBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) {
533
    checkArgument(keepAliveTime > 0L, "keepalive time must be positive");
1✔
534
    keepAliveTimeNanos = timeUnit.toNanos(keepAliveTime);
×
535
    keepAliveTimeNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeNanos);
×
536
    if (keepAliveTimeNanos >= AS_LARGE_AS_INFINITE) {
×
537
      // Bump keepalive time to infinite. This disables keepalive.
538
      keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
×
539
    }
540
    return this;
×
541
  }
542

543
  /**
544
   * {@inheritDoc}
545
   *
546
   * @since 1.3.0
547
   */
548
  @CanIgnoreReturnValue
549
  @Override
550
  public NettyChannelBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) {
551
    checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive");
1✔
552
    keepAliveTimeoutNanos = timeUnit.toNanos(keepAliveTimeout);
×
553
    keepAliveTimeoutNanos = KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutNanos);
×
554
    return this;
×
555
  }
556

557
  /**
558
   * {@inheritDoc}
559
   *
560
   * @since 1.3.0
561
   */
562
  @CanIgnoreReturnValue
563
  @Override
564
  public NettyChannelBuilder keepAliveWithoutCalls(boolean enable) {
565
    keepAliveWithoutCalls = enable;
×
566
    return this;
×
567
  }
568

569

570
  /**
571
   * If non-{@code null}, attempts to create connections bound to a local port.
572
   */
573
  @CanIgnoreReturnValue
574
  public NettyChannelBuilder localSocketPicker(@Nullable LocalSocketPicker localSocketPicker) {
575
    this.localSocketPicker = localSocketPicker;
×
576
    return this;
×
577
  }
578

579
  /**
580
   * This class is meant to be overriden with a custom implementation of
581
   * {@link #createSocketAddress}.  The default implementation is a no-op.
582
   *
583
   * @since 1.16.0
584
   */
585
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4917")
586
  public static class LocalSocketPicker {
1✔
587

588
    /**
589
     * Called by gRPC to pick local socket to bind to.  This may be called multiple times.
590
     * Subclasses are expected to override this method.
591
     *
592
     * @param remoteAddress the remote address to connect to.
593
     * @param attrs the Attributes present on the {@link io.grpc.EquivalentAddressGroup} associated
594
     *        with the address.
595
     * @return a {@link SocketAddress} suitable for binding, or else {@code null}.
596
     * @since 1.16.0
597
     */
598
    @Nullable
599
    public SocketAddress createSocketAddress(
600
        SocketAddress remoteAddress, @EquivalentAddressGroup.Attr Attributes attrs) {
601
      return null;
1✔
602
    }
603
  }
604

605
  /**
606
   * Sets the maximum message size allowed for a single gRPC frame. If an inbound messages larger
607
   * than this limit is received it will not be processed and the RPC will fail with
608
   * RESOURCE_EXHAUSTED.
609
   */
610
  @CanIgnoreReturnValue
611
  @Override
612
  public NettyChannelBuilder maxInboundMessageSize(int max) {
613
    checkArgument(max >= 0, "negative max");
1✔
614
    maxInboundMessageSize = max;
1✔
615
    return this;
1✔
616
  }
617

618
  ClientTransportFactory buildTransportFactory() {
619
    assertEventLoopAndChannelType();
1✔
620

621
    ProtocolNegotiator negotiator = protocolNegotiatorFactory.newNegotiator();
1✔
622
    return new NettyTransportFactory(
1✔
623
        negotiator,
624
        channelFactory,
625
        channelOptions,
626
        eventLoopGroupPool,
627
        autoFlowControl,
628
        flowControlWindow,
629
        maxInboundMessageSize,
630
        maxHeaderListSize,
631
        softLimitHeaderListSize,
632
        keepAliveTimeNanos,
633
        keepAliveTimeoutNanos,
634
        keepAliveWithoutCalls,
635
        transportTracerFactory,
636
        localSocketPicker,
637
        useGetForSafeMethods,
638
        transportSocketType);
639
  }
640

641
  @VisibleForTesting
642
  void assertEventLoopAndChannelType() {
643
    boolean bothProvided = channelFactory != DEFAULT_CHANNEL_FACTORY
1✔
644
        && eventLoopGroupPool != DEFAULT_EVENT_LOOP_GROUP_POOL;
645
    boolean nonProvided = channelFactory == DEFAULT_CHANNEL_FACTORY
1✔
646
        && eventLoopGroupPool == DEFAULT_EVENT_LOOP_GROUP_POOL;
647
    checkState(
1✔
648
        bothProvided || nonProvided,
649
        "Both EventLoopGroup and ChannelType should be provided or neither should be");
650
  }
1✔
651

652
  int getDefaultPort() {
653
    return protocolNegotiatorFactory.getDefaultPort();
×
654
  }
655

656
  @VisibleForTesting
657
  static ProtocolNegotiator createProtocolNegotiatorByType(
658
      NegotiationType negotiationType,
659
      SslContext sslContext,
660
      ObjectPool<? extends Executor> executorPool) {
661
    switch (negotiationType) {
1✔
662
      case PLAINTEXT:
663
        return ProtocolNegotiators.plaintext();
1✔
664
      case PLAINTEXT_UPGRADE:
665
        return ProtocolNegotiators.plaintextUpgrade();
1✔
666
      case TLS:
667
        return ProtocolNegotiators.tls(sslContext, executorPool, Optional.absent(), null, null);
1✔
668
      default:
669
        throw new IllegalArgumentException("Unsupported negotiationType: " + negotiationType);
×
670
    }
671
  }
672

673
  @CanIgnoreReturnValue
674
  NettyChannelBuilder disableCheckAuthority() {
675
    this.managedChannelImplBuilder.disableCheckAuthority();
1✔
676
    return this;
1✔
677
  }
678

679
  @CanIgnoreReturnValue
680
  NettyChannelBuilder enableCheckAuthority() {
681
    this.managedChannelImplBuilder.enableCheckAuthority();
1✔
682
    return this;
1✔
683
  }
684

685
  void protocolNegotiatorFactory(ProtocolNegotiator.ClientFactory protocolNegotiatorFactory) {
686
    checkState(!freezeProtocolNegotiatorFactory,
1✔
687
               "Cannot change security when using ChannelCredentials");
688
    this.protocolNegotiatorFactory
1✔
689
        = checkNotNull(protocolNegotiatorFactory, "protocolNegotiatorFactory");
1✔
690
  }
1✔
691

692
  void setTracingEnabled(boolean value) {
693
    this.managedChannelImplBuilder.setTracingEnabled(value);
1✔
694
  }
1✔
695

696
  void setStatsEnabled(boolean value) {
697
    this.managedChannelImplBuilder.setStatsEnabled(value);
1✔
698
  }
1✔
699

700
  void setStatsRecordStartedRpcs(boolean value) {
701
    this.managedChannelImplBuilder.setStatsRecordStartedRpcs(value);
×
702
  }
×
703

704
  void setStatsRecordFinishedRpcs(boolean value) {
705
    this.managedChannelImplBuilder.setStatsRecordFinishedRpcs(value);
×
706
  }
×
707

708
  void setStatsRecordRealTimeMetrics(boolean value) {
709
    this.managedChannelImplBuilder.setStatsRecordRealTimeMetrics(value);
×
710
  }
×
711

712
  void setStatsRecordRetryMetrics(boolean value) {
713
    this.managedChannelImplBuilder.setStatsRecordRetryMetrics(value);
×
714
  }
×
715

716
  @CanIgnoreReturnValue
717
  @VisibleForTesting
718
  NettyChannelBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) {
719
    this.transportTracerFactory = transportTracerFactory;
1✔
720
    return this;
1✔
721
  }
722

723

724

725
  static Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
726
    return Collections.singleton(InetSocketAddress.class);
1✔
727
  }
728

729
  private final class DefaultProtocolNegotiator implements ProtocolNegotiator.ClientFactory {
1✔
730
    private NegotiationType negotiationType = NegotiationType.TLS;
1✔
731
    private SslContext sslContext;
732

733
    @Override
734
    public ProtocolNegotiator newNegotiator() {
735
      SslContext localSslContext = sslContext;
1✔
736
      if (negotiationType == NegotiationType.TLS && localSslContext == null) {
1✔
737
        try {
738
          localSslContext = GrpcSslContexts.forClient().build();
1✔
739
        } catch (SSLException ex) {
×
740
          throw new RuntimeException(ex);
×
741
        }
1✔
742
      }
743
      return createProtocolNegotiatorByType(negotiationType, localSslContext,
1✔
744
          managedChannelImplBuilder.getOffloadExecutorPool());
1✔
745
    }
746

747
    @Override
748
    public int getDefaultPort() {
749
      switch (negotiationType) {
1✔
750
        case PLAINTEXT:
751
        case PLAINTEXT_UPGRADE:
752
          return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
1✔
753
        case TLS:
754
          return GrpcUtil.DEFAULT_PORT_SSL;
1✔
755
        default:
756
          throw new AssertionError(negotiationType + " not handled");
×
757
      }
758
    }
759
  }
760

761
  /**
762
   * Creates Netty transports. Exposed for internal use, as it should be private.
763
   */
764
  private static final class NettyTransportFactory implements ClientTransportFactory {
765
    private final ProtocolNegotiator protocolNegotiator;
766
    private final ChannelFactory<? extends Channel> channelFactory;
767
    private final Map<ChannelOption<?>, ?> channelOptions;
768
    private final ObjectPool<? extends EventLoopGroup> groupPool;
769
    private final EventLoopGroup group;
770
    private final boolean autoFlowControl;
771
    private final int flowControlWindow;
772
    private final int maxMessageSize;
773
    private final int maxHeaderListSize;
774
    private final int softLimitHeaderListSize;
775
    private final long keepAliveTimeNanos;
776
    private final AtomicBackoff keepAliveBackoff;
777
    private final long keepAliveTimeoutNanos;
778
    private final boolean keepAliveWithoutCalls;
779
    private final TransportTracer.Factory transportTracerFactory;
780
    private final LocalSocketPicker localSocketPicker;
781
    private final boolean useGetForSafeMethods;
782

783
    private boolean closed;
784
    private final Class<? extends SocketAddress> transportSocketType;
785

786
    NettyTransportFactory(
787
        ProtocolNegotiator protocolNegotiator,
788
        ChannelFactory<? extends Channel> channelFactory,
789
        Map<ChannelOption<?>, ?> channelOptions,
790
        ObjectPool<? extends EventLoopGroup> groupPool,
791
        boolean autoFlowControl,
792
        int flowControlWindow,
793
        int maxMessageSize,
794
        int maxHeaderListSize,
795
        int softLimitHeaderListSize,
796
        long keepAliveTimeNanos,
797
        long keepAliveTimeoutNanos,
798
        boolean keepAliveWithoutCalls,
799
        TransportTracer.Factory transportTracerFactory,
800
        LocalSocketPicker localSocketPicker,
801
        boolean useGetForSafeMethods,
802
        Class<? extends SocketAddress> transportSocketType) {
1✔
803
      this.protocolNegotiator = checkNotNull(protocolNegotiator, "protocolNegotiator");
1✔
804
      this.channelFactory = channelFactory;
1✔
805
      this.channelOptions = new HashMap<ChannelOption<?>, Object>(channelOptions);
1✔
806
      this.groupPool = groupPool;
1✔
807
      this.group = groupPool.getObject();
1✔
808
      this.autoFlowControl = autoFlowControl;
1✔
809
      this.flowControlWindow = flowControlWindow;
1✔
810
      this.maxMessageSize = maxMessageSize;
1✔
811
      this.maxHeaderListSize = maxHeaderListSize;
1✔
812
      this.softLimitHeaderListSize = softLimitHeaderListSize;
1✔
813
      this.keepAliveTimeNanos = keepAliveTimeNanos;
1✔
814
      this.keepAliveBackoff = new AtomicBackoff("keepalive time nanos", keepAliveTimeNanos);
1✔
815
      this.keepAliveTimeoutNanos = keepAliveTimeoutNanos;
1✔
816
      this.keepAliveWithoutCalls = keepAliveWithoutCalls;
1✔
817
      this.transportTracerFactory = transportTracerFactory;
1✔
818
      this.localSocketPicker =
1✔
819
          localSocketPicker != null ? localSocketPicker : new LocalSocketPicker();
1✔
820
      this.useGetForSafeMethods = useGetForSafeMethods;
1✔
821
      this.transportSocketType = transportSocketType;
1✔
822
    }
1✔
823

824
    @Override
825
    public ConnectionClientTransport newClientTransport(
826
        SocketAddress serverAddress, ClientTransportOptions options, ChannelLogger channelLogger) {
827
      checkState(!closed, "The transport factory is closed.");
1✔
828

829
      ProtocolNegotiator localNegotiator = protocolNegotiator;
1✔
830
      HttpConnectProxiedSocketAddress proxiedAddr = options.getHttpConnectProxiedSocketAddress();
1✔
831
      if (proxiedAddr != null) {
1✔
832
        serverAddress = proxiedAddr.getTargetAddress();
×
833
        localNegotiator = ProtocolNegotiators.httpProxy(
×
834
            proxiedAddr.getProxyAddress(),
×
835
            proxiedAddr.getHeaders(),
×
836
            proxiedAddr.getUsername(),
×
837
            proxiedAddr.getPassword(),
×
838
            protocolNegotiator);
839
      }
840

841
      final AtomicBackoff.State keepAliveTimeNanosState = keepAliveBackoff.getState();
1✔
842
      Runnable tooManyPingsRunnable = new Runnable() {
1✔
843
        @Override
844
        public void run() {
845
          keepAliveTimeNanosState.backoff();
×
846
        }
×
847
      };
848

849
      // TODO(carl-mastrangelo): Pass channelLogger in.
850
      NettyClientTransport transport =
1✔
851
          new NettyClientTransport(
852
              serverAddress,
853
              channelFactory,
854
              channelOptions,
855
              group,
856
              localNegotiator,
857
              autoFlowControl,
858
              flowControlWindow,
859
              maxMessageSize,
860
              maxHeaderListSize,
861
              softLimitHeaderListSize,
862
              keepAliveTimeNanosState.get(),
1✔
863
              keepAliveTimeoutNanos,
864
              keepAliveWithoutCalls,
865
              options.getAuthority(),
1✔
866
              options.getUserAgent(),
1✔
867
              tooManyPingsRunnable,
868
              transportTracerFactory.create(),
1✔
869
              options.getEagAttributes(),
1✔
870
              localSocketPicker,
871
              channelLogger,
872
              useGetForSafeMethods,
873
              options.getMetricRecorder(),
1✔
874
              Ticker.systemTicker());
1✔
875
      return transport;
1✔
876
    }
877

878
    @Override
879
    public ScheduledExecutorService getScheduledExecutorService() {
880
      return group;
1✔
881
    }
882

883
    @Override
884
    public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) {
885
      checkNotNull(channelCreds, "channelCreds");
1✔
886
      FromChannelCredentialsResult result = ProtocolNegotiators.from(channelCreds);
1✔
887
      if (result.error != null) {
1✔
888
        return null;
1✔
889
      }
890
      ClientTransportFactory factory =
1✔
891
          new NettyTransportFactory(
892
              result.negotiator.newNegotiator(),
1✔
893
              channelFactory,
894
              channelOptions,
895
              groupPool,
896
              autoFlowControl,
897
              flowControlWindow,
898
              maxMessageSize,
899
              maxHeaderListSize,
900
              softLimitHeaderListSize,
901
              keepAliveTimeNanos,
902
              keepAliveTimeoutNanos,
903
              keepAliveWithoutCalls,
904
              transportTracerFactory,
905
              localSocketPicker,
906
              useGetForSafeMethods,
907
              transportSocketType);
908
      return new SwapChannelCredentialsResult(factory, result.callCredentials);
1✔
909
    }
910

911
    @Override
912
    public void close() {
913
      if (closed) {
1✔
914
        return;
1✔
915
      }
916
      closed = true;
1✔
917

918
      protocolNegotiator.close();
1✔
919
      groupPool.returnObject(group);
1✔
920
    }
1✔
921

922
    @Override
923
    public Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
924
      return transportSocketType == null ? null
1✔
925
          : Collections.singleton(transportSocketType);
1✔
926
    }
927
  }
928
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc