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

grpc / grpc-java / #20414

20 Aug 2026 06:02AM UTC coverage: 89.192% (-0.008%) from 89.2%
#20414

push

github

web-flow
netty: Support never-indexed metadata keys (#12976)

Add NettyChannelBuilder.neverIndexMetadataKey() and
neverIndexMetadataKeys() so callers can mark selected outbound metadata
keys for HPACK's never-indexed literal representation.

High-cardinality metadata values provide little compression benefit and
can churn the server's dynamic HPACK table. Keeping them out of the
table avoids unnecessary insertion and eviction work while preserving
dynamic indexing for other headers.

Propagate an immutable set of normalized metadata names through the
client transport and use it in Netty's HPACK sensitivity detector. Add
unit and interoperability coverage.

Generated with AI using OpenAI Codex (GPT-5).

Co-authored-by: Codex <noreply@openai.com>

38580 of 43255 relevant lines covered (89.19%)

0.89 hits per line

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

59.59
/../netty/src/main/java/io/grpc/netty/NettyServerBuilder.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_MAX_MESSAGE_SIZE;
23
import static io.grpc.internal.GrpcUtil.DEFAULT_SERVER_KEEPALIVE_TIMEOUT_NANOS;
24
import static io.grpc.internal.GrpcUtil.DEFAULT_SERVER_KEEPALIVE_TIME_NANOS;
25
import static io.grpc.internal.GrpcUtil.DEFAULT_SERVER_PERMIT_KEEPALIVE_TIME_NANOS;
26
import static io.grpc.internal.GrpcUtil.SERVER_KEEPALIVE_TIME_NANOS_DISABLED;
27

28
import com.google.common.annotations.VisibleForTesting;
29
import com.google.errorprone.annotations.CanIgnoreReturnValue;
30
import com.google.errorprone.annotations.CheckReturnValue;
31
import com.google.errorprone.annotations.InlineMe;
32
import io.grpc.Attributes;
33
import io.grpc.ExperimentalApi;
34
import io.grpc.ForwardingServerBuilder;
35
import io.grpc.Internal;
36
import io.grpc.Metadata;
37
import io.grpc.MetricRecorder;
38
import io.grpc.ServerBuilder;
39
import io.grpc.ServerCredentials;
40
import io.grpc.ServerStreamTracer;
41
import io.grpc.internal.FixedObjectPool;
42
import io.grpc.internal.GrpcUtil;
43
import io.grpc.internal.InternalServer;
44
import io.grpc.internal.KeepAliveManager;
45
import io.grpc.internal.ObjectPool;
46
import io.grpc.internal.ServerImplBuilder;
47
import io.grpc.internal.ServerImplBuilder.ClientTransportServersBuilder;
48
import io.grpc.internal.SharedResourcePool;
49
import io.grpc.internal.TransportTracer;
50
import io.netty.channel.ChannelFactory;
51
import io.netty.channel.ChannelOption;
52
import io.netty.channel.EventLoopGroup;
53
import io.netty.channel.ReflectiveChannelFactory;
54
import io.netty.channel.ServerChannel;
55
import io.netty.channel.socket.nio.NioServerSocketChannel;
56
import io.netty.handler.ssl.SslContext;
57
import io.netty.util.AsciiString;
58
import java.io.File;
59
import java.io.InputStream;
60
import java.net.InetSocketAddress;
61
import java.net.SocketAddress;
62
import java.util.ArrayList;
63
import java.util.Collection;
64
import java.util.HashMap;
65
import java.util.HashSet;
66
import java.util.List;
67
import java.util.Map;
68
import java.util.Set;
69
import java.util.concurrent.TimeUnit;
70
import javax.net.ssl.SSLException;
71

72
/**
73
 * A builder to help simplify the construction of a Netty-based GRPC server.
74
 */
75
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1784")
76
@CheckReturnValue
77
public final class NettyServerBuilder extends ForwardingServerBuilder<NettyServerBuilder> {
78

79
  // 1MiB
80
  public static final int DEFAULT_FLOW_CONTROL_WINDOW = 1024 * 1024;
81

82
  static final long MAX_CONNECTION_IDLE_NANOS_DISABLED = Long.MAX_VALUE;
83
  static final long MAX_CONNECTION_AGE_NANOS_DISABLED = Long.MAX_VALUE;
84
  static final long MAX_CONNECTION_AGE_GRACE_NANOS_INFINITE = Long.MAX_VALUE;
85
  static final int MAX_RST_COUNT_DISABLED = 0;
86

87
  private static final long MIN_MAX_CONNECTION_IDLE_NANO = TimeUnit.SECONDS.toNanos(1L);
1 ✔
88
  private static final long MIN_MAX_CONNECTION_AGE_NANO = TimeUnit.SECONDS.toNanos(1L);
1 ✔
89
  private static final long AS_LARGE_AS_INFINITE = TimeUnit.DAYS.toNanos(1000L);
1 ✔
90
  private static final ObjectPool<? extends EventLoopGroup> DEFAULT_BOSS_EVENT_LOOP_GROUP_POOL =
1 ✔
91
      SharedResourcePool.forResource(Utils.DEFAULT_BOSS_EVENT_LOOP_GROUP);
1 ✔
92
  private static final ObjectPool<? extends EventLoopGroup> DEFAULT_WORKER_EVENT_LOOP_GROUP_POOL =
1 ✔
93
      SharedResourcePool.forResource(Utils.DEFAULT_WORKER_EVENT_LOOP_GROUP);
1 ✔
94

95
  private final ServerImplBuilder serverImplBuilder;
96
  private final List<SocketAddress> listenAddresses = new ArrayList<>();
1 ✔
97

98
  private TransportTracer.Factory transportTracerFactory = TransportTracer.getDefaultFactory();
1 ✔
99
  private ChannelFactory<? extends ServerChannel> channelFactory =
1 ✔
100
      Utils.DEFAULT_SERVER_CHANNEL_FACTORY;
101
  private final Map<ChannelOption<?>, Object> channelOptions = new HashMap<>();
1 ✔
102
  private final Map<ChannelOption<?>, Object> childChannelOptions = new HashMap<>();
1 ✔
103
  private ObjectPool<? extends EventLoopGroup> bossEventLoopGroupPool =
1 ✔
104
      DEFAULT_BOSS_EVENT_LOOP_GROUP_POOL;
105
  private ObjectPool<? extends EventLoopGroup> workerEventLoopGroupPool =
1 ✔
106
      DEFAULT_WORKER_EVENT_LOOP_GROUP_POOL;
107
  private boolean forceHeapBuffer;
108
  private ProtocolNegotiator.ServerFactory protocolNegotiatorFactory;
109
  private final boolean freezeProtocolNegotiatorFactory;
110
  private int maxConcurrentCallsPerConnection = Integer.MAX_VALUE;
1 ✔
111
  private boolean autoFlowControl = true;
1 ✔
112
  private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW;
1 ✔
113
  private final Set<AsciiString> neverIndexedMetadataKeys = new HashSet<>();
1 ✔
114
  private int maxMessageSize = DEFAULT_MAX_MESSAGE_SIZE;
1 ✔
115
  private int maxHeaderListSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE;
1 ✔
116
  private int softLimitHeaderListSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE;
1 ✔
117
  private long keepAliveTimeInNanos = DEFAULT_SERVER_KEEPALIVE_TIME_NANOS;
1 ✔
118
  private long keepAliveTimeoutInNanos = DEFAULT_SERVER_KEEPALIVE_TIMEOUT_NANOS;
1 ✔
119
  private long maxConnectionIdleInNanos = MAX_CONNECTION_IDLE_NANOS_DISABLED;
1 ✔
120
  private long maxConnectionAgeInNanos = MAX_CONNECTION_AGE_NANOS_DISABLED;
1 ✔
121
  private long maxConnectionAgeGraceInNanos = MAX_CONNECTION_AGE_GRACE_NANOS_INFINITE;
1 ✔
122
  private boolean permitKeepAliveWithoutCalls;
123
  private long permitKeepAliveTimeInNanos = DEFAULT_SERVER_PERMIT_KEEPALIVE_TIME_NANOS;
1 ✔
124
  private int maxRstCount;
125
  private long maxRstPeriodNanos;
126
  private Attributes eagAttributes = Attributes.EMPTY;
1 ✔
127

128
  /**
129
   * Creates a server builder that will bind to the given port.
130
   *
131
   * @param port the port on which the server is to be bound.
132
   * @return the server builder.
133
   */
134
  public static NettyServerBuilder forPort(int port) {
135
    return forAddress(new InetSocketAddress(port));
1 ✔
136
  }
137

138
  /**
139
   * Creates a server builder that will bind to the given port.
140
   *
141
   * @param port the port on which the server is to be bound.
142
   * @return the server builder.
143
   */
144
  public static NettyServerBuilder forPort(int port, ServerCredentials creds) {
145
    return forAddress(new InetSocketAddress(port), creds);
1 ✔
146
  }
147

148
  /**
149
   * Creates a server builder configured with the given {@link SocketAddress}.
150
   *
151
   * @param address the socket address on which the server is to be bound.
152
   * @return the server builder
153
   */
154
  public static NettyServerBuilder forAddress(SocketAddress address) {
155
    return new NettyServerBuilder(address);
1 ✔
156
  }
157

158
  /**
159
   * Creates a server builder configured with the given {@link SocketAddress}.
160
   *
161
   * @param address the socket address on which the server is to be bound.
162
   * @return the server builder
163
   */
164
  public static NettyServerBuilder forAddress(SocketAddress address, ServerCredentials creds) {
165
    ProtocolNegotiators.FromServerCredentialsResult result = ProtocolNegotiators.from(creds);
1 ✔
166
    if (result.error != null) {
1 ✔
167
      throw new IllegalArgumentException(result.error);
×
168
    }
169
    return new NettyServerBuilder(address, result.negotiator);
1 ✔
170
  }
171

172
  private final class NettyClientTransportServersBuilder implements ClientTransportServersBuilder {
1 ✔
173
    @Override
174
    public InternalServer buildClientTransportServers(
175
        List<? extends ServerStreamTracer.Factory> streamTracerFactories,
176
        MetricRecorder metricRecorder) {
177
      return buildTransportServers(streamTracerFactories, metricRecorder);
1 ✔
178
    }
179
  }
180

181
  private NettyServerBuilder(SocketAddress address) {
1 ✔
182
    serverImplBuilder = new ServerImplBuilder(new NettyClientTransportServersBuilder());
1 ✔
183
    this.listenAddresses.add(address);
1 ✔
184
    this.protocolNegotiatorFactory = ProtocolNegotiators.serverPlaintextFactory();
1 ✔
185
    this.freezeProtocolNegotiatorFactory = false;
1 ✔
186
  }
1 ✔
187

188
  NettyServerBuilder(SocketAddress address, ProtocolNegotiator.ServerFactory negotiatorFactory) {
1 ✔
189
    serverImplBuilder = new ServerImplBuilder(new NettyClientTransportServersBuilder());
1 ✔
190
    this.listenAddresses.add(address);
1 ✔
191
    this.protocolNegotiatorFactory = checkNotNull(negotiatorFactory, "negotiatorFactory");
1 ✔
192
    this.freezeProtocolNegotiatorFactory = true;
1 ✔
193
  }
1 ✔
194

195
  @Internal
196
  @Override
197
  protected ServerBuilder<?> delegate() {
198
    return serverImplBuilder;
1 ✔
199
  }
200

201
  /**
202
   * Adds an additional address for this server to listen on.  Callers must ensure that all socket
203
   * addresses are compatible with the Netty channel type, and that they don't conflict with each
204
   * other.
205
   */
206
  @CanIgnoreReturnValue
207
  public NettyServerBuilder addListenAddress(SocketAddress listenAddress) {
208
    this.listenAddresses.add(checkNotNull(listenAddress, "listenAddress"));
1 ✔
209
    return this;
1 ✔
210
  }
211

212
  /**
213
   * Specifies the channel type to use, by default we use {@code EpollServerSocketChannel} if
214
   * available, otherwise using {@link NioServerSocketChannel}.
215
   *
216
   * <p>You either use this or {@link #channelFactory(io.netty.channel.ChannelFactory)} if your
217
   * {@link ServerChannel} implementation has no no-args constructor.
218
   *
219
   * <p>It's an optional parameter. If the user has not provided an Channel type or ChannelFactory
220
   * when the channel is built, the builder will use the default one which is static.
221
   *
222
   * <p>You must also provide corresponding {@link EventLoopGroup} using {@link
223
   * #workerEventLoopGroup(EventLoopGroup)} and {@link #bossEventLoopGroup(EventLoopGroup)}. For
224
   * example, {@link NioServerSocketChannel} must use {@link
225
   * io.netty.channel.nio.NioEventLoopGroup}, otherwise your server won't start.
226
   */
227
  @CanIgnoreReturnValue
228
  public NettyServerBuilder channelType(Class<? extends ServerChannel> channelType) {
229
    checkNotNull(channelType, "channelType");
1 ✔
230
    return channelFactory(new ReflectiveChannelFactory<>(channelType));
1 ✔
231
  }
232

233
  /**
234
   * Specifies the {@link ChannelFactory} to create {@link ServerChannel} instances. This method is
235
   * usually only used if the specific {@code ServerChannel} requires complex logic which requires
236
   * additional information to create the {@code ServerChannel}. Otherwise, recommend to use {@link
237
   * #channelType(Class)}.
238
   *
239
   * <p>It's an optional parameter. If the user has not provided an Channel type or ChannelFactory
240
   * when the channel is built, the builder will use the default one which is static.
241
   *
242
   * <p>You must also provide corresponding {@link EventLoopGroup} using {@link
243
   * #workerEventLoopGroup(EventLoopGroup)} and {@link #bossEventLoopGroup(EventLoopGroup)}. For
244
   * example, if the factory creates {@link NioServerSocketChannel} you must use {@link
245
   * io.netty.channel.nio.NioEventLoopGroup}, otherwise your server won't start.
246
   */
247
  @CanIgnoreReturnValue
248
  public NettyServerBuilder channelFactory(ChannelFactory<? extends ServerChannel> channelFactory) {
249
    this.channelFactory = checkNotNull(channelFactory, "channelFactory");
1 ✔
250
    return this;
1 ✔
251
  }
252

253
  /**
254
   * Specifies a channel option. As the underlying channel as well as network implementation may
255
   * ignore this value applications should consider it a hint.
256
   *
257
   * @since 1.30.0
258
   */
259
  @CanIgnoreReturnValue
260
  public <T> NettyServerBuilder withOption(ChannelOption<T> option, T value) {
261
    this.channelOptions.put(option, value);
×
262
    return this;
×
263
  }
264

265
  /**
266
   * Specifies a child channel option. As the underlying channel as well as network implementation
267
   * may ignore this value applications should consider it a hint.
268
   *
269
   * @since 1.9.0
270
   */
271
  @CanIgnoreReturnValue
272
  public <T> NettyServerBuilder withChildOption(ChannelOption<T> option, T value) {
273
    this.childChannelOptions.put(option, value);
×
274
    return this;
×
275
  }
276

277
  /**
278
   * Provides the boss EventGroupLoop to the server.
279
   *
280
   * <p>It's an optional parameter. If the user has not provided one when the server is built, the
281
   * builder will use the default one which is static.
282
   *
283
   * <p>You must also provide corresponding {@link io.netty.channel.Channel} type using {@link
284
   * #channelType(Class)} and {@link #workerEventLoopGroup(EventLoopGroup)}. For example, {@link
285
   * NioServerSocketChannel} must use {@link io.netty.channel.nio.NioEventLoopGroup} for both boss
286
   * and worker {@link EventLoopGroup}, otherwise your server won't start.
287
   *
288
   * <p>The server won't take ownership of the given EventLoopGroup. It's caller's responsibility
289
   * to shut it down when it's desired.
290
   *
291
   * <p>Grpc uses non-daemon {@link Thread}s by default and thus a {@link io.grpc.Server} will
292
   * continue to run even after the main thread has terminated. However, users have to be cautious
293
   * when providing their own {@link EventLoopGroup}s.
294
   * For example, Netty's {@link EventLoopGroup}s use daemon threads by default
295
   * and thus an application with only daemon threads running besides the main thread will exit as
296
   * soon as the main thread completes.
297
   * A simple solution to this problem is to call {@link io.grpc.Server#awaitTermination()} to
298
   * keep the main thread alive until the server has terminated.
299
   */
300
  @CanIgnoreReturnValue
301
  public NettyServerBuilder bossEventLoopGroup(EventLoopGroup group) {
302
    if (group != null) {
1 ✔
303
      return bossEventLoopGroupPool(new FixedObjectPool<>(group));
1 ✔
304
    }
305
    return bossEventLoopGroupPool(DEFAULT_BOSS_EVENT_LOOP_GROUP_POOL);
×
306
  }
307

308
  @CanIgnoreReturnValue
309
  NettyServerBuilder bossEventLoopGroupPool(
310
      ObjectPool<? extends EventLoopGroup> bossEventLoopGroupPool) {
311
    this.bossEventLoopGroupPool = checkNotNull(bossEventLoopGroupPool, "bossEventLoopGroupPool");
1 ✔
312
    return this;
1 ✔
313
  }
314

315
  /**
316
   * Provides the worker EventGroupLoop to the server.
317
   *
318
   * <p>It's an optional parameter. If the user has not provided one when the server is built, the
319
   * builder will create one.
320
   *
321
   * <p>You must also provide corresponding {@link io.netty.channel.Channel} type using {@link
322
   * #channelType(Class)} and {@link #bossEventLoopGroup(EventLoopGroup)}. For example, {@link
323
   * NioServerSocketChannel} must use {@link io.netty.channel.nio.NioEventLoopGroup} for both boss
324
   * and worker {@link EventLoopGroup}, otherwise your server won't start.
325
   *
326
   * <p>The server won't take ownership of the given EventLoopGroup. It's caller's responsibility
327
   * to shut it down when it's desired.
328
   *
329
   * <p>Grpc uses non-daemon {@link Thread}s by default and thus a {@link io.grpc.Server} will
330
   * continue to run even after the main thread has terminated. However, users have to be cautious
331
   * when providing their own {@link EventLoopGroup}s.
332
   * For example, Netty's {@link EventLoopGroup}s use daemon threads by default
333
   * and thus an application with only daemon threads running besides the main thread will exit as
334
   * soon as the main thread completes.
335
   * A simple solution to this problem is to call {@link io.grpc.Server#awaitTermination()} to
336
   * keep the main thread alive until the server has terminated.
337
   */
338
  @CanIgnoreReturnValue
339
  public NettyServerBuilder workerEventLoopGroup(EventLoopGroup group) {
340
    if (group != null) {
1 ✔
341
      return workerEventLoopGroupPool(new FixedObjectPool<>(group));
1 ✔
342
    }
343
    return workerEventLoopGroupPool(DEFAULT_WORKER_EVENT_LOOP_GROUP_POOL);
×
344
  }
345

346
  @CanIgnoreReturnValue
347
  NettyServerBuilder workerEventLoopGroupPool(
348
      ObjectPool<? extends EventLoopGroup> workerEventLoopGroupPool) {
349
    this.workerEventLoopGroupPool =
1 ✔
350
        checkNotNull(workerEventLoopGroupPool, "workerEventLoopGroupPool");
1 ✔
351
    return this;
1 ✔
352
  }
353

354
  /**
355
   * Force using heap buffer when custom allocator is enabled.
356
   */
357
  void setForceHeapBuffer(boolean value) {
358
    forceHeapBuffer = value;
×
359
  }
×
360

361
  /**
362
   * Sets the TLS context to use for encryption. Providing a context enables encryption. It must
363
   * have been configured with {@link GrpcSslContexts}, but options could have been overridden.
364
   */
365
  @CanIgnoreReturnValue
366
  public NettyServerBuilder sslContext(SslContext sslContext) {
367
    checkState(!freezeProtocolNegotiatorFactory,
1 ✔
368
               "Cannot change security when using ServerCredentials");
369
    if (sslContext != null) {
1 ✔
370
      checkArgument(sslContext.isServer(),
1 ✔
371
          "Client SSL context can not be used for server");
372
      GrpcSslContexts.ensureAlpnAndH2Enabled(sslContext.applicationProtocolNegotiator());
1 ✔
373
      protocolNegotiatorFactory = ProtocolNegotiators.serverTlsFactory(sslContext);
1 ✔
374
    } else {
375
      protocolNegotiatorFactory = ProtocolNegotiators.serverPlaintextFactory();
1 ✔
376
    }
377
    return this;
1 ✔
378
  }
379

380
  /**
381
   * Sets the {@link ProtocolNegotiator} to be used. Overrides the value specified in {@link
382
   * #sslContext(SslContext)}.
383
   */
384
  @CanIgnoreReturnValue
385
  @Internal
386
  public final NettyServerBuilder protocolNegotiator(ProtocolNegotiator protocolNegotiator) {
387
    checkState(!freezeProtocolNegotiatorFactory,
×
388
               "Cannot change security when using ServerCredentials");
389
    this.protocolNegotiatorFactory = ProtocolNegotiators.fixedServerFactory(protocolNegotiator);
×
390
    return this;
×
391
  }
392

393
  void setTracingEnabled(boolean value) {
394
    this.serverImplBuilder.setTracingEnabled(value);
×
395
  }
×
396

397
  void setStatsEnabled(boolean value) {
398
    this.serverImplBuilder.setStatsEnabled(value);
1 ✔
399
  }
1 ✔
400

401
  void setStatsRecordStartedRpcs(boolean value) {
402
    this.serverImplBuilder.setStatsRecordStartedRpcs(value);
×
403
  }
×
404

405
  void setStatsRecordRealTimeMetrics(boolean value) {
406
    this.serverImplBuilder.setStatsRecordRealTimeMetrics(value);
×
407
  }
×
408

409
  /**
410
   * The maximum number of concurrent calls permitted for each incoming connection. Defaults to no
411
   * limit.
412
   */
413
  @CanIgnoreReturnValue
414
  public NettyServerBuilder maxConcurrentCallsPerConnection(int maxCalls) {
415
    checkArgument(maxCalls > 0, "max must be positive: %s", maxCalls);
1 ✔
416
    this.maxConcurrentCallsPerConnection = maxCalls;
×
417
    return this;
×
418
  }
419

420
  /**
421
   * Sets the initial flow control window in bytes. Setting initial flow control window enables auto
422
   * flow control tuning using bandwidth-delay product algorithm. To disable auto flow control
423
   * tuning, use {@link #flowControlWindow(int)}. By default, auto flow control is enabled with
424
   * initial flow control window size of {@link #DEFAULT_FLOW_CONTROL_WINDOW}.
425
   */
426
  @CanIgnoreReturnValue
427
  public NettyServerBuilder initialFlowControlWindow(int initialFlowControlWindow) {
428
    checkArgument(initialFlowControlWindow > 0, "initialFlowControlWindow must be positive");
1 ✔
429
    this.flowControlWindow = initialFlowControlWindow;
1 ✔
430
    this.autoFlowControl = true;
1 ✔
431
    return this;
1 ✔
432
  }
433

434
  /**
435
   * Sets the flow control window in bytes. Setting flowControlWindow disables auto flow control
436
   * tuning; use {@link #initialFlowControlWindow(int)} to enable auto flow control tuning. If not
437
   * called, the default value is {@link #DEFAULT_FLOW_CONTROL_WINDOW}) with auto flow control
438
   * tuning.
439
   */
440
  @CanIgnoreReturnValue
441
  public NettyServerBuilder flowControlWindow(int flowControlWindow) {
442
    checkArgument(flowControlWindow > 0, "flowControlWindow must be positive: %s",
1 ✔
443
        flowControlWindow);
444
    this.flowControlWindow = flowControlWindow;
1 ✔
445
    this.autoFlowControl = false;
1 ✔
446
    return this;
1 ✔
447
  }
448

449
  /**
450
   * Configures an outbound metadata key to use HPACK's never-indexed literal representation.
451
   *
452
   * <p>All values associated with the key's normalized name will be sent as literals and will not
453
   * be added to the peer's HPACK dynamic table. This method is additive and may be called multiple
454
   * times. Configuring the same normalized key name more than once has no additional effect. By
455
   * default, no metadata keys are configured as never indexed.
456
   *
457
   * @since 1.84.0
458
   */
459
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/12976")
460
  @CanIgnoreReturnValue
461
  public NettyServerBuilder neverIndexMetadataKey(Metadata.Key<?> key) {
462
    neverIndexedMetadataKeys.add(AsciiString.of(checkNotNull(key, "key").name()));
1 ✔
463
    return this;
1 ✔
464
  }
465

466
  /**
467
   * Configures outbound metadata keys to use HPACK's never-indexed literal representation.
468
   *
469
   * <p>This method is equivalent to calling {@link #neverIndexMetadataKey} for each key in {@code
470
   * keys}. Duplicate normalized key names are ignored.
471
   *
472
   * @since 1.84.0
473
   */
474
  @ExperimentalApi("https://github.com/grpc/grpc-java/issues/12976")
475
  @CanIgnoreReturnValue
476
  public NettyServerBuilder neverIndexMetadataKeys(
477
      Collection<? extends Metadata.Key<?>> keys) {
478
    Set<AsciiString> normalizedKeys = new HashSet<>();
1 ✔
479
    for (Metadata.Key<?> key : checkNotNull(keys, "keys")) {
1 ✔
480
      normalizedKeys.add(AsciiString.of(checkNotNull(key, "key").name()));
1 ✔
481
    }
1 ✔
482
    neverIndexedMetadataKeys.addAll(normalizedKeys);
1 ✔
483
    return this;
1 ✔
484
  }
485

486
  /**
487
   * Sets the maximum message size allowed to be received on the server. If not called,
488
   * defaults to 4 MiB. The default provides protection to services who haven't considered the
489
   * possibility of receiving large messages while trying to be large enough to not be hit in normal
490
   * usage.
491
   *
492
   * @deprecated Call {@link #maxInboundMessageSize} instead. This method will be removed in a
493
   *     future release.
494
   */
495
  @CanIgnoreReturnValue
496
  @Deprecated
497
  @InlineMe(replacement = "this.maxInboundMessageSize(maxMessageSize)")
498
  public NettyServerBuilder maxMessageSize(int maxMessageSize) {
499
    return maxInboundMessageSize(maxMessageSize);
×
500
  }
501

502
  /** {@inheritDoc} */
503
  @CanIgnoreReturnValue
504
  @Override
505
  public NettyServerBuilder maxInboundMessageSize(int bytes) {
506
    checkArgument(bytes >= 0, "bytes must be non-negative: %s", bytes);
1 ✔
507
    this.maxMessageSize = bytes;
1 ✔
508
    return this;
1 ✔
509
  }
510

511
  /**
512
   * Sets the maximum size of header list allowed to be received. This is cumulative size of the
513
   * headers with some overhead, as defined for
514
   * <a href="http://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2">
515
   * HTTP/2's SETTINGS_MAX_HEADER_LIST_SIZE</a>. The default is 8 KiB.
516
   *
517
   * @deprecated Use {@link #maxInboundMetadataSize} instead
518
   */
519
  @CanIgnoreReturnValue
520
  @Deprecated
521
  @InlineMe(replacement = "this.maxInboundMetadataSize(maxHeaderListSize)")
522
  public NettyServerBuilder maxHeaderListSize(int maxHeaderListSize) {
523
    return maxInboundMetadataSize(maxHeaderListSize);
×
524
  }
525

526
  /**
527
   * Sets the maximum size of metadata allowed to be received. This is cumulative size of the
528
   * entries with some overhead, as defined for
529
   * <a href="http://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2">
530
   * HTTP/2's SETTINGS_MAX_HEADER_LIST_SIZE</a>. The default is 8 KiB.
531
   *
532
   * @param bytes the maximum size of received metadata
533
   * @return this
534
   * @throws IllegalArgumentException if bytes is non-positive
535
   * @since 1.17.0
536
   */
537
  @CanIgnoreReturnValue
538
  @Override
539
  public NettyServerBuilder maxInboundMetadataSize(int bytes) {
540
    checkArgument(bytes > 0, "maxInboundMetadataSize must be positive: %s", bytes);
1 ✔
541
    this.maxHeaderListSize = bytes;
×
542
    // Clear the soft limit setting, by setting soft limit to maxInboundMetadataSize. The
543
    // maxInboundMetadataSize will take precedence over soft limit check.
544
    this.softLimitHeaderListSize = bytes;
×
545
    return this;
×
546
  }
547

548
  /**
549
   * Sets the size of metadata that clients are advised to not exceed. When a metadata with size
550
   * larger than the soft limit is encountered there will be a probability the RPC will fail. The
551
   * chance of failing increases as the metadata size approaches the hard limit.
552
   * {@code Integer.MAX_VALUE} disables the enforcement. The default is implementation-dependent,
553
   * but is not generally less than 8 KiB and may be unlimited.
554
   *
555
   * <p>This is cumulative size of the metadata. The precise calculation is
556
   * implementation-dependent, but implementations are encouraged to follow the calculation used
557
   * for
558
   * <a href="http://httpwg.org/specs/rfc7540.html#rfc.section.6.5.2">HTTP/2's
559
   * SETTINGS_MAX_HEADER_LIST_SIZE</a>. It sums the bytes from each entry's key and value, plus 32
560
   * bytes of overhead per entry.
561
   *
562
   * @param soft the soft size limit of received metadata
563
   * @param max the hard size limit of received metadata
564
   * @return this
565
   * @throws IllegalArgumentException if soft and/or max is non-positive, or max smaller than soft
566
   * @since 1.68.0
567
   */
568
  @CanIgnoreReturnValue
569
  public NettyServerBuilder maxInboundMetadataSize(int soft, int max) {
570
    checkArgument(soft > 0, "softLimitHeaderListSize must be positive: %s", soft);
1 ✔
571
    checkArgument(max > soft,
1 ✔
572
        "maxInboundMetadataSize: %s must be greater than softLimitHeaderListSize: %s", max, soft);
573
    this.softLimitHeaderListSize = soft;
×
574
    this.maxHeaderListSize = max;
×
575
    return this;
×
576
  }
577

578
  /**
579
   * Sets a custom keepalive time, the delay time for sending next keepalive ping. An unreasonably
580
   * small value might be increased, and {@code Long.MAX_VALUE} nano seconds or an unreasonably
581
   * large value will disable keepalive.
582
   *
583
   * @since 1.3.0
584
   */
585
  @CanIgnoreReturnValue
586
  @Override
587
  public NettyServerBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) {
588
    checkArgument(keepAliveTime > 0L, "keepalive time must be positive:%s", keepAliveTime);
1 ✔
589
    keepAliveTimeInNanos = timeUnit.toNanos(keepAliveTime);
×
590
    keepAliveTimeInNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeInNanos);
×
591
    if (keepAliveTimeInNanos >= AS_LARGE_AS_INFINITE) {
×
592
      // Bump keepalive time to infinite. This disables keep alive.
593
      keepAliveTimeInNanos = SERVER_KEEPALIVE_TIME_NANOS_DISABLED;
×
594
    }
595
    return this;
×
596
  }
597

598
  /**
599
   * Sets a custom keepalive timeout, the timeout for keepalive ping requests. An unreasonably small
600
   * value might be increased.
601
   *
602
   * @since 1.3.0
603
   */
604
  @CanIgnoreReturnValue
605
  @Override
606
  public NettyServerBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) {
607
    checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive: %s",
1 ✔
608
        keepAliveTimeout);
609
    keepAliveTimeoutInNanos = timeUnit.toNanos(keepAliveTimeout);
×
610
    keepAliveTimeoutInNanos =
×
611
        KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutInNanos);
×
612
    return this;
×
613
  }
614

615
  /**
616
   * Sets a custom max connection idle time, connection being idle for longer than which will be
617
   * gracefully terminated. Idleness duration is defined since the most recent time the number of
618
   * outstanding RPCs became zero or the connection establishment. An unreasonably small value might
619
   * be increased. {@code Long.MAX_VALUE} nano seconds or an unreasonably large value will disable
620
   * max connection idle.
621
   *
622
   * @since 1.4.0
623
   */
624
  @CanIgnoreReturnValue
625
  @Override
626
  public NettyServerBuilder maxConnectionIdle(long maxConnectionIdle, TimeUnit timeUnit) {
627
    checkArgument(maxConnectionIdle > 0L, "max connection idle must be positive: %s",
1 ✔
628
        maxConnectionIdle);
629
    maxConnectionIdleInNanos = timeUnit.toNanos(maxConnectionIdle);
×
630
    if (maxConnectionIdleInNanos >= AS_LARGE_AS_INFINITE) {
×
631
      maxConnectionIdleInNanos = MAX_CONNECTION_IDLE_NANOS_DISABLED;
×
632
    }
633
    if (maxConnectionIdleInNanos < MIN_MAX_CONNECTION_IDLE_NANO) {
×
634
      maxConnectionIdleInNanos = MIN_MAX_CONNECTION_IDLE_NANO;
×
635
    }
636
    return this;
×
637
  }
638

639
  /**
640
   * Sets a custom max connection age, connection lasting longer than which will be gracefully
641
   * terminated. An unreasonably small value might be increased.  A random jitter of +/-10% will be
642
   * added to it. {@code Long.MAX_VALUE} nano seconds or an unreasonably large value will disable
643
   * max connection age.
644
   *
645
   * @since 1.3.0
646
   */
647
  @CanIgnoreReturnValue
648
  @Override
649
  public NettyServerBuilder maxConnectionAge(long maxConnectionAge, TimeUnit timeUnit) {
650
    checkArgument(maxConnectionAge > 0L, "max connection age must be positive: %s",
1 ✔
651
        maxConnectionAge);
652
    maxConnectionAgeInNanos = timeUnit.toNanos(maxConnectionAge);
×
653
    if (maxConnectionAgeInNanos >= AS_LARGE_AS_INFINITE) {
×
654
      maxConnectionAgeInNanos = MAX_CONNECTION_AGE_NANOS_DISABLED;
×
655
    }
656
    if (maxConnectionAgeInNanos < MIN_MAX_CONNECTION_AGE_NANO) {
×
657
      maxConnectionAgeInNanos = MIN_MAX_CONNECTION_AGE_NANO;
×
658
    }
659
    return this;
×
660
  }
661

662
  /**
663
   * Sets a custom grace time for the graceful connection termination. Once the max connection age
664
   * is reached, RPCs have the grace time to complete. RPCs that do not complete in time will be
665
   * cancelled, allowing the connection to terminate. {@code Long.MAX_VALUE} nano seconds or an
666
   * unreasonably large value are considered infinite.
667
   *
668
   * @see #maxConnectionAge(long, TimeUnit)
669
   * @since 1.3.0
670
   */
671
  @CanIgnoreReturnValue
672
  @Override
673
  public NettyServerBuilder maxConnectionAgeGrace(long maxConnectionAgeGrace, TimeUnit timeUnit) {
674
    checkArgument(maxConnectionAgeGrace >= 0L, "max connection age grace must be non-negative: %s",
1 ✔
675
        maxConnectionAgeGrace);
676
    maxConnectionAgeGraceInNanos = timeUnit.toNanos(maxConnectionAgeGrace);
×
677
    if (maxConnectionAgeGraceInNanos >= AS_LARGE_AS_INFINITE) {
×
678
      maxConnectionAgeGraceInNanos = MAX_CONNECTION_AGE_GRACE_NANOS_INFINITE;
×
679
    }
680
    return this;
×
681
  }
682

683
  /**
684
   * Specify the most aggressive keep-alive time clients are permitted to configure. The server will
685
   * try to detect clients exceeding this rate and when detected will forcefully close the
686
   * connection. The default is 5 minutes.
687
   *
688
   * <p>Even though a default is defined that allows some keep-alives, clients must not use
689
   * keep-alive without approval from the service owner. Otherwise, they may experience failures in
690
   * the future if the service becomes more restrictive. When unthrottled, keep-alives can cause a
691
   * significant amount of traffic and CPU usage, so clients and servers should be conservative in
692
   * what they use and accept.
693
   *
694
   * @see #permitKeepAliveWithoutCalls(boolean)
695
   * @since 1.3.0
696
   */
697
  @CanIgnoreReturnValue
698
  @Override
699
  public NettyServerBuilder permitKeepAliveTime(long keepAliveTime, TimeUnit timeUnit) {
700
    checkArgument(keepAliveTime >= 0, "permit keepalive time must be non-negative: %s",
1 ✔
701
        keepAliveTime);
702
    permitKeepAliveTimeInNanos = timeUnit.toNanos(keepAliveTime);
×
703
    return this;
×
704
  }
705

706
  /**
707
   * Sets whether to allow clients to send keep-alive HTTP/2 PINGs even if there are no outstanding
708
   * RPCs on the connection. Defaults to {@code false}.
709
   *
710
   * @see #permitKeepAliveTime(long, TimeUnit)
711
   * @since 1.3.0
712
   */
713
  @CanIgnoreReturnValue
714
  @Override
715
  public NettyServerBuilder permitKeepAliveWithoutCalls(boolean permit) {
716
    permitKeepAliveWithoutCalls = permit;
×
717
    return this;
×
718
  }
719

720
  /**
721
   * Limits the rate of incoming RST_STREAM frames per connection to maxRstStream per
722
   * secondsPerWindow. When exceeded on a connection, the connection is closed. This can reduce the
723
   * impact of an attacker continually resetting RPCs before they complete, when combined with TLS
724
   * and {@link #maxConcurrentCallsPerConnection(int)}.
725
   *
726
   * <p>gRPC clients send RST_STREAM when they cancel RPCs, so some RST_STREAMs are normal and
727
   * setting this too low can cause errors for legimitate clients.
728
   *
729
   * <p>By default there is no limit.
730
   *
731
   * @param maxRstStream the positive limit of RST_STREAM frames per connection per period, or
732
   *     {@code Integer.MAX_VALUE} for unlimited
733
   * @param secondsPerWindow the positive number of seconds per period
734
   */
735
  @CanIgnoreReturnValue
736
  public NettyServerBuilder maxRstFramesPerWindow(int maxRstStream, int secondsPerWindow) {
737
    checkArgument(maxRstStream > 0, "maxRstStream must be positive");
×
738
    checkArgument(secondsPerWindow > 0, "secondsPerWindow must be positive");
×
739
    if (maxRstStream == Integer.MAX_VALUE) {
×
740
      maxRstStream = MAX_RST_COUNT_DISABLED;
×
741
    }
742
    this.maxRstCount = maxRstStream;
×
743
    this.maxRstPeriodNanos = TimeUnit.SECONDS.toNanos(secondsPerWindow);
×
744
    return this;
×
745
  }
746

747
  /** Sets the EAG attributes available to protocol negotiators. Not for general use. */
748
  void eagAttributes(Attributes eagAttributes) {
749
    this.eagAttributes = checkNotNull(eagAttributes, "eagAttributes");
1 ✔
750
  }
1 ✔
751

752
  @VisibleForTesting
753
  NettyServer buildTransportServers(
754
      List<? extends ServerStreamTracer.Factory> streamTracerFactories,
755
      MetricRecorder metricRecorder) {
756
    assertEventLoopsAndChannelType();
1 ✔
757

758
    ProtocolNegotiator negotiator = protocolNegotiatorFactory.newNegotiator(
1 ✔
759
        this.serverImplBuilder.getExecutorPool());
1 ✔
760

761
    return new NettyServer(
1 ✔
762
        listenAddresses,
763
        channelFactory,
764
        channelOptions,
765
        childChannelOptions,
766
        bossEventLoopGroupPool,
767
        workerEventLoopGroupPool,
768
        forceHeapBuffer,
769
        negotiator,
770
        streamTracerFactories,
771
        transportTracerFactory,
772
        maxConcurrentCallsPerConnection,
773
        autoFlowControl,
774
        flowControlWindow,
775
        neverIndexedMetadataKeys,
776
        maxMessageSize,
777
        maxHeaderListSize,
778
        softLimitHeaderListSize,
779
        keepAliveTimeInNanos,
780
        keepAliveTimeoutInNanos,
781
        maxConnectionIdleInNanos,
782
        maxConnectionAgeInNanos,
783
        maxConnectionAgeGraceInNanos,
784
        permitKeepAliveWithoutCalls,
785
        permitKeepAliveTimeInNanos,
786
        maxRstCount,
787
        maxRstPeriodNanos,
788
        eagAttributes,
789
        this.serverImplBuilder.getChannelz(),
1 ✔
790
        metricRecorder);
791
  }
792

793
  @VisibleForTesting
794
  void assertEventLoopsAndChannelType() {
795
    boolean allProvided = channelFactory != Utils.DEFAULT_SERVER_CHANNEL_FACTORY
1 ✔
796
        && bossEventLoopGroupPool != DEFAULT_BOSS_EVENT_LOOP_GROUP_POOL
797
        && workerEventLoopGroupPool != DEFAULT_WORKER_EVENT_LOOP_GROUP_POOL;
798
    boolean nonProvided = channelFactory == Utils.DEFAULT_SERVER_CHANNEL_FACTORY
1 ✔
799
        && bossEventLoopGroupPool == DEFAULT_BOSS_EVENT_LOOP_GROUP_POOL
800
        && workerEventLoopGroupPool == DEFAULT_WORKER_EVENT_LOOP_GROUP_POOL;
801
    checkState(
1 ✔
802
        allProvided || nonProvided,
803
        "All of BossEventLoopGroup, WorkerEventLoopGroup and ChannelType should be provided or "
804
            + "neither should be");
805
  }
1 ✔
806

807
  @CanIgnoreReturnValue
808
  NettyServerBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) {
809
    this.transportTracerFactory = transportTracerFactory;
1 ✔
810
    return this;
1 ✔
811
  }
812

813
  @CanIgnoreReturnValue
814
  @Override
815
  public NettyServerBuilder useTransportSecurity(File certChain, File privateKey) {
816
    checkState(!freezeProtocolNegotiatorFactory,
×
817
               "Cannot change security when using ServerCredentials");
818
    SslContext sslContext;
819
    try {
820
      sslContext = GrpcSslContexts.forServer(certChain, privateKey).build();
×
821
    } catch (SSLException e) {
×
822
      // This should likely be some other, easier to catch exception.
823
      throw new RuntimeException(e);
×
824
    }
×
825
    protocolNegotiatorFactory = ProtocolNegotiators.serverTlsFactory(sslContext);
×
826
    return this;
×
827
  }
828

829
  @CanIgnoreReturnValue
830
  @Override
831
  public NettyServerBuilder useTransportSecurity(InputStream certChain, InputStream privateKey) {
832
    checkState(!freezeProtocolNegotiatorFactory,
×
833
               "Cannot change security when using ServerCredentials");
834
    SslContext sslContext;
835
    try {
836
      sslContext = GrpcSslContexts.forServer(certChain, privateKey).build();
×
837
    } catch (SSLException e) {
×
838
      // This should likely be some other, easier to catch exception.
839
      throw new RuntimeException(e);
×
840
    }
×
841
    protocolNegotiatorFactory = ProtocolNegotiators.serverTlsFactory(sslContext);
×
842
    return this;
×
843
  }
844
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc