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

grpc / grpc-java / #19528

28 Oct 2024 05:25PM UTC coverage: 84.59% (-0.04%) from 84.627%
#19528

push

github

web-flow
netty: add soft Metadata size limit enforcement. (#11603)

33914 of 40092 relevant lines covered (84.59%)

0.85 hits per line

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

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

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

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

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

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

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

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

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

123
  private Class<? extends SocketAddress> transportSocketType = InetSocketAddress.class;
1✔
124

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

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

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

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

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

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

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

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

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

207
  NettyChannelBuilder(
208
      String target, ChannelCredentials channelCreds, CallCredentials callCreds,
209
      ProtocolNegotiator.ClientFactory negotiator) {
1✔
210
    managedChannelImplBuilder = new ManagedChannelImplBuilder(
1✔
211
        target, channelCreds, callCreds,
212
        new NettyChannelTransportFactoryBuilder(),
213
        new NettyChannelDefaultPortProvider());
214
    this.protocolNegotiatorFactory = checkNotNull(negotiator, "negotiator");
1✔
215
    this.freezeProtocolNegotiatorFactory = true;
1✔
216
  }
1✔
217

218
  NettyChannelBuilder(SocketAddress address) {
1✔
219
    managedChannelImplBuilder = new ManagedChannelImplBuilder(address,
1✔
220
        getAuthorityFromAddress(address),
1✔
221
        new NettyChannelTransportFactoryBuilder(),
222
        new NettyChannelDefaultPortProvider());
223
    this.freezeProtocolNegotiatorFactory = false;
1✔
224
  }
1✔
225

226
  NettyChannelBuilder(
227
      SocketAddress address, ChannelCredentials channelCreds, CallCredentials callCreds,
228
      ProtocolNegotiator.ClientFactory negotiator) {
1✔
229
    managedChannelImplBuilder = new ManagedChannelImplBuilder(address,
1✔
230
        getAuthorityFromAddress(address),
1✔
231
        channelCreds, callCreds,
232
        new NettyChannelTransportFactoryBuilder(),
233
        new NettyChannelDefaultPortProvider());
234
    this.protocolNegotiatorFactory = checkNotNull(negotiator, "negotiator");
1✔
235
    this.freezeProtocolNegotiatorFactory = true;
1✔
236
  }
1✔
237

238
  @Internal
239
  @Override
240
  protected ManagedChannelBuilder<?> delegate() {
241
    return managedChannelImplBuilder;
1✔
242
  }
243

244
  private static String getAuthorityFromAddress(SocketAddress address) {
245
    if (address instanceof InetSocketAddress) {
1✔
246
      InetSocketAddress inetAddress = (InetSocketAddress) address;
1✔
247
      return GrpcUtil.authorityFromHostAndPort(inetAddress.getHostString(), inetAddress.getPort());
1✔
248
    } else {
249
      return address.toString();
1✔
250
    }
251
  }
252

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

272
  /**
273
   * Similar to {@link #channelType(Class)} above but allows the
274
   * caller to specify the socket-type associated with the channelType.
275
   *
276
   * @param channelType the type of {@link Channel} to use.
277
   * @param transportSocketType the associated {@link SocketAddress} type. If {@code null}, then
278
   *     no compatibility check is performed between channel transport and name-resolver addresses.
279
   */
280
  @CanIgnoreReturnValue
281
  public NettyChannelBuilder channelType(Class<? extends Channel> channelType,
282
      @Nullable Class<? extends SocketAddress> transportSocketType) {
283
    checkNotNull(channelType, "channelType");
1✔
284
    return channelFactory(new ReflectiveChannelFactory<>(channelType),
1✔
285
        transportSocketType);
286
  }
287

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

306
  /**
307
   * Similar to {@link #channelFactory(ChannelFactory)} above but allows the
308
   * caller to specify the socket-type associated with the channelFactory.
309
   *
310
   * @param channelFactory the {@link ChannelFactory} to use.
311
   * @param transportSocketType the associated {@link SocketAddress} type. If {@code null}, then
312
   *     no compatibility check is performed between channel transport and name-resolver addresses.
313
   */
314
  @CanIgnoreReturnValue
315
  public NettyChannelBuilder channelFactory(ChannelFactory<? extends Channel> channelFactory,
316
      @Nullable Class<? extends SocketAddress> transportSocketType) {
317
    this.channelFactory = checkNotNull(channelFactory, "channelFactory");
1✔
318
    this.transportSocketType = transportSocketType;
1✔
319
    return this;
1✔
320
  }
321

322
  /**
323
   * Specifies a channel option. As the underlying channel as well as network implementation may
324
   * ignore this value applications should consider it a hint.
325
   */
326
  @CanIgnoreReturnValue
327
  public <T> NettyChannelBuilder withOption(ChannelOption<T> option, T value) {
328
    channelOptions.put(option, value);
×
329
    return this;
×
330
  }
331

332
  /**
333
   * Sets the negotiation type for the HTTP/2 connection.
334
   *
335
   * <p>Default: <code>TLS</code>
336
   */
337
  @CanIgnoreReturnValue
338
  public NettyChannelBuilder negotiationType(NegotiationType type) {
339
    checkState(!freezeProtocolNegotiatorFactory,
1✔
340
               "Cannot change security when using ChannelCredentials");
341
    if (!(protocolNegotiatorFactory instanceof DefaultProtocolNegotiator)) {
1✔
342
      // Do nothing for compatibility
343
      return this;
×
344
    }
345
    ((DefaultProtocolNegotiator) protocolNegotiatorFactory).negotiationType = type;
1✔
346
    return this;
1✔
347
  }
348

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

370
  @CanIgnoreReturnValue
371
  NettyChannelBuilder eventLoopGroupPool(ObjectPool<? extends EventLoopGroup> eventLoopGroupPool) {
372
    this.eventLoopGroupPool = checkNotNull(eventLoopGroupPool, "eventLoopGroupPool");
1✔
373
    return this;
1✔
374
  }
375

376
  /**
377
   * SSL/TLS context to use instead of the system default. It must have been configured with {@link
378
   * GrpcSslContexts}, but options could have been overridden.
379
   */
380
  @CanIgnoreReturnValue
381
  public NettyChannelBuilder sslContext(SslContext sslContext) {
382
    checkState(!freezeProtocolNegotiatorFactory,
1✔
383
               "Cannot change security when using ChannelCredentials");
384
    if (sslContext != null) {
1✔
385
      checkArgument(sslContext.isClient(),
1✔
386
          "Server SSL context can not be used for client channel");
387
      GrpcSslContexts.ensureAlpnAndH2Enabled(sslContext.applicationProtocolNegotiator());
1✔
388
    }
389
    if (!(protocolNegotiatorFactory instanceof DefaultProtocolNegotiator)) {
1✔
390
      // Do nothing for compatibility
391
      return this;
×
392
    }
393
    ((DefaultProtocolNegotiator) protocolNegotiatorFactory).sslContext = sslContext;
1✔
394
    return this;
1✔
395
  }
396

397
  /**
398
   * Sets the initial flow control window in bytes. Setting initial flow control window enables auto
399
   * flow control tuning using bandwidth-delay product algorithm. To disable auto flow control
400
   * tuning, use {@link #flowControlWindow(int)}. By default, auto flow control is enabled with
401
   * initial flow control window size of {@link #DEFAULT_FLOW_CONTROL_WINDOW}.
402
   */
403
  @CanIgnoreReturnValue
404
  public NettyChannelBuilder initialFlowControlWindow(int initialFlowControlWindow) {
405
    checkArgument(initialFlowControlWindow > 0, "initialFlowControlWindow must be positive");
1✔
406
    this.flowControlWindow = initialFlowControlWindow;
1✔
407
    this.autoFlowControl = true;
1✔
408
    return this;
1✔
409
  }
410

411
  /**
412
   * Sets the flow control window in bytes. Setting flowControlWindow disables auto flow control
413
   * tuning; use {@link #initialFlowControlWindow(int)} to enable auto flow control tuning. If not
414
   * called, the default value is {@link #DEFAULT_FLOW_CONTROL_WINDOW}) with auto flow control
415
   * tuning.
416
   */
417
  @CanIgnoreReturnValue
418
  public NettyChannelBuilder flowControlWindow(int flowControlWindow) {
419
    checkArgument(flowControlWindow > 0, "flowControlWindow must be positive");
1✔
420
    this.flowControlWindow = flowControlWindow;
1✔
421
    this.autoFlowControl = false;
1✔
422
    return this;
1✔
423
  }
424

425
  /**
426
   * Sets the maximum size of header list allowed to be received. This is cumulative size of the
427
   * headers with some overhead, as defined for
428
   * <a href="http://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2">
429
   * HTTP/2's SETTINGS_MAX_HEADER_LIST_SIZE</a>. The default is 8 KiB.
430
   *
431
   * @deprecated Use {@link #maxInboundMetadataSize} instead
432
   */
433
  @CanIgnoreReturnValue
434
  @Deprecated
435
  @InlineMe(replacement = "this.maxInboundMetadataSize(maxHeaderListSize)")
436
  public NettyChannelBuilder maxHeaderListSize(int maxHeaderListSize) {
437
    return maxInboundMetadataSize(maxHeaderListSize);
×
438
  }
439

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

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

493
  /**
494
   * Equivalent to using {@link #negotiationType(NegotiationType)} with {@code PLAINTEXT}.
495
   */
496
  @CanIgnoreReturnValue
497
  @Override
498
  public NettyChannelBuilder usePlaintext() {
499
    negotiationType(NegotiationType.PLAINTEXT);
1✔
500
    return this;
1✔
501
  }
502

503
  /**
504
   * Equivalent to using {@link #negotiationType(NegotiationType)} with {@code TLS}.
505
   */
506
  @CanIgnoreReturnValue
507
  @Override
508
  public NettyChannelBuilder useTransportSecurity() {
509
    negotiationType(NegotiationType.TLS);
×
510
    return this;
×
511
  }
512

513
  /**
514
   * {@inheritDoc}
515
   *
516
   * @since 1.3.0
517
   */
518
  @CanIgnoreReturnValue
519
  @Override
520
  public NettyChannelBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) {
521
    checkArgument(keepAliveTime > 0L, "keepalive time must be positive");
1✔
522
    keepAliveTimeNanos = timeUnit.toNanos(keepAliveTime);
×
523
    keepAliveTimeNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeNanos);
×
524
    if (keepAliveTimeNanos >= AS_LARGE_AS_INFINITE) {
×
525
      // Bump keepalive time to infinite. This disables keepalive.
526
      keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
×
527
    }
528
    return this;
×
529
  }
530

531
  /**
532
   * {@inheritDoc}
533
   *
534
   * @since 1.3.0
535
   */
536
  @CanIgnoreReturnValue
537
  @Override
538
  public NettyChannelBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) {
539
    checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive");
1✔
540
    keepAliveTimeoutNanos = timeUnit.toNanos(keepAliveTimeout);
×
541
    keepAliveTimeoutNanos = KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutNanos);
×
542
    return this;
×
543
  }
544

545
  /**
546
   * {@inheritDoc}
547
   *
548
   * @since 1.3.0
549
   */
550
  @CanIgnoreReturnValue
551
  @Override
552
  public NettyChannelBuilder keepAliveWithoutCalls(boolean enable) {
553
    keepAliveWithoutCalls = enable;
×
554
    return this;
×
555
  }
556

557

558
  /**
559
   * If non-{@code null}, attempts to create connections bound to a local port.
560
   */
561
  @CanIgnoreReturnValue
562
  public NettyChannelBuilder localSocketPicker(@Nullable LocalSocketPicker localSocketPicker) {
563
    this.localSocketPicker = localSocketPicker;
×
564
    return this;
×
565
  }
566

567
  /**
568
   * This class is meant to be overriden with a custom implementation of
569
   * {@link #createSocketAddress}.  The default implementation is a no-op.
570
   *
571
   * @since 1.16.0
572
   */
573
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/4917")
574
  public static class LocalSocketPicker {
1✔
575

576
    /**
577
     * Called by gRPC to pick local socket to bind to.  This may be called multiple times.
578
     * Subclasses are expected to override this method.
579
     *
580
     * @param remoteAddress the remote address to connect to.
581
     * @param attrs the Attributes present on the {@link io.grpc.EquivalentAddressGroup} associated
582
     *        with the address.
583
     * @return a {@link SocketAddress} suitable for binding, or else {@code null}.
584
     * @since 1.16.0
585
     */
586
    @Nullable
587
    public SocketAddress createSocketAddress(
588
        SocketAddress remoteAddress, @EquivalentAddressGroup.Attr Attributes attrs) {
589
      return null;
1✔
590
    }
591
  }
592

593
  /**
594
   * Sets the maximum message size allowed for a single gRPC frame. If an inbound messages larger
595
   * than this limit is received it will not be processed and the RPC will fail with
596
   * RESOURCE_EXHAUSTED.
597
   */
598
  @CanIgnoreReturnValue
599
  @Override
600
  public NettyChannelBuilder maxInboundMessageSize(int max) {
601
    checkArgument(max >= 0, "negative max");
1✔
602
    maxInboundMessageSize = max;
1✔
603
    return this;
1✔
604
  }
605

606
  ClientTransportFactory buildTransportFactory() {
607
    assertEventLoopAndChannelType();
1✔
608

609
    ProtocolNegotiator negotiator = protocolNegotiatorFactory.newNegotiator();
1✔
610
    return new NettyTransportFactory(
1✔
611
        negotiator,
612
        channelFactory,
613
        channelOptions,
614
        eventLoopGroupPool,
615
        autoFlowControl,
616
        flowControlWindow,
617
        maxInboundMessageSize,
618
        maxHeaderListSize,
619
        softLimitHeaderListSize,
620
        keepAliveTimeNanos,
621
        keepAliveTimeoutNanos,
622
        keepAliveWithoutCalls,
623
        transportTracerFactory,
624
        localSocketPicker,
625
        useGetForSafeMethods,
626
        transportSocketType);
627
  }
628

629
  @VisibleForTesting
630
  void assertEventLoopAndChannelType() {
631
    boolean bothProvided = channelFactory != DEFAULT_CHANNEL_FACTORY
1✔
632
        && eventLoopGroupPool != DEFAULT_EVENT_LOOP_GROUP_POOL;
633
    boolean nonProvided = channelFactory == DEFAULT_CHANNEL_FACTORY
1✔
634
        && eventLoopGroupPool == DEFAULT_EVENT_LOOP_GROUP_POOL;
635
    checkState(
1✔
636
        bothProvided || nonProvided,
637
        "Both EventLoopGroup and ChannelType should be provided or neither should be");
638
  }
1✔
639

640
  int getDefaultPort() {
641
    return protocolNegotiatorFactory.getDefaultPort();
×
642
  }
643

644
  @VisibleForTesting
645
  static ProtocolNegotiator createProtocolNegotiatorByType(
646
      NegotiationType negotiationType,
647
      SslContext sslContext,
648
      ObjectPool<? extends Executor> executorPool) {
649
    switch (negotiationType) {
1✔
650
      case PLAINTEXT:
651
        return ProtocolNegotiators.plaintext();
1✔
652
      case PLAINTEXT_UPGRADE:
653
        return ProtocolNegotiators.plaintextUpgrade();
1✔
654
      case TLS:
655
        return ProtocolNegotiators.tls(sslContext, executorPool, Optional.empty());
1✔
656
      default:
657
        throw new IllegalArgumentException("Unsupported negotiationType: " + negotiationType);
×
658
    }
659
  }
660

661
  @CanIgnoreReturnValue
662
  NettyChannelBuilder disableCheckAuthority() {
663
    this.managedChannelImplBuilder.disableCheckAuthority();
1✔
664
    return this;
1✔
665
  }
666

667
  @CanIgnoreReturnValue
668
  NettyChannelBuilder enableCheckAuthority() {
669
    this.managedChannelImplBuilder.enableCheckAuthority();
1✔
670
    return this;
1✔
671
  }
672

673
  void protocolNegotiatorFactory(ProtocolNegotiator.ClientFactory protocolNegotiatorFactory) {
674
    checkState(!freezeProtocolNegotiatorFactory,
1✔
675
               "Cannot change security when using ChannelCredentials");
676
    this.protocolNegotiatorFactory
1✔
677
        = checkNotNull(protocolNegotiatorFactory, "protocolNegotiatorFactory");
1✔
678
  }
1✔
679

680
  void setTracingEnabled(boolean value) {
681
    this.managedChannelImplBuilder.setTracingEnabled(value);
1✔
682
  }
1✔
683

684
  void setStatsEnabled(boolean value) {
685
    this.managedChannelImplBuilder.setStatsEnabled(value);
1✔
686
  }
1✔
687

688
  void setStatsRecordStartedRpcs(boolean value) {
689
    this.managedChannelImplBuilder.setStatsRecordStartedRpcs(value);
×
690
  }
×
691

692
  void setStatsRecordFinishedRpcs(boolean value) {
693
    this.managedChannelImplBuilder.setStatsRecordFinishedRpcs(value);
×
694
  }
×
695

696
  void setStatsRecordRealTimeMetrics(boolean value) {
697
    this.managedChannelImplBuilder.setStatsRecordRealTimeMetrics(value);
×
698
  }
×
699

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

704
  @CanIgnoreReturnValue
705
  @VisibleForTesting
706
  NettyChannelBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) {
707
    this.transportTracerFactory = transportTracerFactory;
1✔
708
    return this;
1✔
709
  }
710

711
  static Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
712
    return Collections.singleton(InetSocketAddress.class);
1✔
713
  }
714

715
  private final class DefaultProtocolNegotiator implements ProtocolNegotiator.ClientFactory {
1✔
716
    private NegotiationType negotiationType = NegotiationType.TLS;
1✔
717
    private SslContext sslContext;
718

719
    @Override
720
    public ProtocolNegotiator newNegotiator() {
721
      SslContext localSslContext = sslContext;
1✔
722
      if (negotiationType == NegotiationType.TLS && localSslContext == null) {
1✔
723
        try {
724
          localSslContext = GrpcSslContexts.forClient().build();
1✔
725
        } catch (SSLException ex) {
×
726
          throw new RuntimeException(ex);
×
727
        }
1✔
728
      }
729
      return createProtocolNegotiatorByType(negotiationType, localSslContext,
1✔
730
          managedChannelImplBuilder.getOffloadExecutorPool());
1✔
731
    }
732

733
    @Override
734
    public int getDefaultPort() {
735
      switch (negotiationType) {
1✔
736
        case PLAINTEXT:
737
        case PLAINTEXT_UPGRADE:
738
          return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
1✔
739
        case TLS:
740
          return GrpcUtil.DEFAULT_PORT_SSL;
1✔
741
        default:
742
          throw new AssertionError(negotiationType + " not handled");
×
743
      }
744
    }
745
  }
746

747
  /**
748
   * Creates Netty transports. Exposed for internal use, as it should be private.
749
   */
750
  private static final class NettyTransportFactory implements ClientTransportFactory {
751
    private final ProtocolNegotiator protocolNegotiator;
752
    private final ChannelFactory<? extends Channel> channelFactory;
753
    private final Map<ChannelOption<?>, ?> channelOptions;
754
    private final ObjectPool<? extends EventLoopGroup> groupPool;
755
    private final EventLoopGroup group;
756
    private final boolean autoFlowControl;
757
    private final int flowControlWindow;
758
    private final int maxMessageSize;
759
    private final int maxHeaderListSize;
760
    private final int softLimitHeaderListSize;
761
    private final long keepAliveTimeNanos;
762
    private final AtomicBackoff keepAliveBackoff;
763
    private final long keepAliveTimeoutNanos;
764
    private final boolean keepAliveWithoutCalls;
765
    private final TransportTracer.Factory transportTracerFactory;
766
    private final LocalSocketPicker localSocketPicker;
767
    private final boolean useGetForSafeMethods;
768

769
    private boolean closed;
770
    private final Class<? extends SocketAddress> transportSocketType;
771

772
    NettyTransportFactory(
773
        ProtocolNegotiator protocolNegotiator,
774
        ChannelFactory<? extends Channel> channelFactory,
775
        Map<ChannelOption<?>, ?> channelOptions,
776
        ObjectPool<? extends EventLoopGroup> groupPool,
777
        boolean autoFlowControl,
778
        int flowControlWindow,
779
        int maxMessageSize,
780
        int maxHeaderListSize,
781
        int softLimitHeaderListSize,
782
        long keepAliveTimeNanos,
783
        long keepAliveTimeoutNanos,
784
        boolean keepAliveWithoutCalls,
785
        TransportTracer.Factory transportTracerFactory,
786
        LocalSocketPicker localSocketPicker,
787
        boolean useGetForSafeMethods,
788
        Class<? extends SocketAddress> transportSocketType) {
1✔
789
      this.protocolNegotiator = checkNotNull(protocolNegotiator, "protocolNegotiator");
1✔
790
      this.channelFactory = channelFactory;
1✔
791
      this.channelOptions = new HashMap<ChannelOption<?>, Object>(channelOptions);
1✔
792
      this.groupPool = groupPool;
1✔
793
      this.group = groupPool.getObject();
1✔
794
      this.autoFlowControl = autoFlowControl;
1✔
795
      this.flowControlWindow = flowControlWindow;
1✔
796
      this.maxMessageSize = maxMessageSize;
1✔
797
      this.maxHeaderListSize = maxHeaderListSize;
1✔
798
      this.softLimitHeaderListSize = softLimitHeaderListSize;
1✔
799
      this.keepAliveTimeNanos = keepAliveTimeNanos;
1✔
800
      this.keepAliveBackoff = new AtomicBackoff("keepalive time nanos", keepAliveTimeNanos);
1✔
801
      this.keepAliveTimeoutNanos = keepAliveTimeoutNanos;
1✔
802
      this.keepAliveWithoutCalls = keepAliveWithoutCalls;
1✔
803
      this.transportTracerFactory = transportTracerFactory;
1✔
804
      this.localSocketPicker =
1✔
805
          localSocketPicker != null ? localSocketPicker : new LocalSocketPicker();
1✔
806
      this.useGetForSafeMethods = useGetForSafeMethods;
1✔
807
      this.transportSocketType = transportSocketType;
1✔
808
    }
1✔
809

810
    @Override
811
    public ConnectionClientTransport newClientTransport(
812
        SocketAddress serverAddress, ClientTransportOptions options, ChannelLogger channelLogger) {
813
      checkState(!closed, "The transport factory is closed.");
1✔
814

815
      ProtocolNegotiator localNegotiator = protocolNegotiator;
1✔
816
      HttpConnectProxiedSocketAddress proxiedAddr = options.getHttpConnectProxiedSocketAddress();
1✔
817
      if (proxiedAddr != null) {
1✔
818
        serverAddress = proxiedAddr.getTargetAddress();
×
819
        localNegotiator = ProtocolNegotiators.httpProxy(
×
820
            proxiedAddr.getProxyAddress(),
×
821
            proxiedAddr.getUsername(),
×
822
            proxiedAddr.getPassword(),
×
823
            protocolNegotiator);
824
      }
825

826
      final AtomicBackoff.State keepAliveTimeNanosState = keepAliveBackoff.getState();
1✔
827
      Runnable tooManyPingsRunnable = new Runnable() {
1✔
828
        @Override
829
        public void run() {
830
          keepAliveTimeNanosState.backoff();
×
831
        }
×
832
      };
833

834
      // TODO(carl-mastrangelo): Pass channelLogger in.
835
      NettyClientTransport transport =
1✔
836
          new NettyClientTransport(
837
              serverAddress,
838
              channelFactory,
839
              channelOptions,
840
              group,
841
              localNegotiator,
842
              autoFlowControl,
843
              flowControlWindow,
844
              maxMessageSize,
845
              maxHeaderListSize,
846
              softLimitHeaderListSize,
847
              keepAliveTimeNanosState.get(),
1✔
848
              keepAliveTimeoutNanos,
849
              keepAliveWithoutCalls,
850
              options.getAuthority(),
1✔
851
              options.getUserAgent(),
1✔
852
              tooManyPingsRunnable,
853
              transportTracerFactory.create(),
1✔
854
              options.getEagAttributes(),
1✔
855
              localSocketPicker,
856
              channelLogger,
857
              useGetForSafeMethods,
858
              Ticker.systemTicker());
1✔
859
      return transport;
1✔
860
    }
861

862
    @Override
863
    public ScheduledExecutorService getScheduledExecutorService() {
864
      return group;
1✔
865
    }
866

867
    @Override
868
    public SwapChannelCredentialsResult swapChannelCredentials(ChannelCredentials channelCreds) {
869
      checkNotNull(channelCreds, "channelCreds");
1✔
870
      FromChannelCredentialsResult result = ProtocolNegotiators.from(channelCreds);
1✔
871
      if (result.error != null) {
1✔
872
        return null;
1✔
873
      }
874
      ClientTransportFactory factory =
1✔
875
          new NettyTransportFactory(
876
              result.negotiator.newNegotiator(),
1✔
877
              channelFactory,
878
              channelOptions,
879
              groupPool,
880
              autoFlowControl,
881
              flowControlWindow,
882
              maxMessageSize,
883
              maxHeaderListSize,
884
              softLimitHeaderListSize,
885
              keepAliveTimeNanos,
886
              keepAliveTimeoutNanos,
887
              keepAliveWithoutCalls,
888
              transportTracerFactory,
889
              localSocketPicker,
890
              useGetForSafeMethods,
891
              transportSocketType);
892
      return new SwapChannelCredentialsResult(factory, result.callCredentials);
1✔
893
    }
894

895
    @Override
896
    public void close() {
897
      if (closed) {
1✔
898
        return;
1✔
899
      }
900
      closed = true;
1✔
901

902
      protocolNegotiator.close();
1✔
903
      groupPool.returnObject(group);
1✔
904
    }
1✔
905

906
    @Override
907
    public Collection<Class<? extends SocketAddress>> getSupportedSocketAddressTypes() {
908
      return transportSocketType == null ? null
1✔
909
          : Collections.singleton(transportSocketType);
1✔
910
    }
911
  }
912
}
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

© 2025 Coveralls, Inc