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

grpc / grpc-java / #18820

06 Sep 2023 06:12PM UTC coverage: 88.304% (+0.02%) from 88.283%
#18820

push

github-actions

temawi
core: only try to resolve InternalCensusStatsAccessor once

30336 of 34354 relevant lines covered (88.3%)

0.88 hits per line

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

84.39
/../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

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

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

78
  public static final AsciiString STATUS_OK = AsciiString.of("200");
1✔
79
  public static final AsciiString HTTP_METHOD = AsciiString.of(GrpcUtil.HTTP_METHOD);
1✔
80
  public static final AsciiString HTTP_GET_METHOD = AsciiString.of("GET");
1✔
81
  public static final AsciiString HTTPS = AsciiString.of("https");
1✔
82
  public static final AsciiString HTTP = AsciiString.of("http");
1✔
83
  public static final AsciiString CONTENT_TYPE_HEADER = AsciiString.of(CONTENT_TYPE_KEY.name());
1✔
84
  public static final AsciiString CONTENT_TYPE_GRPC = AsciiString.of(GrpcUtil.CONTENT_TYPE_GRPC);
1✔
85
  public static final AsciiString TE_HEADER = AsciiString.of(GrpcUtil.TE_HEADER.name());
1✔
86
  public static final AsciiString TE_TRAILERS = AsciiString.of(GrpcUtil.TE_TRAILERS);
1✔
87
  public static final AsciiString USER_AGENT = AsciiString.of(GrpcUtil.USER_AGENT_KEY.name());
1✔
88
  public static final Resource<EventLoopGroup> NIO_BOSS_EVENT_LOOP_GROUP
1✔
89
      = new DefaultEventLoopGroupResource(1, "grpc-nio-boss-ELG", EventLoopGroupType.NIO);
90
  public static final Resource<EventLoopGroup> NIO_WORKER_EVENT_LOOP_GROUP
1✔
91
      = new DefaultEventLoopGroupResource(0, "grpc-nio-worker-ELG", EventLoopGroupType.NIO);
92

93
  public static final Resource<EventLoopGroup> DEFAULT_BOSS_EVENT_LOOP_GROUP;
94
  public static final Resource<EventLoopGroup> DEFAULT_WORKER_EVENT_LOOP_GROUP;
95

96
  // This class is initialized on first use, thus provides delayed allocator creation.
97
  private static final class ByteBufAllocatorPreferDirectHolder {
98
    private static final ByteBufAllocator allocator = createByteBufAllocator(true);
1✔
99
  }
100

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

106
  public static final ChannelFactory<? extends ServerChannel> DEFAULT_SERVER_CHANNEL_FACTORY;
107
  public static final Class<? extends Channel> DEFAULT_CLIENT_CHANNEL_TYPE;
108
  public static final Class<? extends Channel> EPOLL_DOMAIN_CLIENT_CHANNEL_TYPE;
109

110
  @Nullable
111
  private static final Constructor<? extends EventLoopGroup> EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR;
112

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

135
  public static ByteBufAllocator getByteBufAllocator(boolean forceHeapBuffer) {
136
    if (Boolean.parseBoolean(
1✔
137
            System.getProperty("io.grpc.netty.useCustomAllocator", "true"))) {
1✔
138
      boolean defaultPreferDirect = PooledByteBufAllocator.defaultPreferDirect();
1✔
139
      logger.log(
1✔
140
          Level.FINE,
141
          "Using custom allocator: forceHeapBuffer={0}, defaultPreferDirect={1}",
142
          new Object[] { forceHeapBuffer, defaultPreferDirect });
1✔
143
      if (forceHeapBuffer || !defaultPreferDirect) {
1✔
144
        return ByteBufAllocatorPreferHeapHolder.allocator;
×
145
      } else {
146
        return ByteBufAllocatorPreferDirectHolder.allocator;
1✔
147
      }
148
    } else {
149
      logger.log(Level.FINE, "Using default allocator");
×
150
      return ByteBufAllocator.DEFAULT;
×
151
    }
152
  }
153

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

181
  public static Metadata convertHeaders(Http2Headers http2Headers) {
182
    if (http2Headers instanceof GrpcHttp2InboundHeaders) {
1✔
183
      GrpcHttp2InboundHeaders h = (GrpcHttp2InboundHeaders) http2Headers;
1✔
184
      return InternalMetadata.newMetadata(h.numHeaders(), h.namesAndValues());
1✔
185
    }
186
    return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
1✔
187
  }
188

189
  @CheckReturnValue
190
  private static byte[][] convertHeadersToArray(Http2Headers http2Headers) {
191
    // The Netty AsciiString class is really just a wrapper around a byte[] and supports
192
    // arbitrary binary data, not just ASCII.
193
    byte[][] headerValues = new byte[http2Headers.size() * 2][];
1✔
194
    int i = 0;
1✔
195
    for (Map.Entry<CharSequence, CharSequence> entry : http2Headers) {
1✔
196
      headerValues[i++] = bytes(entry.getKey());
1✔
197
      headerValues[i++] = bytes(entry.getValue());
1✔
198
    }
1✔
199
    return toRawSerializedHeaders(headerValues);
1✔
200
  }
201

202
  private static byte[] bytes(CharSequence seq) {
203
    if (seq instanceof AsciiString) {
1✔
204
      // Fast path - sometimes copy.
205
      AsciiString str = (AsciiString) seq;
1✔
206
      return str.isEntireArrayUsed() ? str.array() : str.toByteArray();
1✔
207
    }
208
    // Slow path - copy.
209
    return seq.toString().getBytes(UTF_8);
1✔
210
  }
211

212
  public static Http2Headers convertClientHeaders(Metadata headers,
213
      AsciiString scheme,
214
      AsciiString defaultPath,
215
      AsciiString authority,
216
      AsciiString method,
217
      AsciiString userAgent) {
218
    Preconditions.checkNotNull(defaultPath, "defaultPath");
1✔
219
    Preconditions.checkNotNull(authority, "authority");
1✔
220
    Preconditions.checkNotNull(method, "method");
1✔
221

222
    // Discard any application supplied duplicates of the reserved headers
223
    headers.discardAll(CONTENT_TYPE_KEY);
1✔
224
    headers.discardAll(GrpcUtil.TE_HEADER);
1✔
225
    headers.discardAll(GrpcUtil.USER_AGENT_KEY);
1✔
226

227
    return GrpcHttp2OutboundHeaders.clientRequestHeaders(
1✔
228
        toHttp2Headers(headers),
1✔
229
        authority,
230
        defaultPath,
231
        method,
232
        scheme,
233
        userAgent);
234
  }
235

236
  public static Http2Headers convertServerHeaders(Metadata headers) {
237
    // Discard any application supplied duplicates of the reserved headers
238
    headers.discardAll(CONTENT_TYPE_KEY);
1✔
239
    headers.discardAll(GrpcUtil.TE_HEADER);
1✔
240
    headers.discardAll(GrpcUtil.USER_AGENT_KEY);
1✔
241

242
    return GrpcHttp2OutboundHeaders.serverResponseHeaders(toHttp2Headers(headers));
1✔
243
  }
244

245
  public static Metadata convertTrailers(Http2Headers http2Headers) {
246
    if (http2Headers instanceof GrpcHttp2InboundHeaders) {
1✔
247
      GrpcHttp2InboundHeaders h = (GrpcHttp2InboundHeaders) http2Headers;
1✔
248
      return InternalMetadata.newMetadata(h.numHeaders(), h.namesAndValues());
1✔
249
    }
250
    return InternalMetadata.newMetadata(convertHeadersToArray(http2Headers));
1✔
251
  }
252

253
  public static Http2Headers convertTrailers(Metadata trailers, boolean headersSent) {
254
    if (!headersSent) {
1✔
255
      return convertServerHeaders(trailers);
1✔
256
    }
257
    return GrpcHttp2OutboundHeaders.serverResponseTrailers(toHttp2Headers(trailers));
1✔
258
  }
259

260
  public static Status statusFromThrowable(Throwable t) {
261
    Status s = Status.fromThrowable(t);
1✔
262
    if (s.getCode() != Status.Code.UNKNOWN) {
1✔
263
      return s;
1✔
264
    }
265
    if (t instanceof ClosedChannelException) {
1✔
266
      // ClosedChannelException is used any time the Netty channel is closed. Proper error
267
      // processing requires remembering the error that occurred before this one and using it
268
      // instead.
269
      //
270
      // Netty uses an exception that has no stack trace, while we would never hope to show this to
271
      // users, if it happens having the extra information may provide a small hint of where to
272
      // look.
273
      ClosedChannelException extraT = new ClosedChannelException();
1✔
274
      extraT.initCause(t);
1✔
275
      return Status.UNKNOWN.withDescription("channel closed").withCause(extraT);
1✔
276
    }
277
    if (t instanceof DecoderException && t.getCause() instanceof SSLException) {
1✔
278
      return Status.UNAVAILABLE.withDescription("ssl exception").withCause(t);
1✔
279
    }
280
    if (t instanceof IOException) {
1✔
281
      return Status.UNAVAILABLE.withDescription("io exception").withCause(t);
1✔
282
    }
283
    if (t instanceof UnresolvedAddressException) {
1✔
284
      return Status.UNAVAILABLE.withDescription("unresolved address").withCause(t);
1✔
285
    }
286
    if (t instanceof Http2Exception) {
1✔
287
      return Status.INTERNAL.withDescription("http2 exception").withCause(t);
1✔
288
    }
289
    return s;
1✔
290
  }
291

292
  @VisibleForTesting
293
  static boolean isEpollAvailable() {
294
    try {
295
      return (boolean) (Boolean)
1✔
296
          Class
297
              .forName("io.netty.channel.epoll.Epoll")
1✔
298
              .getDeclaredMethod("isAvailable")
1✔
299
              .invoke(null);
1✔
300
    } catch (ClassNotFoundException e) {
1✔
301
      // this is normal if netty-epoll runtime dependency doesn't exist.
302
      return false;
1✔
303
    } catch (Exception e) {
×
304
      throw new RuntimeException("Exception while checking Epoll availability", e);
×
305
    }
306
  }
307

308
  private static Throwable getEpollUnavailabilityCause() {
309
    try {
310
      return (Throwable)
1✔
311
          Class
312
              .forName("io.netty.channel.epoll.Epoll")
×
313
              .getDeclaredMethod("unavailabilityCause")
×
314
              .invoke(null);
×
315
    } catch (Exception e) {
1✔
316
      return e;
1✔
317
    }
318
  }
319

320
  // Must call when epoll is available
321
  private static Class<? extends Channel> epollChannelType() {
322
    try {
323
      Class<? extends Channel> channelType = Class
1✔
324
          .forName("io.netty.channel.epoll.EpollSocketChannel").asSubclass(Channel.class);
1✔
325
      return channelType;
1✔
326
    } catch (ClassNotFoundException e) {
×
327
      throw new RuntimeException("Cannot load EpollSocketChannel", e);
×
328
    }
329
  }
330

331
  // Must call when epoll is available
332
  private static Class<? extends Channel> epollDomainSocketChannelType() {
333
    try {
334
      Class<? extends Channel> channelType = Class
1✔
335
          .forName("io.netty.channel.epoll.EpollDomainSocketChannel").asSubclass(Channel.class);
1✔
336
      return channelType;
1✔
337
    } catch (ClassNotFoundException e) {
×
338
      throw new RuntimeException("Cannot load EpollDomainSocketChannel", e);
×
339
    }
340
  }
341

342
  // Must call when epoll is available
343
  private static Constructor<? extends EventLoopGroup> epollEventLoopGroupConstructor() {
344
    try {
345
      return Class
1✔
346
          .forName("io.netty.channel.epoll.EpollEventLoopGroup").asSubclass(EventLoopGroup.class)
1✔
347
          .getConstructor(Integer.TYPE, ThreadFactory.class);
1✔
348
    } catch (ClassNotFoundException e) {
×
349
      throw new RuntimeException("Cannot load EpollEventLoopGroup", e);
×
350
    } catch (NoSuchMethodException e) {
×
351
      throw new RuntimeException("EpollEventLoopGroup constructor not found", e);
×
352
    }
353
  }
354

355
  // Must call when epoll is available
356
  private static Class<? extends ServerChannel> epollServerChannelType() {
357
    try {
358
      Class<? extends ServerChannel> serverSocketChannel =
1✔
359
          Class
360
              .forName("io.netty.channel.epoll.EpollServerSocketChannel")
1✔
361
              .asSubclass(ServerChannel.class);
1✔
362
      return serverSocketChannel;
1✔
363
    } catch (ClassNotFoundException e) {
×
364
      throw new RuntimeException("Cannot load EpollServerSocketChannel", e);
×
365
    }
366
  }
367

368
  private static EventLoopGroup createEpollEventLoopGroup(
369
      int parallelism,
370
      ThreadFactory threadFactory) {
371
    checkState(EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR != null, "Epoll is not available");
1✔
372

373
    try {
374
      return EPOLL_EVENT_LOOP_GROUP_CONSTRUCTOR
1✔
375
          .newInstance(parallelism, threadFactory);
1✔
376
    } catch (Exception e) {
×
377
      throw new RuntimeException("Cannot create Epoll EventLoopGroup", e);
×
378
    }
379
  }
380

381
  private static ChannelFactory<ServerChannel> nioServerChannelFactory() {
382
    return new ChannelFactory<ServerChannel>() {
1✔
383
      @Override
384
      public ServerChannel newChannel() {
385
        return new NioServerSocketChannel();
1✔
386
      }
387
    };
388
  }
389

390
  /**
391
   * Returns TCP_USER_TIMEOUT channel option for Epoll channel if Epoll is available, otherwise
392
   * null.
393
   */
394
  @Nullable
395
  static ChannelOption<Integer> maybeGetTcpUserTimeoutOption() {
396
    return getEpollChannelOption("TCP_USER_TIMEOUT");
1✔
397
  }
398

399
  @Nullable
400
  @SuppressWarnings("unchecked")
401
  private static <T> ChannelOption<T> getEpollChannelOption(String optionName) {
402
    if (isEpollAvailable()) {
1✔
403
      try {
404
        return
1✔
405
            (ChannelOption<T>) Class.forName("io.netty.channel.epoll.EpollChannelOption")
1✔
406
                .getField(optionName)
1✔
407
                .get(null);
1✔
408
      } catch (Exception e) {
×
409
        throw new RuntimeException("ChannelOption(" + optionName + ") is not available", e);
×
410
      }
411
    }
412
    return null;
×
413
  }
414

415
  private static final class DefaultEventLoopGroupResource implements Resource<EventLoopGroup> {
416
    private final String name;
417
    private final int numEventLoops;
418
    private final EventLoopGroupType eventLoopGroupType;
419

420
    DefaultEventLoopGroupResource(
421
        int numEventLoops, String name, EventLoopGroupType eventLoopGroupType) {
1✔
422
      this.name = name;
1✔
423
      // See the implementation of MultithreadEventLoopGroup.  DEFAULT_EVENT_LOOP_THREADS there
424
      // defaults to NettyRuntime.availableProcessors() * 2.  We don't think we need that many
425
      // threads.  The overhead of a thread includes file descriptors and at least one chunk
426
      // allocation from PooledByteBufAllocator.  Here we reduce the default number of threads by
427
      // half.
428
      if (numEventLoops == 0 && System.getProperty("io.netty.eventLoopThreads") == null) {
1✔
429
        this.numEventLoops = NettyRuntime.availableProcessors();
1✔
430
      } else {
431
        this.numEventLoops = numEventLoops;
1✔
432
      }
433
      this.eventLoopGroupType = eventLoopGroupType;
1✔
434
    }
1✔
435

436
    @Override
437
    public EventLoopGroup create() {
438
      // Use Netty's DefaultThreadFactory in order to get the benefit of FastThreadLocal.
439
      ThreadFactory threadFactory = new DefaultThreadFactory(name, /* daemon= */ true);
1✔
440
      switch (eventLoopGroupType) {
1✔
441
        case NIO:
442
          return new NioEventLoopGroup(numEventLoops, threadFactory);
1✔
443
        case EPOLL:
444
          return createEpollEventLoopGroup(numEventLoops, threadFactory);
1✔
445
        default:
446
          throw new AssertionError("Unknown/Unsupported EventLoopGroupType: " + eventLoopGroupType);
×
447
      }
448
    }
449

450
    @Override
451
    public void close(EventLoopGroup instance) {
452
      instance.shutdownGracefully(0, 0, TimeUnit.SECONDS);
1✔
453
    }
1✔
454

455
    @Override
456
    public String toString() {
457
      return name;
×
458
    }
459
  }
460

461
  static final class FlowControlReader implements TransportTracer.FlowControlReader {
462
    private final Http2Stream connectionStream;
463
    private final Http2FlowController local;
464
    private final Http2FlowController remote;
465

466
    FlowControlReader(Http2Connection connection) {
1✔
467
      // 'local' in Netty is the _controller_ that controls inbound data. 'local' in Channelz is
468
      // the _present window_ provided by the remote that allows data to be sent. They are
469
      // opposites.
470
      local = connection.remote().flowController();
1✔
471
      remote = connection.local().flowController();
1✔
472
      connectionStream = connection.connectionStream();
1✔
473
    }
1✔
474

475
    @Override
476
    public TransportTracer.FlowControlWindows read() {
477
      return new TransportTracer.FlowControlWindows(
1✔
478
          local.windowSize(connectionStream),
1✔
479
          remote.windowSize(connectionStream));
1✔
480
    }
481
  }
482

483
  static InternalChannelz.SocketOptions getSocketOptions(Channel channel) {
484
    ChannelConfig config = channel.config();
1✔
485
    InternalChannelz.SocketOptions.Builder b = new InternalChannelz.SocketOptions.Builder();
1✔
486

487
    // The API allows returning null but not sure if it can happen in practice.
488
    // Let's be paranoid and do null checking just in case.
489
    Integer lingerSeconds = config.getOption(SO_LINGER);
1✔
490
    if (lingerSeconds != null) {
1✔
491
      b.setSocketOptionLingerSeconds(lingerSeconds);
1✔
492
    }
493

494
    Integer timeoutMillis = config.getOption(SO_TIMEOUT);
1✔
495
    if (timeoutMillis != null) {
1✔
496
      // in java, SO_TIMEOUT only applies to receiving
497
      b.setSocketOptionTimeoutMillis(timeoutMillis);
1✔
498
    }
499

500
    for (Map.Entry<ChannelOption<?>, Object> opt : config.getOptions().entrySet()) {
1✔
501
      ChannelOption<?> key = opt.getKey();
1✔
502
      // Constants are pooled, so there should only be one instance of each constant
503
      if (key.equals(SO_LINGER) || key.equals(SO_TIMEOUT)) {
1✔
504
        continue;
1✔
505
      }
506
      Object value = opt.getValue();
1✔
507
      // zpencer: Can a netty option be null?
508
      b.addOption(key.name(), String.valueOf(value));
1✔
509
    }
1✔
510

511
    NativeSocketOptions nativeOptions
1✔
512
        = NettySocketSupport.getNativeSocketOptions(channel);
1✔
513
    if (nativeOptions != null) {
1✔
514
      b.setTcpInfo(nativeOptions.tcpInfo); // may be null
×
515
      for (Map.Entry<String, String> entry : nativeOptions.otherInfo.entrySet()) {
×
516
        b.addOption(entry.getKey(), entry.getValue());
×
517
      }
×
518
    }
519
    return b.build();
1✔
520
  }
521

522
  private enum EventLoopGroupType {
1✔
523
    NIO,
1✔
524
    EPOLL
1✔
525
  }
526

527
  private Utils() {
528
    // Prevents instantiation
529
  }
530
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc