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

grpc / grpc-java / #19693

14 Feb 2025 09:20PM CUT coverage: 88.626% (-0.008%) from 88.634%
#19693

push

github

ejona86
kokoro: Increase gradle mem in android-interop

To try to aid failure when building android-interop-testing
```
The Daemon will expire after the build after running out of JVM heap space.
The project memory settings are likely not configured or are configured to an insufficient value.
The daemon will restart for the next build, which may increase subsequent build times.
These settings can be adjusted by setting 'org.gradle.jvmargs' in 'gradle.properties'.
The currently configured max heap space is '512 MiB' and the configured max metaspace is '384 MiB'.
...
Exception in thread "Daemon client event forwarder" java.lang.OutOfMemoryError: Java heap space
...
> Task :grpc-android-interop-testing:mergeDexDebug FAILED
ERROR:D8: java.lang.OutOfMemoryError: Java heap space
com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives:
```

34270 of 38668 relevant lines covered (88.63%)

0.89 hits per line

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

84.3
/../netty/src/main/java/io/grpc/netty/Utils.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.checkState;
20
import static io.grpc.internal.GrpcUtil.CONTENT_TYPE_KEY;
21
import static io.grpc.internal.TransportFrameUtil.toHttp2Headers;
22
import static io.grpc.internal.TransportFrameUtil.toRawSerializedHeaders;
23
import static io.netty.channel.ChannelOption.SO_LINGER;
24
import static io.netty.channel.ChannelOption.SO_TIMEOUT;
25
import static io.netty.util.CharsetUtil.UTF_8;
26
import static java.nio.charset.StandardCharsets.US_ASCII;
27

28
import com.google.common.annotations.VisibleForTesting;
29
import com.google.common.base.Preconditions;
30
import com.google.errorprone.annotations.CheckReturnValue;
31
import io.grpc.InternalChannelz;
32
import io.grpc.InternalMetadata;
33
import io.grpc.Metadata;
34
import io.grpc.Status;
35
import io.grpc.internal.GrpcUtil;
36
import io.grpc.internal.SharedResourceHolder.Resource;
37
import io.grpc.internal.TransportTracer;
38
import io.grpc.netty.GrpcHttp2HeadersUtils.GrpcHttp2InboundHeaders;
39
import io.grpc.netty.NettySocketSupport.NativeSocketOptions;
40
import io.netty.buffer.ByteBufAllocator;
41
import io.netty.buffer.PooledByteBufAllocator;
42
import io.netty.buffer.UnpooledByteBufAllocator;
43
import io.netty.channel.Channel;
44
import io.netty.channel.ChannelConfig;
45
import io.netty.channel.ChannelFactory;
46
import io.netty.channel.ChannelOption;
47
import io.netty.channel.EventLoopGroup;
48
import io.netty.channel.ReflectiveChannelFactory;
49
import io.netty.channel.ServerChannel;
50
import io.netty.channel.nio.NioEventLoopGroup;
51
import io.netty.channel.socket.nio.NioServerSocketChannel;
52
import io.netty.channel.socket.nio.NioSocketChannel;
53
import io.netty.handler.codec.DecoderException;
54
import io.netty.handler.codec.http2.Http2Connection;
55
import io.netty.handler.codec.http2.Http2Exception;
56
import io.netty.handler.codec.http2.Http2FlowController;
57
import io.netty.handler.codec.http2.Http2Headers;
58
import io.netty.handler.codec.http2.Http2Stream;
59
import io.netty.util.AsciiString;
60
import io.netty.util.NettyRuntime;
61
import io.netty.util.concurrent.DefaultThreadFactory;
62
import java.io.IOException;
63
import java.lang.reflect.Constructor;
64
import java.nio.channels.ClosedChannelException;
65
import java.nio.channels.UnresolvedAddressException;
66
import java.util.Locale;
67
import java.util.Map;
68
import java.util.concurrent.ThreadFactory;
69
import java.util.concurrent.TimeUnit;
70
import java.util.logging.Level;
71
import java.util.logging.Logger;
72
import javax.annotation.Nullable;
73
import javax.net.ssl.SSLException;
74

75
/**
76
 * Common utility methods.
77
 */
78
class Utils {
79
  private static final Logger logger = Logger.getLogger(Utils.class.getName());
1✔
80

81
  public static final AsciiString STATUS_OK = AsciiString.of("200");
1✔
82
  public static final AsciiString HTTP_METHOD = AsciiString.of(GrpcUtil.HTTP_METHOD);
1✔
83
  public static final AsciiString HTTP_GET_METHOD = AsciiString.of("GET");
1✔
84
  public static final AsciiString HTTPS = AsciiString.of("https");
1✔
85
  public static final AsciiString HTTP = AsciiString.of("http");
1✔
86
  public static final AsciiString CONTENT_TYPE_HEADER = AsciiString.of(CONTENT_TYPE_KEY.name());
1✔
87
  public static final AsciiString CONTENT_TYPE_GRPC = AsciiString.of(GrpcUtil.CONTENT_TYPE_GRPC);
1✔
88
  public static final AsciiString TE_HEADER = AsciiString.of(GrpcUtil.TE_HEADER.name());
1✔
89
  public static final AsciiString TE_TRAILERS = AsciiString.of(GrpcUtil.TE_TRAILERS);
1✔
90
  public static final AsciiString USER_AGENT = AsciiString.of(GrpcUtil.USER_AGENT_KEY.name());
1✔
91
  public static final Resource<EventLoopGroup> NIO_BOSS_EVENT_LOOP_GROUP
1✔
92
      = new DefaultEventLoopGroupResource(1, "grpc-nio-boss-ELG", EventLoopGroupType.NIO);
93
  public static final Resource<EventLoopGroup> NIO_WORKER_EVENT_LOOP_GROUP
1✔
94
      = new DefaultEventLoopGroupResource(0, "grpc-nio-worker-ELG", EventLoopGroupType.NIO);
95
  private static final int HEADER_ENTRY_OVERHEAD = 32;
96
  private static final byte[] binaryHeaderSuffixBytes =
1✔
97
      Metadata.BINARY_HEADER_SUFFIX.getBytes(US_ASCII);
1✔
98
  public static final Resource<EventLoopGroup> DEFAULT_BOSS_EVENT_LOOP_GROUP;
99
  public static final Resource<EventLoopGroup> DEFAULT_WORKER_EVENT_LOOP_GROUP;
100

101
  // This class is initialized on first use, thus provides delayed allocator creation.
102
  private static final class ByteBufAllocatorPreferDirectHolder {
103
    private static final ByteBufAllocator allocator = createByteBufAllocator(true);
1✔
104
  }
105

106
  // This class is initialized on first use, thus provides delayed allocator creation.
107
  private static final class ByteBufAllocatorPreferHeapHolder {
108
    private static final ByteBufAllocator allocator = createByteBufAllocator(false);
1✔
109
  }
110

111
  public static final ChannelFactory<? extends ServerChannel> DEFAULT_SERVER_CHANNEL_FACTORY;
112
  public static final Class<? extends Channel> DEFAULT_CLIENT_CHANNEL_TYPE;
113
  public static final Class<? extends Channel> EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE;
114

115
  @Nullable
116
  private static final Constructor<? extends EventLoopGroup> EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR;
117

118
  static {
119
    // Decide default channel types and EventLoopGroup based on Epoll availability
120
    if (isEpollAvailable()) {
1✔
121
      DEFAULT_CLIENT_CHANNEL_TYPE = epollChannelType();
1✔
122
      EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE = epollDomainSocketChannelType();
1✔
123
      DEFAULT_SERVER_CHANNEL_FACTORY = new ReflectiveChannelFactory<>(epollServerChannelType());
1✔
124
      EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR = epollEventLoopGroupConstructor();
1✔
125
      DEFAULT_BOSS_EVENT_LOOP_GROUP
1✔
126
        = new DefaultEventLoopGroupResource(1, "grpc-default-boss-ELG", EventLoopGroupType.EPOLL);
127
      DEFAULT_WORKER_EVENT_LOOP_GROUP
1✔
128
        = new DefaultEventLoopGroupResource(0,"grpc-default-worker-ELG", EventLoopGroupType.EPOLL);
129
    } else {
130
      logger.log(Level.FINE, "Epoll is not available, using Nio.", getEpollUnavailabilityCause());
1✔
131
      DEFAULT_SERVER_CHANNEL_FACTORY = nioServerChannelFactory();
1✔
132
      DEFAULT_CLIENT_CHANNEL_TYPE = NioSocketChannel.class;
1✔
133
      EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE = null;
1✔
134
      DEFAULT_BOSS_EVENT_LOOP_GROUP = NIO_BOSS_EVENT_LOOP_GROUP;
1✔
135
      DEFAULT_WORKER_EVENT_LOOP_GROUP = NIO_WORKER_EVENT_LOOP_GROUP;
1✔
136
      EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR = null;
1✔
137
    }
138
  }
1✔
139

140
  public static ByteBufAllocator getByteBufAllocator(boolean forceHeapBuffer) {
141
    if (Boolean.parseBoolean(
1✔
142
            System.getProperty("io.grpc.netty.useCustomAllocator", "true"))) {
1✔
143

144
      String allocType = System.getProperty("io.netty.allocator.type", "pooled");
1✔
145
      if (allocType.toLowerCase(Locale.ROOT).equals("unpooled")) {
1✔
146
        logger.log(Level.FINE, "Using unpooled allocator");
1✔
147
        return UnpooledByteBufAllocator.DEFAULT;
1✔
148
      }
149

150
      boolean defaultPreferDirect = PooledByteBufAllocator.defaultPreferDirect();
1✔
151
      logger.log(
1✔
152
          Level.FINE,
153
          "Using custom allocator: forceHeapBuffer={0}, defaultPreferDirect={1}",
154
          new Object[] { forceHeapBuffer, defaultPreferDirect });
1✔
155
      if (forceHeapBuffer || !defaultPreferDirect) {
1✔
156
        return ByteBufAllocatorPreferHeapHolder.allocator;
1✔
157
      } else {
158
        return ByteBufAllocatorPreferDirectHolder.allocator;
1✔
159
      }
160
    } else {
161
      logger.log(Level.FINE, "Using default allocator");
×
162
      return ByteBufAllocator.DEFAULT;
×
163
    }
164
  }
165

166
  private static ByteBufAllocator createByteBufAllocator(boolean preferDirect) {
167
    int maxOrder;
168
    logger.log(Level.FINE, "Creating allocator, preferDirect=" + preferDirect);
1✔
169
    if (System.getProperty("io.netty.allocator.maxOrder") == null) {
1✔
170
      // See the implementation of PooledByteBufAllocator.  DEFAULT_MAX_ORDER in there is
171
      // 11, which makes chunk size to be 8192 << 11 = 16 MiB.  We want the chunk size to be
172
      // 2MiB, thus reducing the maxOrder to 8.
173
      maxOrder = 8;
1✔
174
      logger.log(Level.FINE, "Forcing maxOrder=" + maxOrder);
1✔
175
    } else {
176
      maxOrder = PooledByteBufAllocator.defaultMaxOrder();
×
177
      logger.log(Level.FINE, "Using default maxOrder=" + maxOrder);
×
178
    }
179
    return new PooledByteBufAllocator(
1✔
180
        preferDirect,
181
        PooledByteBufAllocator.defaultNumHeapArena(),
1✔
182
        // Assuming neither gRPC nor netty are using allocator.directBuffer() to request
183
        // specifically for direct buffers, which is true as I just checked, setting arenas to 0
184
        // will make sure no direct buffer is ever created.
185
        preferDirect ? PooledByteBufAllocator.defaultNumDirectArena() : 0,
1✔
186
        PooledByteBufAllocator.defaultPageSize(),
1✔
187
        maxOrder,
188
        PooledByteBufAllocator.defaultSmallCacheSize(),
1✔
189
        PooledByteBufAllocator.defaultNormalCacheSize(),
1✔
190
        PooledByteBufAllocator.defaultUseCacheForAllThreads());
1✔
191
  }
192

193
  public static Metadata convertHeaders(Http2Headers http2Headers) {
194
    if (http2Headers instanceof GrpcHttp2InboundHeaders) {
1✔
195
      GrpcHttp2InboundHeaders h = (GrpcHttp2InboundHeaders) http2Headers;
1✔
196
      return InternalMetadata.newMetadata(h.numHeaders(), h.namesAndValues());
1✔
197
    }
198
    return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
1✔
199
  }
200

201
  public static int getH2HeadersSize(Http2Headers http2Headers) {
202
    if (http2Headers instanceof GrpcHttp2InboundHeaders) {
1✔
203
      GrpcHttp2InboundHeaders h = (GrpcHttp2InboundHeaders) http2Headers;
1✔
204
      int size = 0;
1✔
205
      for (int i = 0; i < h.numHeaders(); i++) {
1✔
206
        size += h.namesAndValues()[2 * i].length;
1✔
207
        size +=
1✔
208
            maybeAddBinaryHeaderOverhead(h.namesAndValues()[2 * i], h.namesAndValues()[2 * i + 1]);
1✔
209
        size += HEADER_ENTRY_OVERHEAD;
1✔
210
      }
211
      return size;
1✔
212
    }
213

214
    // the binary header is not decoded yet, no need to add overhead.
215
    int size = 0;
×
216
    for (Map.Entry<CharSequence, CharSequence> entry : http2Headers) {
×
217
      size += entry.getKey().length();
×
218
      size += entry.getValue().length();
×
219
      size += HEADER_ENTRY_OVERHEAD;
×
220
    }
×
221
    return size;
×
222
  }
223

224
  private static int maybeAddBinaryHeaderOverhead(byte[] name, byte[] value) {
225
    if (endsWith(name, binaryHeaderSuffixBytes)) {
1✔
226
      return value.length * 4 / 3;
1✔
227
    }
228
    return value.length;
1✔
229
  }
230

231
  private static boolean endsWith(byte[] bytes, byte[] suffix) {
232
    if (bytes == null || suffix == null || bytes.length < suffix.length) {
1✔
233
      return false;
1✔
234
    }
235

236
    for (int i = 0; i < suffix.length; i++) {
1✔
237
      if (bytes[bytes.length - suffix.length + i] != suffix[i]) {
1✔
238
        return false;
1✔
239
      }
240
    }
241

242
    return true;
1✔
243
  }
244

245
  public static boolean shouldRejectOnMetadataSizeSoftLimitExceeded(
246
      int h2HeadersSize, int softLimitHeaderListSize, int maxHeaderListSize) {
247
    if (h2HeadersSize < softLimitHeaderListSize) {
1✔
248
      return false;
1✔
249
    }
250
    double failProbability =
1✔
251
        (double) (h2HeadersSize - softLimitHeaderListSize) / (double) (maxHeaderListSize
252
            - softLimitHeaderListSize);
253
    return Math.random() < failProbability;
1✔
254
  }
255

256
  @CheckReturnValue
257
  private static byte[][] convertHeadersToArray(Http2Headers http2Headers) {
258
    // The Netty AsciiString class is really just a wrapper around a byte[] and supports
259
    // arbitrary binary data, not just ASCII.
260
    byte[][] headerValues = new byte[http2Headers.size() * 2][];
1✔
261
    int i = 0;
1✔
262
    for (Map.Entry<CharSequence, CharSequence> entry : http2Headers) {
1✔
263
      headerValues[i++] = bytes(entry.getKey());
1✔
264
      headerValues[i++] = bytes(entry.getValue());
1✔
265
    }
1✔
266
    return toRawSerializedHeaders(headerValues);
1✔
267
  }
268

269
  private static byte[] bytes(CharSequence seq) {
270
    if (seq instanceof AsciiString) {
1✔
271
      // Fast path - sometimes copy.
272
      AsciiString str = (AsciiString) seq;
1✔
273
      return str.isEntireArrayUsed() ? str.array() : str.toByteArray();
1✔
274
    }
275
    // Slow path - copy.
276
    return seq.toString().getBytes(UTF_8);
1✔
277
  }
278

279
  public static Http2Headers convertClientHeaders(Metadata headers,
280
      AsciiString scheme,
281
      AsciiString defaultPath,
282
      AsciiString authority,
283
      AsciiString method,
284
      AsciiString userAgent) {
285
    Preconditions.checkNotNull(defaultPath, "defaultPath");
1✔
286
    Preconditions.checkNotNull(authority, "authority");
1✔
287
    Preconditions.checkNotNull(method, "method");
1✔
288

289
    // Discard any application supplied duplicates of the reserved headers
290
    headers.discardAll(CONTENT_TYPE_KEY);
1✔
291
    headers.discardAll(GrpcUtil.TE_HEADER);
1✔
292
    headers.discardAll(GrpcUtil.USER_AGENT_KEY);
1✔
293

294
    return GrpcHttp2OutboundHeaders.clientRequestHeaders(
1✔
295
        toHttp2Headers(headers),
1✔
296
        authority,
297
        defaultPath,
298
        method,
299
        scheme,
300
        userAgent);
301
  }
302

303
  public static Http2Headers convertServerHeaders(Metadata headers) {
304
    // Discard any application supplied duplicates of the reserved headers
305
    headers.discardAll(CONTENT_TYPE_KEY);
1✔
306
    headers.discardAll(GrpcUtil.TE_HEADER);
1✔
307
    headers.discardAll(GrpcUtil.USER_AGENT_KEY);
1✔
308

309
    return GrpcHttp2OutboundHeaders.serverResponseHeaders(toHttp2Headers(headers));
1✔
310
  }
311

312
  public static Metadata convertTrailers(Http2Headers http2Headers) {
313
    if (http2Headers instanceof GrpcHttp2InboundHeaders) {
1✔
314
      GrpcHttp2InboundHeaders h = (GrpcHttp2InboundHeaders) http2Headers;
1✔
315
      return InternalMetadata.newMetadata(h.numHeaders(), h.namesAndValues());
1✔
316
    }
317
    return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
1✔
318
  }
319

320
  public static Http2Headers convertTrailers(Metadata trailers, boolean headersSent) {
321
    if (!headersSent) {
1✔
322
      return convertServerHeaders(trailers);
1✔
323
    }
324
    return GrpcHttp2OutboundHeaders.serverResponseTrailers(toHttp2Headers(trailers));
1✔
325
  }
326

327
  public static Status statusFromThrowable(Throwable t) {
328
    Status s = Status.fromThrowable(t);
1✔
329
    if (s.getCode() != Status.Code.UNKNOWN) {
1✔
330
      return s;
1✔
331
    }
332
    if (t instanceof ClosedChannelException) {
1✔
333
      if (t.getCause() != null) {
1✔
334
        // If the remote closes the connection while the event loop is processing, then a write or
335
        // flush can be the first operation to notice the closure. Those exceptions are a
336
        // ClosedChannelException, with a cause that provides more information, which is exactly
337
        // what we'd hope for.
338
        return Status.UNAVAILABLE.withDescription("channel closed").withCause(t);
×
339
      }
340
      // ClosedChannelException is used for all operations after the Netty channel is closed. But it
341
      // doesn't have the original closure information. Proper error processing requires remembering
342
      // the error that occurred before this one and using it instead.
343
      //
344
      // Netty uses an exception that has no stack trace, while we would never hope to show this to
345
      // users, if it happens having the extra information may provide a small hint of where to
346
      // look.
347
      ClosedChannelException extraT = new ClosedChannelException();
1✔
348
      extraT.initCause(t);
1✔
349
      return Status.UNKNOWN.withDescription("channel closed").withCause(extraT);
1✔
350
    }
351
    if (t instanceof DecoderException && t.getCause() instanceof SSLException) {
1✔
352
      return Status.UNAVAILABLE.withDescription("ssl exception").withCause(t);
1✔
353
    }
354
    if (t instanceof IOException) {
1✔
355
      return Status.UNAVAILABLE.withDescription("io exception").withCause(t);
1✔
356
    }
357
    if (t instanceof UnresolvedAddressException) {
1✔
358
      return Status.UNAVAILABLE.withDescription("unresolved address").withCause(t);
1✔
359
    }
360
    if (t instanceof Http2Exception) {
1✔
361
      return Status.INTERNAL.withDescription("http2 exception").withCause(t);
1✔
362
    }
363
    return s;
1✔
364
  }
365

366
  @VisibleForTesting
367
  static boolean isEpollAvailable() {
368
    try {
369
      return (boolean) (Boolean)
1✔
370
          Class
371
              .forName("io.netty.channel.epoll.Epoll")
1✔
372
              .getDeclaredMethod("isAvailable")
1✔
373
              .invoke(null);
1✔
374
    } catch (ClassNotFoundException e) {
1✔
375
      // this is normal if netty-epoll runtime dependency doesn't exist.
376
      return false;
1✔
377
    } catch (Exception e) {
×
378
      throw new RuntimeException("Exception while checking Epoll availability", e);
×
379
    }
380
  }
381

382
  private static Throwable getEpollUnavailabilityCause() {
383
    try {
384
      return (Throwable)
1✔
385
          Class
386
              .forName("io.netty.channel.epoll.Epoll")
×
387
              .getDeclaredMethod("unavailabilityCause")
×
388
              .invoke(null);
×
389
    } catch (Exception e) {
1✔
390
      return e;
1✔
391
    }
392
  }
393

394
  // Must call when epoll is available
395
  private static Class<? extends Channel> epollChannelType() {
396
    try {
397
      Class<? extends Channel> channelType = Class
1✔
398
          .forName("io.netty.channel.epoll.EpollSocketChannel").asSubclass(Channel.class);
1✔
399
      return channelType;
1✔
400
    } catch (ClassNotFoundException e) {
×
401
      throw new RuntimeException("Cannot load EpollSocketChannel", e);
×
402
    }
403
  }
404

405
  // Must call when epoll is available
406
  private static Class<? extends Channel> epollDomainSocketChannelType() {
407
    try {
408
      Class<? extends Channel> channelType = Class
1✔
409
          .forName("io.netty.channel.epoll.EpollDomainSocketChannel").asSubclass(Channel.class);
1✔
410
      return channelType;
1✔
411
    } catch (ClassNotFoundException e) {
×
412
      throw new RuntimeException("Cannot load EpollDomainSocketChannel", e);
×
413
    }
414
  }
415

416
  // Must call when epoll is available
417
  private static Constructor<? extends EventLoopGroup> epollEventLoopGroupConstructor() {
418
    try {
419
      return Class
1✔
420
          .forName("io.netty.channel.epoll.EpollEventLoopGroup").asSubclass(EventLoopGroup.class)
1✔
421
          .getConstructor(Integer.TYPE, ThreadFactory.class);
1✔
422
    } catch (ClassNotFoundException e) {
×
423
      throw new RuntimeException("Cannot load EpollEventLoopGroup", e);
×
424
    } catch (NoSuchMethodException e) {
×
425
      throw new RuntimeException("EpollEventLoopGroup constructor not found", e);
×
426
    }
427
  }
428

429
  // Must call when epoll is available
430
  private static Class<? extends ServerChannel> epollServerChannelType() {
431
    try {
432
      Class<? extends ServerChannel> serverSocketChannel =
1✔
433
          Class
434
              .forName("io.netty.channel.epoll.EpollServerSocketChannel")
1✔
435
              .asSubclass(ServerChannel.class);
1✔
436
      return serverSocketChannel;
1✔
437
    } catch (ClassNotFoundException e) {
×
438
      throw new RuntimeException("Cannot load EpollServerSocketChannel", e);
×
439
    }
440
  }
441

442
  private static EventLoopGroup createEpollEventLoopGroup(
443
      int parallelism,
444
      ThreadFactory threadFactory) {
445
    checkState(EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR != null, "Epoll is not available");
1✔
446

447
    try {
448
      return EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR
1✔
449
          .newInstance(parallelism, threadFactory);
1✔
450
    } catch (Exception e) {
×
451
      throw new RuntimeException("Cannot create Epoll EventLoopGroup", e);
×
452
    }
453
  }
454

455
  private static ChannelFactory<ServerChannel> nioServerChannelFactory() {
456
    return new ChannelFactory<ServerChannel>() {
1✔
457
      @Override
458
      public ServerChannel newChannel() {
459
        return new NioServerSocketChannel();
1✔
460
      }
461
    };
462
  }
463

464
  /**
465
   * Returns TCP_USER_TIMEOUT channel option for Epoll channel if Epoll is available, otherwise
466
   * null.
467
   */
468
  @Nullable
469
  static ChannelOption<Integer> maybeGetTcpUserTimeoutOption() {
470
    return getEpollChannelOption("TCP_USER_TIMEOUT");
1✔
471
  }
472

473
  @Nullable
474
  @SuppressWarnings("unchecked")
475
  private static <T> ChannelOption<T> getEpollChannelOption(String optionName) {
476
    if (isEpollAvailable()) {
1✔
477
      try {
478
        return
1✔
479
            (ChannelOption<T>) Class.forName("io.netty.channel.epoll.EpollChannelOption")
1✔
480
                .getField(optionName)
1✔
481
                .get(null);
1✔
482
      } catch (Exception e) {
×
483
        throw new RuntimeException("ChannelOption(" + optionName + ") is not available", e);
×
484
      }
485
    }
486
    return null;
×
487
  }
488

489
  private static final class DefaultEventLoopGroupResource implements Resource<EventLoopGroup> {
490
    private final String name;
491
    private final int numEventLoops;
492
    private final EventLoopGroupType eventLoopGroupType;
493

494
    DefaultEventLoopGroupResource(
495
        int numEventLoops, String name, EventLoopGroupType eventLoopGroupType) {
1✔
496
      this.name = name;
1✔
497
      // See the implementation of MultithreadEventLoopGroup.  DEFAULT_EVENT_LOOP_THREADS there
498
      // defaults to NettyRuntime.availableProcessors() * 2.  We don't think we need that many
499
      // threads.  The overhead of a thread includes file descriptors and at least one chunk
500
      // allocation from PooledByteBufAllocator.  Here we reduce the default number of threads by
501
      // half.
502
      if (numEventLoops == 0 && System.getProperty("io.netty.eventLoopThreads") == null) {
1✔
503
        this.numEventLoops = NettyRuntime.availableProcessors();
1✔
504
      } else {
505
        this.numEventLoops = numEventLoops;
1✔
506
      }
507
      this.eventLoopGroupType = eventLoopGroupType;
1✔
508
    }
1✔
509

510
    @Override
511
    public EventLoopGroup create() {
512
      // Use Netty's DefaultThreadFactory in order to get the benefit of FastThreadLocal.
513
      ThreadFactory threadFactory = new DefaultThreadFactory(name, /* daemon= */ true);
1✔
514
      switch (eventLoopGroupType) {
1✔
515
        case NIO:
516
          return new NioEventLoopGroup(numEventLoops, threadFactory);
1✔
517
        case EPOLL:
518
          return createEpollEventLoopGroup(numEventLoops, threadFactory);
1✔
519
        default:
520
          throw new AssertionError("Unknown/Unsupported EventLoopGroupType: " + eventLoopGroupType);
×
521
      }
522
    }
523

524
    @Override
525
    public void close(EventLoopGroup instance) {
526
      instance.shutdownGracefully(0, 0, TimeUnit.SECONDS);
1✔
527
    }
1✔
528

529
    @Override
530
    public String toString() {
531
      return name;
×
532
    }
533
  }
534

535
  static final class FlowControlReader implements TransportTracer.FlowControlReader {
536
    private final Http2Stream connectionStream;
537
    private final Http2FlowController local;
538
    private final Http2FlowController remote;
539

540
    FlowControlReader(Http2Connection connection) {
1✔
541
      // 'local' in Netty is the _controller_ that controls inbound data. 'local' in Channelz is
542
      // the _present window_ provided by the remote that allows data to be sent. They are
543
      // opposites.
544
      local = connection.remote().flowController();
1✔
545
      remote = connection.local().flowController();
1✔
546
      connectionStream = connection.connectionStream();
1✔
547
    }
1✔
548

549
    @Override
550
    public TransportTracer.FlowControlWindows read() {
551
      return new TransportTracer.FlowControlWindows(
1✔
552
          local.windowSize(connectionStream),
1✔
553
          remote.windowSize(connectionStream));
1✔
554
    }
555
  }
556

557
  static InternalChannelz.SocketOptions getSocketOptions(Channel channel) {
558
    ChannelConfig config = channel.config();
1✔
559
    InternalChannelz.SocketOptions.Builder b = new InternalChannelz.SocketOptions.Builder();
1✔
560

561
    // The API allows returning null but not sure if it can happen in practice.
562
    // Let's be paranoid and do null checking just in case.
563
    Integer lingerSeconds = config.getOption(SO_LINGER);
1✔
564
    if (lingerSeconds != null) {
1✔
565
      b.setSocketOptionLingerSeconds(lingerSeconds);
1✔
566
    }
567

568
    Integer timeoutMillis = config.getOption(SO_TIMEOUT);
1✔
569
    if (timeoutMillis != null) {
1✔
570
      // in java, SO_TIMEOUT only applies to receiving
571
      b.setSocketOptionTimeoutMillis(timeoutMillis);
1✔
572
    }
573

574
    for (Map.Entry<ChannelOption<?>, Object> opt : config.getOptions().entrySet()) {
1✔
575
      ChannelOption<?> key = opt.getKey();
1✔
576
      // Constants are pooled, so there should only be one instance of each constant
577
      if (key.equals(SO_LINGER) || key.equals(SO_TIMEOUT)) {
1✔
578
        continue;
1✔
579
      }
580
      Object value = opt.getValue();
1✔
581
      // zpencer: Can a netty option be null?
582
      b.addOption(key.name(), String.valueOf(value));
1✔
583
    }
1✔
584

585
    NativeSocketOptions nativeOptions
1✔
586
        = NettySocketSupport.getNativeSocketOptions(channel);
1✔
587
    if (nativeOptions != null) {
1✔
588
      b.setTcpInfo(nativeOptions.tcpInfo); // may be null
×
589
      for (Map.Entry<String, String> entry : nativeOptions.otherInfo.entrySet()) {
×
590
        b.addOption(entry.getKey(), entry.getValue());
×
591
      }
×
592
    }
593
    return b.build();
1✔
594
  }
595

596
  private enum EventLoopGroupType {
1✔
597
    NIO,
1✔
598
    EPOLL
1✔
599
  }
600

601
  private Utils() {
602
    // Prevents instantiation
603
  }
604
}
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