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

grpc / grpc-java / #19109

21 Mar 2024 11:59PM UTC coverage: 88.287% (+0.01%) from 88.277%
#19109

push

github

web-flow
Enable Happy Eyeballs by default (#11022)

* Flip the flag

* Fix test flakiness where IPv6 was not considered loopback

31198 of 35337 relevant lines covered (88.29%)

0.88 hits per line

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

90.79
/../core/src/main/java/io/grpc/internal/GrpcUtil.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.internal;
18

19
import static com.google.common.base.Preconditions.checkArgument;
20
import static com.google.common.base.Preconditions.checkState;
21

22
import com.google.common.annotations.VisibleForTesting;
23
import com.google.common.base.Objects;
24
import com.google.common.base.Preconditions;
25
import com.google.common.base.Splitter;
26
import com.google.common.base.Stopwatch;
27
import com.google.common.base.Strings;
28
import com.google.common.base.Supplier;
29
import com.google.common.util.concurrent.ListenableFuture;
30
import com.google.common.util.concurrent.ThreadFactoryBuilder;
31
import io.grpc.CallOptions;
32
import io.grpc.ClientStreamTracer;
33
import io.grpc.ClientStreamTracer.StreamInfo;
34
import io.grpc.InternalChannelz.SocketStats;
35
import io.grpc.InternalLogId;
36
import io.grpc.InternalMetadata;
37
import io.grpc.InternalMetadata.TrustedAsciiMarshaller;
38
import io.grpc.LoadBalancer.PickResult;
39
import io.grpc.LoadBalancer.Subchannel;
40
import io.grpc.Metadata;
41
import io.grpc.MethodDescriptor;
42
import io.grpc.ProxiedSocketAddress;
43
import io.grpc.ProxyDetector;
44
import io.grpc.Status;
45
import io.grpc.internal.ClientStreamListener.RpcProgress;
46
import io.grpc.internal.SharedResourceHolder.Resource;
47
import io.grpc.internal.StreamListener.MessageProducer;
48
import java.io.Closeable;
49
import java.io.IOException;
50
import java.io.InputStream;
51
import java.lang.reflect.InvocationTargetException;
52
import java.lang.reflect.Method;
53
import java.net.HttpURLConnection;
54
import java.net.InetSocketAddress;
55
import java.net.SocketAddress;
56
import java.net.URI;
57
import java.net.URISyntaxException;
58
import java.nio.charset.Charset;
59
import java.util.Arrays;
60
import java.util.Collection;
61
import java.util.Collections;
62
import java.util.EnumSet;
63
import java.util.HashSet;
64
import java.util.List;
65
import java.util.Locale;
66
import java.util.Set;
67
import java.util.concurrent.Executor;
68
import java.util.concurrent.ExecutorService;
69
import java.util.concurrent.Executors;
70
import java.util.concurrent.ScheduledExecutorService;
71
import java.util.concurrent.ThreadFactory;
72
import java.util.concurrent.TimeUnit;
73
import java.util.logging.Level;
74
import java.util.logging.Logger;
75
import javax.annotation.Nullable;
76
import javax.annotation.concurrent.Immutable;
77

78
/**
79
 * Common utilities for GRPC.
80
 */
81
public final class GrpcUtil {
82

83
  private static final Logger log = Logger.getLogger(GrpcUtil.class.getName());
1✔
84

85
  private static final Set<Status.Code> INAPPROPRIATE_CONTROL_PLANE_STATUS
1✔
86
      = Collections.unmodifiableSet(EnumSet.of(
1✔
87
          Status.Code.OK,
88
          Status.Code.INVALID_ARGUMENT,
89
          Status.Code.NOT_FOUND,
90
          Status.Code.ALREADY_EXISTS,
91
          Status.Code.FAILED_PRECONDITION,
92
          Status.Code.ABORTED,
93
          Status.Code.OUT_OF_RANGE,
94
          Status.Code.DATA_LOSS));
95

96
  public static final Charset US_ASCII = Charset.forName("US-ASCII");
1✔
97

98
  /**
99
   * {@link io.grpc.Metadata.Key} for the timeout header.
100
   */
101
  public static final Metadata.Key<Long> TIMEOUT_KEY =
1✔
102
          Metadata.Key.of(GrpcUtil.TIMEOUT, new TimeoutMarshaller());
1✔
103

104
  /**
105
   * {@link io.grpc.Metadata.Key} for the message encoding header.
106
   */
107
  public static final Metadata.Key<String> MESSAGE_ENCODING_KEY =
1✔
108
          Metadata.Key.of(GrpcUtil.MESSAGE_ENCODING, Metadata.ASCII_STRING_MARSHALLER);
1✔
109

110
  /**
111
   * {@link io.grpc.Metadata.Key} for the accepted message encodings header.
112
   */
113
  public static final Metadata.Key<byte[]> MESSAGE_ACCEPT_ENCODING_KEY =
1✔
114
      InternalMetadata.keyOf(GrpcUtil.MESSAGE_ACCEPT_ENCODING, new AcceptEncodingMarshaller());
1✔
115

116
  /**
117
   * {@link io.grpc.Metadata.Key} for the stream's content encoding header.
118
   */
119
  public static final Metadata.Key<String> CONTENT_ENCODING_KEY =
1✔
120
      Metadata.Key.of(GrpcUtil.CONTENT_ENCODING, Metadata.ASCII_STRING_MARSHALLER);
1✔
121

122
  /**
123
   * {@link io.grpc.Metadata.Key} for the stream's accepted content encoding header.
124
   */
125
  public static final Metadata.Key<byte[]> CONTENT_ACCEPT_ENCODING_KEY =
1✔
126
      InternalMetadata.keyOf(GrpcUtil.CONTENT_ACCEPT_ENCODING, new AcceptEncodingMarshaller());
1✔
127

128
  static final Metadata.Key<String> CONTENT_LENGTH_KEY =
1✔
129
      Metadata.Key.of("content-length", Metadata.ASCII_STRING_MARSHALLER);
1✔
130

131
  private static final class AcceptEncodingMarshaller implements TrustedAsciiMarshaller<byte[]> {
132
    @Override
133
    public byte[] toAsciiString(byte[] value) {
134
      return value;
1✔
135
    }
136

137
    @Override
138
    public byte[] parseAsciiString(byte[] serialized) {
139
      return serialized;
1✔
140
    }
141
  }
142

143
  /**
144
   * {@link io.grpc.Metadata.Key} for the Content-Type request/response header.
145
   */
146
  public static final Metadata.Key<String> CONTENT_TYPE_KEY =
1✔
147
          Metadata.Key.of("content-type", Metadata.ASCII_STRING_MARSHALLER);
1✔
148

149
  /**
150
   * {@link io.grpc.Metadata.Key} for the Transfer encoding.
151
   */
152
  public static final Metadata.Key<String> TE_HEADER =
1✔
153
      Metadata.Key.of("te", Metadata.ASCII_STRING_MARSHALLER);
1✔
154

155
  /**
156
   * {@link io.grpc.Metadata.Key} for the Content-Type request/response header.
157
   */
158
  public static final Metadata.Key<String> USER_AGENT_KEY =
1✔
159
          Metadata.Key.of("user-agent", Metadata.ASCII_STRING_MARSHALLER);
1✔
160

161
  /**
162
   * The default port for plain-text connections.
163
   */
164
  public static final int DEFAULT_PORT_PLAINTEXT = 80;
165

166
  /**
167
   * The default port for SSL connections.
168
   */
169
  public static final int DEFAULT_PORT_SSL = 443;
170

171
  /**
172
   * Content-Type used for GRPC-over-HTTP/2.
173
   */
174
  public static final String CONTENT_TYPE_GRPC = "application/grpc";
175

176
  /**
177
   * The HTTP method used for GRPC requests.
178
   */
179
  public static final String HTTP_METHOD = "POST";
180

181
  /**
182
   * The TE (transport encoding) header for requests over HTTP/2.
183
   */
184
  public static final String TE_TRAILERS = "trailers";
185

186
  /**
187
   * The Timeout header name.
188
   */
189
  public static final String TIMEOUT = "grpc-timeout";
190

191
  /**
192
   * The message encoding (i.e. compression) that can be used in the stream.
193
   */
194
  public static final String MESSAGE_ENCODING = "grpc-encoding";
195

196
  /**
197
   * The accepted message encodings (i.e. compression) that can be used in the stream.
198
   */
199
  public static final String MESSAGE_ACCEPT_ENCODING = "grpc-accept-encoding";
200

201
  /**
202
   * The content-encoding used to compress the full gRPC stream.
203
   */
204
  public static final String CONTENT_ENCODING = "content-encoding";
205

206
  /**
207
   * The accepted content-encodings that can be used to compress the full gRPC stream.
208
   */
209
  public static final String CONTENT_ACCEPT_ENCODING = "accept-encoding";
210

211
  /**
212
   * The default maximum uncompressed size (in bytes) for inbound messages. Defaults to 4 MiB.
213
   */
214
  public static final int DEFAULT_MAX_MESSAGE_SIZE = 4 * 1024 * 1024;
215

216
  /**
217
   * The default maximum size (in bytes) for inbound header/trailer.
218
   */
219
  // Update documentation in public-facing Builders when changing this value.
220
  public static final int DEFAULT_MAX_HEADER_LIST_SIZE = 8192;
221

222
  public static final Splitter ACCEPT_ENCODING_SPLITTER = Splitter.on(',').trimResults();
1✔
223

224
  public static final String IMPLEMENTATION_VERSION = "1.63.0-SNAPSHOT"; // CURRENT_GRPC_VERSION
225

226
  /**
227
   * The default timeout in nanos for a keepalive ping request.
228
   */
229
  public static final long DEFAULT_KEEPALIVE_TIMEOUT_NANOS = TimeUnit.SECONDS.toNanos(20L);
1✔
230

231
  /**
232
   * The magic keepalive time value that disables client keepalive.
233
   */
234
  public static final long KEEPALIVE_TIME_NANOS_DISABLED = Long.MAX_VALUE;
235

236
  /**
237
   * The default delay in nanos for server keepalive.
238
   */
239
  public static final long DEFAULT_SERVER_KEEPALIVE_TIME_NANOS = TimeUnit.HOURS.toNanos(2L);
1✔
240

241
  /**
242
   * The default timeout in nanos for a server keepalive ping request.
243
   */
244
  public static final long DEFAULT_SERVER_KEEPALIVE_TIMEOUT_NANOS = TimeUnit.SECONDS.toNanos(20L);
1✔
245

246
  /**
247
   * The magic keepalive time value that disables keepalive.
248
   */
249
  public static final long SERVER_KEEPALIVE_TIME_NANOS_DISABLED = Long.MAX_VALUE;
250

251
  /**
252
   * The default proxy detector.
253
   */
254
  public static final ProxyDetector DEFAULT_PROXY_DETECTOR = new ProxyDetectorImpl();
1✔
255

256
  /**
257
   * A proxy detector that always claims no proxy is needed.
258
   */
259
  public static final ProxyDetector NOOP_PROXY_DETECTOR = new ProxyDetector() {
1✔
260
    @Nullable
261
    @Override
262
    public ProxiedSocketAddress proxyFor(SocketAddress targetServerAddress) {
263
      return null;
1✔
264
    }
265
  };
266

267
  /**
268
   * The very default load-balancing policy.
269
   */
270
  public static final String DEFAULT_LB_POLICY = "pick_first";
271

272
  /**
273
   * RPCs created on the Channel returned by {@link io.grpc.LoadBalancer.Subchannel#asChannel}
274
   * will have this option with value {@code true}.  They will be treated differently from
275
   * the ones created by application.
276
   */
277
  public static final CallOptions.Key<Boolean> CALL_OPTIONS_RPC_OWNED_BY_BALANCER =
1✔
278
      CallOptions.Key.create("io.grpc.internal.CALL_OPTIONS_RPC_OWNED_BY_BALANCER");
1✔
279

280
  private static final ClientStreamTracer NOOP_TRACER = new ClientStreamTracer() {};
1✔
281

282
  /**
283
   * Returns true if an RPC with the given properties should be counted when calculating the
284
   * in-use state of a transport.
285
   */
286
  public static boolean shouldBeCountedForInUse(CallOptions callOptions) {
287
    return !Boolean.TRUE.equals(callOptions.getOption(CALL_OPTIONS_RPC_OWNED_BY_BALANCER));
1✔
288
  }
289

290
  /**
291
   * Maps HTTP error response status codes to transport codes, as defined in <a
292
   * href="https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md">
293
   * http-grpc-status-mapping.md</a>. Never returns a status for which {@code status.isOk()} is
294
   * {@code true}.
295
   */
296
  public static Status httpStatusToGrpcStatus(int httpStatusCode) {
297
    return httpStatusToGrpcCode(httpStatusCode).toStatus()
1✔
298
        .withDescription("HTTP status code " + httpStatusCode);
1✔
299
  }
300

301
  private static Status.Code httpStatusToGrpcCode(int httpStatusCode) {
302
    if (httpStatusCode >= 100 && httpStatusCode < 200) {
1✔
303
      // 1xx. These headers should have been ignored.
304
      return Status.Code.INTERNAL;
1✔
305
    }
306
    switch (httpStatusCode) {
1✔
307
      case HttpURLConnection.HTTP_BAD_REQUEST:  // 400
308
      case 431: // Request Header Fields Too Large
309
        // TODO(carl-mastrangelo): this should be added to the http-grpc-status-mapping.md doc.
310
        return Status.Code.INTERNAL;
1✔
311
      case HttpURLConnection.HTTP_UNAUTHORIZED:  // 401
312
        return Status.Code.UNAUTHENTICATED;
1✔
313
      case HttpURLConnection.HTTP_FORBIDDEN:  // 403
314
        return Status.Code.PERMISSION_DENIED;
1✔
315
      case HttpURLConnection.HTTP_NOT_FOUND:  // 404
316
        return Status.Code.UNIMPLEMENTED;
1✔
317
      case 429:  // Too Many Requests
318
      case HttpURLConnection.HTTP_BAD_GATEWAY:  // 502
319
      case HttpURLConnection.HTTP_UNAVAILABLE:  // 503
320
      case HttpURLConnection.HTTP_GATEWAY_TIMEOUT:  // 504
321
        return Status.Code.UNAVAILABLE;
1✔
322
      default:
323
        return Status.Code.UNKNOWN;
1✔
324
    }
325
  }
326

327
  /**
328
   * All error codes identified by the HTTP/2 spec. Used in GOAWAY and RST_STREAM frames.
329
   */
330
  public enum Http2Error {
1✔
331
    /**
332
     * Servers implementing a graceful shutdown of the connection will send {@code GOAWAY} with
333
     * {@code NO_ERROR}. In this case it is important to indicate to the application that the
334
     * request should be retried (i.e. {@link Status#UNAVAILABLE}).
335
     */
336
    NO_ERROR(0x0, Status.UNAVAILABLE),
1✔
337
    PROTOCOL_ERROR(0x1, Status.INTERNAL),
1✔
338
    INTERNAL_ERROR(0x2, Status.INTERNAL),
1✔
339
    FLOW_CONTROL_ERROR(0x3, Status.INTERNAL),
1✔
340
    SETTINGS_TIMEOUT(0x4, Status.INTERNAL),
1✔
341
    STREAM_CLOSED(0x5, Status.INTERNAL),
1✔
342
    FRAME_SIZE_ERROR(0x6, Status.INTERNAL),
1✔
343
    REFUSED_STREAM(0x7, Status.UNAVAILABLE),
1✔
344
    CANCEL(0x8, Status.CANCELLED),
1✔
345
    COMPRESSION_ERROR(0x9, Status.INTERNAL),
1✔
346
    CONNECT_ERROR(0xA, Status.INTERNAL),
1✔
347
    ENHANCE_YOUR_CALM(0xB, Status.RESOURCE_EXHAUSTED.withDescription("Bandwidth exhausted")),
1✔
348
    INADEQUATE_SECURITY(0xC, Status.PERMISSION_DENIED.withDescription("Permission denied as "
1✔
349
        + "protocol is not secure enough to call")),
350
    HTTP_1_1_REQUIRED(0xD, Status.UNKNOWN);
1✔
351

352
    // Populate a mapping of code to enum value for quick look-up.
353
    private static final Http2Error[] codeMap = buildHttp2CodeMap();
1✔
354

355
    private static Http2Error[] buildHttp2CodeMap() {
356
      Http2Error[] errors = Http2Error.values();
1✔
357
      int size = (int) errors[errors.length - 1].code() + 1;
1✔
358
      Http2Error[] http2CodeMap = new Http2Error[size];
1✔
359
      for (Http2Error error : errors) {
1✔
360
        int index = (int) error.code();
1✔
361
        http2CodeMap[index] = error;
1✔
362
      }
363
      return http2CodeMap;
1✔
364
    }
365

366
    private final int code;
367
    // Status is not guaranteed to be deeply immutable. Don't care though, since that's only true
368
    // when there are exceptions in the Status, which is not true here.
369
    @SuppressWarnings("ImmutableEnumChecker")
370
    private final Status status;
371

372
    Http2Error(int code, Status status) {
1✔
373
      this.code = code;
1✔
374
      String description = "HTTP/2 error code: " + this.name();
1✔
375
      if (status.getDescription() != null) {
1✔
376
        description += " (" + status.getDescription() + ")";
1✔
377
      }
378
      this.status = status.withDescription(description);
1✔
379
    }
1✔
380

381
    /**
382
     * Gets the code for this error used on the wire.
383
     */
384
    public long code() {
385
      return code;
1✔
386
    }
387

388
    /**
389
     * Gets the {@link Status} associated with this HTTP/2 code.
390
     */
391
    public Status status() {
392
      return status;
1✔
393
    }
394

395
    /**
396
     * Looks up the HTTP/2 error code enum value for the specified code.
397
     *
398
     * @param code an HTTP/2 error code value.
399
     * @return the HTTP/2 error code enum or {@code null} if not found.
400
     */
401
    public static Http2Error forCode(long code) {
402
      if (code >= codeMap.length || code < 0) {
1✔
403
        return null;
1✔
404
      }
405
      return codeMap[(int) code];
1✔
406
    }
407

408
    /**
409
     * Looks up the {@link Status} from the given HTTP/2 error code. This is preferred over {@code
410
     * forCode(code).status()}, to more easily conform to HTTP/2:
411
     *
412
     * <blockquote>Unknown or unsupported error codes MUST NOT trigger any special behavior.
413
     * These MAY be treated by an implementation as being equivalent to INTERNAL_ERROR.</blockquote>
414
     *
415
     * @param code the HTTP/2 error code.
416
     * @return a {@link Status} representing the given error.
417
     */
418
    public static Status statusForCode(long code) {
419
      Http2Error error = forCode(code);
1✔
420
      if (error == null) {
1✔
421
        // This "forgets" the message of INTERNAL_ERROR while keeping the same status code.
422
        Status.Code statusCode = INTERNAL_ERROR.status().getCode();
1✔
423
        return Status.fromCodeValue(statusCode.value())
1✔
424
            .withDescription("Unrecognized HTTP/2 error code: " + code);
1✔
425
      }
426

427
      return error.status();
1✔
428
    }
429
  }
430

431
  /**
432
   * Indicates whether or not the given value is a valid gRPC content-type.
433
   */
434
  public static boolean isGrpcContentType(String contentType) {
435
    if (contentType == null) {
1✔
436
      return false;
1✔
437
    }
438

439
    if (CONTENT_TYPE_GRPC.length() > contentType.length()) {
1✔
440
      return false;
1✔
441
    }
442

443
    contentType = contentType.toLowerCase(Locale.US);
1✔
444
    if (!contentType.startsWith(CONTENT_TYPE_GRPC)) {
1✔
445
      // Not a gRPC content-type.
446
      return false;
1✔
447
    }
448

449
    if (contentType.length() == CONTENT_TYPE_GRPC.length()) {
1✔
450
      // The strings match exactly.
451
      return true;
1✔
452
    }
453

454
    // The contentType matches, but is longer than the expected string.
455
    // We need to support variations on the content-type (e.g. +proto, +json) as defined by the
456
    // gRPC wire spec.
457
    char nextChar = contentType.charAt(CONTENT_TYPE_GRPC.length());
1✔
458
    return nextChar == '+' || nextChar == ';';
1✔
459
  }
460

461
  /**
462
   * Gets the User-Agent string for the gRPC transport.
463
   */
464
  public static String getGrpcUserAgent(
465
      String transportName, @Nullable String applicationUserAgent) {
466
    StringBuilder builder = new StringBuilder();
1✔
467
    if (applicationUserAgent != null) {
1✔
468
      builder.append(applicationUserAgent);
1✔
469
      builder.append(' ');
1✔
470
    }
471
    builder.append("grpc-java-");
1✔
472
    builder.append(transportName);
1✔
473
    builder.append('/');
1✔
474
    builder.append(IMPLEMENTATION_VERSION);
1✔
475
    return builder.toString();
1✔
476
  }
477

478
  @Immutable
479
  public static final class GrpcBuildVersion {
480
    private final String userAgent;
481
    private final String implementationVersion;
482

483
    private GrpcBuildVersion(String userAgent, String implementationVersion) {
1✔
484
      this.userAgent = Preconditions.checkNotNull(userAgent, "userAgentName");
1✔
485
      this.implementationVersion =
1✔
486
          Preconditions.checkNotNull(implementationVersion, "implementationVersion");
1✔
487
    }
1✔
488

489
    public String getUserAgent() {
490
      return userAgent;
1✔
491
    }
492

493
    public String getImplementationVersion() {
494
      return implementationVersion;
1✔
495
    }
496

497
    @Override
498
    public String toString() {
499
      return userAgent + " " + implementationVersion;
1✔
500
    }
501
  }
502

503
  /**
504
   * Returns the build version of gRPC.
505
   */
506
  public static GrpcBuildVersion getGrpcBuildVersion() {
507
    return new GrpcBuildVersion("gRPC Java", IMPLEMENTATION_VERSION);
1✔
508
  }
509

510
  /**
511
   * Parse an authority into a URI for retrieving the host and port.
512
   */
513
  public static URI authorityToUri(String authority) {
514
    Preconditions.checkNotNull(authority, "authority");
1✔
515
    URI uri;
516
    try {
517
      uri = new URI(null, authority, null, null, null);
1✔
518
    } catch (URISyntaxException ex) {
1✔
519
      throw new IllegalArgumentException("Invalid authority: " + authority, ex);
1✔
520
    }
1✔
521
    return uri;
1✔
522
  }
523

524
  /**
525
   * Verify {@code authority} is valid for use with gRPC. The syntax must be valid and it must not
526
   * include userinfo.
527
   *
528
   * @return the {@code authority} provided
529
   */
530
  public static String checkAuthority(String authority) {
531
    URI uri = authorityToUri(authority);
1✔
532
    // Verify that the user Info is not provided.
533
    checkArgument(uri.getAuthority().indexOf('@') == -1,
1✔
534
        "Userinfo must not be present on authority: '%s'", authority);
535
    return authority;
1✔
536
  }
537

538
  /**
539
   * Combine a host and port into an authority string.
540
   */
541
  // There is a copy of this method in io.grpc.Grpc
542
  public static String authorityFromHostAndPort(String host, int port) {
543
    try {
544
      return new URI(null, null, host, port, null, null, null).getAuthority();
1✔
545
    } catch (URISyntaxException ex) {
1✔
546
      throw new IllegalArgumentException("Invalid host or port: " + host + " " + port, ex);
1✔
547
    }
548
  }
549

550
  /**
551
   * Shared executor for channels.
552
   */
553
  public static final Resource<Executor> SHARED_CHANNEL_EXECUTOR =
1✔
554
      new Resource<Executor>() {
1✔
555
        private static final String NAME = "grpc-default-executor";
556
        @Override
557
        public Executor create() {
558
          return Executors.newCachedThreadPool(getThreadFactory(NAME + "-%d", true));
1✔
559
        }
560

561
        @Override
562
        public void close(Executor instance) {
563
          ((ExecutorService) instance).shutdown();
1✔
564
        }
1✔
565

566
        @Override
567
        public String toString() {
568
          return NAME;
×
569
        }
570
      };
571

572
  /**
573
   * Shared single-threaded executor for managing channel timers.
574
   */
575
  public static final Resource<ScheduledExecutorService> TIMER_SERVICE =
1✔
576
      new Resource<ScheduledExecutorService>() {
1✔
577
        @Override
578
        public ScheduledExecutorService create() {
579
          // We don't use newSingleThreadScheduledExecutor because it doesn't return a
580
          // ScheduledThreadPoolExecutor.
581
          ScheduledExecutorService service = Executors.newScheduledThreadPool(
1✔
582
              1,
583
              getThreadFactory("grpc-timer-%d", true));
1✔
584

585
          // If there are long timeouts that are cancelled, they will not actually be removed from
586
          // the executors queue.  This forces immediate removal upon cancellation to avoid a
587
          // memory leak.  Reflection is used because we cannot use methods added in Java 1.7.  If
588
          // the method does not exist, we give up.  Note that the method is not present in 1.6, but
589
          // _is_ present in the android standard library.
590
          try {
591
            Method method = service.getClass().getMethod("setRemoveOnCancelPolicy", boolean.class);
1✔
592
            method.invoke(service, true);
1✔
593
          } catch (NoSuchMethodException e) {
×
594
            // no op
595
          } catch (RuntimeException e) {
×
596
            throw e;
×
597
          } catch (Exception e) {
×
598
            throw new RuntimeException(e);
×
599
          }
1✔
600

601
          return Executors.unconfigurableScheduledExecutorService(service);
1✔
602
        }
603

604
        @Override
605
        public void close(ScheduledExecutorService instance) {
606
          instance.shutdown();
1✔
607
        }
1✔
608
      };
609

610

611
  /**
612
   * Get a {@link ThreadFactory} suitable for use in the current environment.
613
   * @param nameFormat to apply to threads created by the factory.
614
   * @param daemon {@code true} if the threads the factory creates are daemon threads, {@code false}
615
   *     otherwise.
616
   * @return a {@link ThreadFactory}.
617
   */
618
  public static ThreadFactory getThreadFactory(String nameFormat, boolean daemon) {
619
    return new ThreadFactoryBuilder()
1✔
620
        .setDaemon(daemon)
1✔
621
        .setNameFormat(nameFormat)
1✔
622
        .build();
1✔
623
  }
624

625
  /**
626
   * The factory of default Stopwatches.
627
   */
628
  public static final Supplier<Stopwatch> STOPWATCH_SUPPLIER = new Supplier<Stopwatch>() {
1✔
629
      @Override
630
      public Stopwatch get() {
631
        return Stopwatch.createUnstarted();
1✔
632
      }
633
    };
634

635
  /**
636
   * Returns the host via {@link InetSocketAddress#getHostString} if it is possible,
637
   * i.e. in jdk >= 7.
638
   * Otherwise, return it via {@link InetSocketAddress#getHostName} which may incur a DNS lookup.
639
   */
640
  public static String getHost(InetSocketAddress addr) {
641
    try {
642
      Method getHostStringMethod = InetSocketAddress.class.getMethod("getHostString");
1✔
643
      return (String) getHostStringMethod.invoke(addr);
1✔
644
    } catch (NoSuchMethodException e) {
×
645
      // noop
646
    } catch (IllegalAccessException e) {
×
647
      // noop
648
    } catch (InvocationTargetException e) {
×
649
      // noop
650
    }
×
651
    return addr.getHostName();
×
652
  }
653

654
  /**
655
   * Marshals a nanoseconds representation of the timeout to and from a string representation,
656
   * consisting of an ASCII decimal representation of a number with at most 8 digits, followed by a
657
   * unit. Available units:
658
   * n = nanoseconds
659
   * u = microseconds
660
   * m = milliseconds
661
   * S = seconds
662
   * M = minutes
663
   * H = hours
664
   *
665
   * <p>The representation is greedy with respect to precision. That is, 2 seconds will be
666
   * represented as `2000000u`.</p>
667
   *
668
   * <p>See <a href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests">the
669
   * request header definition</a></p>
670
   */
671
  @VisibleForTesting
672
  static class TimeoutMarshaller implements Metadata.AsciiMarshaller<Long> {
1✔
673

674
    @Override
675
    public String toAsciiString(Long timeoutNanos) {
676
      long cutoff = 100000000;
1✔
677
      TimeUnit unit = TimeUnit.NANOSECONDS;
1✔
678
      if (timeoutNanos < 0) {
1✔
679
        throw new IllegalArgumentException("Timeout too small");
×
680
      } else if (timeoutNanos < cutoff) {
1✔
681
        return timeoutNanos + "n";
1✔
682
      } else if (timeoutNanos < cutoff * 1000L) {
1✔
683
        return unit.toMicros(timeoutNanos) + "u";
1✔
684
      } else if (timeoutNanos < cutoff * 1000L * 1000L) {
1✔
685
        return unit.toMillis(timeoutNanos) + "m";
1✔
686
      } else if (timeoutNanos < cutoff * 1000L * 1000L * 1000L) {
1✔
687
        return unit.toSeconds(timeoutNanos) + "S";
1✔
688
      } else if (timeoutNanos < cutoff * 1000L * 1000L * 1000L * 60L) {
1✔
689
        return unit.toMinutes(timeoutNanos) + "M";
1✔
690
      } else {
691
        return unit.toHours(timeoutNanos) + "H";
1✔
692
      }
693
    }
694

695
    @Override
696
    public Long parseAsciiString(String serialized) {
697
      checkArgument(serialized.length() > 0, "empty timeout");
1✔
698
      checkArgument(serialized.length() <= 9, "bad timeout format");
1✔
699
      long value = Long.parseLong(serialized.substring(0, serialized.length() - 1));
1✔
700
      char unit = serialized.charAt(serialized.length() - 1);
1✔
701
      switch (unit) {
1✔
702
        case 'n':
703
          return value;
1✔
704
        case 'u':
705
          return TimeUnit.MICROSECONDS.toNanos(value);
1✔
706
        case 'm':
707
          return TimeUnit.MILLISECONDS.toNanos(value);
1✔
708
        case 'S':
709
          return TimeUnit.SECONDS.toNanos(value);
1✔
710
        case 'M':
711
          return TimeUnit.MINUTES.toNanos(value);
1✔
712
        case 'H':
713
          return TimeUnit.HOURS.toNanos(value);
1✔
714
        default:
715
          throw new IllegalArgumentException(String.format("Invalid timeout unit: %s", unit));
×
716
      }
717
    }
718
  }
719

720
  /**
721
   * Returns a transport out of a PickResult, or {@code null} if the result is "buffer".
722
   */
723
  @Nullable
724
  static ClientTransport getTransportFromPickResult(PickResult result, boolean isWaitForReady) {
725
    final ClientTransport transport;
726
    Subchannel subchannel = result.getSubchannel();
1✔
727
    if (subchannel != null) {
1✔
728
      transport = ((TransportProvider) subchannel.getInternalSubchannel()).obtainActiveTransport();
1✔
729
    } else {
730
      transport = null;
1✔
731
    }
732
    if (transport != null) {
1✔
733
      final ClientStreamTracer.Factory streamTracerFactory = result.getStreamTracerFactory();
1✔
734
      if (streamTracerFactory == null) {
1✔
735
        return transport;
1✔
736
      }
737
      return new ClientTransport() {
1✔
738
        @Override
739
        public ClientStream newStream(
740
            MethodDescriptor<?, ?> method, Metadata headers, CallOptions callOptions,
741
            ClientStreamTracer[] tracers) {
742
          StreamInfo info = StreamInfo.newBuilder().setCallOptions(callOptions).build();
1✔
743
          ClientStreamTracer streamTracer =
1✔
744
              streamTracerFactory.newClientStreamTracer(info, headers);
1✔
745
          checkState(tracers[tracers.length - 1] == NOOP_TRACER, "lb tracer already assigned");
1✔
746
          tracers[tracers.length - 1] = streamTracer;
1✔
747
          return transport.newStream(method, headers, callOptions, tracers);
1✔
748
        }
749

750
        @Override
751
        public void ping(PingCallback callback, Executor executor) {
752
          transport.ping(callback, executor);
×
753
        }
×
754

755
        @Override
756
        public InternalLogId getLogId() {
757
          return transport.getLogId();
×
758
        }
759

760
        @Override
761
        public ListenableFuture<SocketStats> getStats() {
762
          return transport.getStats();
×
763
        }
764
      };
765
    }
766
    if (!result.getStatus().isOk()) {
1✔
767
      if (result.isDrop()) {
1✔
768
        return new FailingClientTransport(
1✔
769
            replaceInappropriateControlPlaneStatus(result.getStatus()), RpcProgress.DROPPED);
1✔
770
      }
771
      if (!isWaitForReady) {
1✔
772
        return new FailingClientTransport(
1✔
773
            replaceInappropriateControlPlaneStatus(result.getStatus()), RpcProgress.PROCESSED);
1✔
774
      }
775
    }
776
    return null;
1✔
777
  }
778

779
  /** Gets stream tracers based on CallOptions. */
780
  public static ClientStreamTracer[] getClientStreamTracers(
781
      CallOptions callOptions, Metadata headers, int previousAttempts, boolean isTransparentRetry) {
782
    List<ClientStreamTracer.Factory> factories = callOptions.getStreamTracerFactories();
1✔
783
    ClientStreamTracer[] tracers = new ClientStreamTracer[factories.size() + 1];
1✔
784
    StreamInfo streamInfo = StreamInfo.newBuilder()
1✔
785
        .setCallOptions(callOptions)
1✔
786
        .setPreviousAttempts(previousAttempts)
1✔
787
        .setIsTransparentRetry(isTransparentRetry)
1✔
788
        .build();
1✔
789
    for (int i = 0; i < factories.size(); i++) {
1✔
790
      tracers[i] = factories.get(i).newClientStreamTracer(streamInfo, headers);
1✔
791
    }
792
    // Reserved to be set later by the lb as per the API contract of ClientTransport.newStream().
793
    // See also GrpcUtil.getTransportFromPickResult()
794
    tracers[tracers.length - 1] = NOOP_TRACER;
1✔
795
    return tracers;
1✔
796
  }
797

798
  /** Quietly closes all messages in MessageProducer. */
799
  static void closeQuietly(MessageProducer producer) {
800
    InputStream message;
801
    while ((message = producer.next()) != null) {
1✔
802
      closeQuietly(message);
1✔
803
    }
804
  }
1✔
805

806
  /**
807
   * Closes a Closeable, ignoring IOExceptions.
808
   * This method exists because Guava's {@code Closeables.closeQuietly()} is beta.
809
   */
810
  public static void closeQuietly(@Nullable Closeable message) {
811
    if (message == null) {
1✔
812
      return;
×
813
    }
814
    try {
815
      message.close();
1✔
816
    } catch (IOException ioException) {
×
817
      // do nothing except log
818
      log.log(Level.WARNING, "exception caught in closeQuietly", ioException);
×
819
    }
1✔
820
  }
1✔
821

822
  /** Reads {@code in} until end of stream. */
823
  public static void exhaust(InputStream in) throws IOException {
824
    byte[] buf = new byte[256];
1✔
825
    while (in.read(buf) != -1) {}
1✔
826
  }
1✔
827

828
  /**
829
   * Some status codes from the control plane are not appropritate to use in the data plane. If one
830
   * is given it will be replaced with INTERNAL, indicating a bug in the control plane
831
   * implementation.
832
   */
833
  public static Status replaceInappropriateControlPlaneStatus(Status status) {
834
    checkArgument(status != null);
1✔
835
    return INAPPROPRIATE_CONTROL_PLANE_STATUS.contains(status.getCode())
1✔
836
        ? Status.INTERNAL.withDescription(
1✔
837
        "Inappropriate status code from control plane: " + status.getCode() + " "
1✔
838
            + status.getDescription()).withCause(status.getCause()) : status;
1✔
839
  }
840

841
  /**
842
   * Checks whether the given item exists in the iterable.  This is copied from Guava Collect's
843
   * {@code Iterables.contains()} because Guava Collect is not Android-friendly thus core can't
844
   * depend on it.
845
   */
846
  static <T> boolean iterableContains(Iterable<T> iterable, T item) {
847
    if (iterable instanceof Collection) {
1✔
848
      Collection<?> collection = (Collection<?>) iterable;
×
849
      try {
850
        return collection.contains(item);
×
851
      } catch (NullPointerException e) {
×
852
        return false;
×
853
      } catch (ClassCastException e) {
×
854
        return false;
×
855
      }
856
    }
857
    for (T i : iterable) {
1✔
858
      if (Objects.equal(i, item)) {
1✔
859
        return true;
1✔
860
      }
861
    }
1✔
862
    return false;
1✔
863
  }
864

865
  /**
866
   * Percent encode the {@code authority} based on
867
   * https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.
868
   *
869
   * <p>When escaping a String, the following rules apply:
870
   *
871
   * <ul>
872
   *   <li>The alphanumeric characters "a" through "z", "A" through "Z" and "0" through "9" remain
873
   *       the same.
874
   *   <li>The unreserved characters ".", "-", "~", and "_" remain the same.
875
   *   <li>The general delimiters for authority, "[", "]", "@" and ":" remain the same.
876
   *   <li>The subdelimiters "!", "$", "&amp;", "'", "(", ")", "*", "+", ",", ";", and "=" remain
877
   *       the same.
878
   *   <li>The space character " " is converted into %20.
879
   *   <li>All other characters are converted into one or more bytes using UTF-8 encoding and each
880
   *       byte is then represented by the 3-character string "%XY", where "XY" is the two-digit,
881
   *       uppercase, hexadecimal representation of the byte value.
882
   * </ul>
883
   * 
884
   * <p>This section does not use URLEscapers from Guava Net as its not Android-friendly thus core
885
   *    can't depend on it.
886
   */
887
  public static class AuthorityEscaper {
×
888
    // Escapers should output upper case hex digits.
889
    private static final char[] UPPER_HEX_DIGITS = "0123456789ABCDEF".toCharArray();
1✔
890
    private static final Set<Character> UNRESERVED_CHARACTERS = Collections
1✔
891
        .unmodifiableSet(new HashSet<>(Arrays.asList('-', '_', '.', '~')));
1✔
892
    private static final Set<Character> SUB_DELIMS = Collections
1✔
893
        .unmodifiableSet(new HashSet<>(
1✔
894
            Arrays.asList('!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=')));
1✔
895
    private static final Set<Character> AUTHORITY_DELIMS = Collections
1✔
896
        .unmodifiableSet(new HashSet<>(Arrays.asList(':', '[', ']', '@')));
1✔
897

898
    private static boolean shouldEscape(char c) {
899
      // Only encode ASCII.
900
      if (c > 127) {
1✔
901
        return false;
1✔
902
      }
903
      // Letters don't need an escape.
904
      if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) {
1✔
905
        return false;
1✔
906
      }
907
      // Numbers don't need to be escaped.
908
      if ((c >= '0' && c <= '9'))  {
1✔
909
        return false;
1✔
910
      }
911
      // Don't escape allowed characters.
912
      if (UNRESERVED_CHARACTERS.contains(c)
1✔
913
          || SUB_DELIMS.contains(c)
1✔
914
          || AUTHORITY_DELIMS.contains(c)) {
1✔
915
        return false;
1✔
916
      }
917
      return true;
1✔
918
    }
919

920
    public static String encodeAuthority(String authority) {
921
      Preconditions.checkNotNull(authority, "authority");
1✔
922
      int authorityLength = authority.length();
1✔
923
      int hexCount = 0;
1✔
924
      // Calculate how many characters actually need escaping.
925
      for (int index = 0; index < authorityLength; index++) {
1✔
926
        char c = authority.charAt(index);
1✔
927
        if (shouldEscape(c)) {
1✔
928
          hexCount++;
1✔
929
        }
930
      }
931
      // If no char need escaping, just return the original string back.
932
      if (hexCount == 0) {
1✔
933
        return authority;
1✔
934
      }
935

936
      // Allocate enough space as encoded characters need 2 extra chars.
937
      StringBuilder encoded_authority = new StringBuilder((2 * hexCount) + authorityLength);
1✔
938
      for (int index = 0; index < authorityLength; index++) {
1✔
939
        char c = authority.charAt(index);
1✔
940
        if (shouldEscape(c)) {
1✔
941
          encoded_authority.append('%');
1✔
942
          encoded_authority.append(UPPER_HEX_DIGITS[c >>> 4]);
1✔
943
          encoded_authority.append(UPPER_HEX_DIGITS[c & 0xF]);
1✔
944
        } else {
945
          encoded_authority.append(c);
1✔
946
        }
947
      }
948
      return encoded_authority.toString();
1✔
949
    }
950
  }
951

952
  public static boolean getFlag(String envVarName, boolean enableByDefault) {
953
    String envVar = System.getenv(envVarName);
1✔
954
    if (envVar == null) {
1✔
955
      envVar = System.getProperty(envVarName);
1✔
956
    }
957
    if (envVar != null) {
1✔
958
      envVar = envVar.trim();
1✔
959
    }
960
    if (enableByDefault) {
1✔
961
      return Strings.isNullOrEmpty(envVar) || Boolean.parseBoolean(envVar);
1✔
962
    } else {
963
      return !Strings.isNullOrEmpty(envVar) && Boolean.parseBoolean(envVar);
×
964
    }
965
  }
966

967

968

969
  private GrpcUtil() {}
970
}
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