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

grpc / grpc-java / #20268

06 May 2026 08:58PM UTC coverage: 88.822% (-0.01%) from 88.836%
#20268

push

github

web-flow
compiler: Avoid compile error on weird proto file names

This only matters when `@generated=javax` is used, so it shouldn't
matter much.

It isn't guaranteed that javac will interpret the file as UTF-8, but it
is exceedingly common, and it doesn't seem too much to ask if you are
using weird file names.

36154 of 40704 relevant lines covered (88.82%)

0.89 hits per line

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

92.95
/../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.Supplier;
28
import com.google.common.util.concurrent.ListenableFuture;
29
import com.google.common.util.concurrent.ThreadFactoryBuilder;
30
import io.grpc.CallOptions;
31
import io.grpc.ClientStreamTracer;
32
import io.grpc.ClientStreamTracer.StreamInfo;
33
import io.grpc.InternalChannelz.SocketStats;
34
import io.grpc.InternalFeatureFlags;
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.Method;
52
import java.net.HttpURLConnection;
53
import java.net.SocketAddress;
54
import java.net.URI;
55
import java.net.URISyntaxException;
56
import java.nio.charset.Charset;
57
import java.util.Arrays;
58
import java.util.Collection;
59
import java.util.Collections;
60
import java.util.EnumSet;
61
import java.util.HashSet;
62
import java.util.List;
63
import java.util.Locale;
64
import java.util.Set;
65
import java.util.concurrent.Executor;
66
import java.util.concurrent.ExecutorService;
67
import java.util.concurrent.Executors;
68
import java.util.concurrent.ScheduledExecutorService;
69
import java.util.concurrent.ThreadFactory;
70
import java.util.concurrent.TimeUnit;
71
import java.util.logging.Level;
72
import java.util.logging.Logger;
73
import javax.annotation.Nullable;
74
import javax.annotation.concurrent.Immutable;
75

76
/**
77
 * Common utilities for GRPC.
78
 */
79
public final class GrpcUtil {
80

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

222
  public static final String IMPLEMENTATION_VERSION = "1.82.0-SNAPSHOT"; // CURRENT_GRPC_VERSION
223

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

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

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

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

244
  /**
245
   * The default minimum time between client keepalive pings permitted by server.
246
   */
247
  public static final long DEFAULT_SERVER_PERMIT_KEEPALIVE_TIME_NANOS
1✔
248
          = TimeUnit.MINUTES.toNanos(5L);
1✔
249

250
  /**
251
   * The magic keepalive time value that disables keepalive.
252
   */
253
  public static final long SERVER_KEEPALIVE_TIME_NANOS_DISABLED = Long.MAX_VALUE;
254

255
  /**
256
   * The default proxy detector.
257
   */
258
  public static final ProxyDetector DEFAULT_PROXY_DETECTOR = new ProxyDetectorImpl();
1✔
259

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

271
  /**
272
   * The very default load-balancing policy.
273
   */
274
  public static final String DEFAULT_LB_POLICY = "pick_first";
275

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

284
  private static final ClientStreamTracer NOOP_TRACER = new ClientStreamTracer() {};
1✔
285

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

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

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

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

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

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

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

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

385
    /**
386
     * Gets the code for this error used on the wire.
387
     */
388
    public long code() {
389
      return code;
1✔
390
    }
391

392
    /**
393
     * Gets the {@link Status} associated with this HTTP/2 code.
394
     */
395
    public Status status() {
396
      return status;
1✔
397
    }
398

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

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

431
      return error.status();
1✔
432
    }
433
  }
434

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

443
    if (CONTENT_TYPE_GRPC.length() > contentType.length()) {
1✔
444
      return false;
1✔
445
    }
446

447
    contentType = contentType.toLowerCase(Locale.US);
1✔
448
    if (!contentType.startsWith(CONTENT_TYPE_GRPC)) {
1✔
449
      // Not a gRPC content-type.
450
      return false;
1✔
451
    }
452

453
    if (contentType.length() == CONTENT_TYPE_GRPC.length()) {
1✔
454
      // The strings match exactly.
455
      return true;
1✔
456
    }
457

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

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

482
  @Immutable
483
  public static final class GrpcBuildVersion {
484
    private final String userAgent;
485
    private final String implementationVersion;
486

487
    private GrpcBuildVersion(String userAgent, String implementationVersion) {
1✔
488
      this.userAgent = Preconditions.checkNotNull(userAgent, "userAgentName");
1✔
489
      this.implementationVersion =
1✔
490
          Preconditions.checkNotNull(implementationVersion, "implementationVersion");
1✔
491
    }
1✔
492

493
    public String getUserAgent() {
494
      return userAgent;
1✔
495
    }
496

497
    public String getImplementationVersion() {
498
      return implementationVersion;
1✔
499
    }
500

501
    @Override
502
    public String toString() {
503
      return userAgent + " " + implementationVersion;
1✔
504
    }
505
  }
506

507
  /**
508
   * Returns the build version of gRPC.
509
   */
510
  public static GrpcBuildVersion getGrpcBuildVersion() {
511
    return new GrpcBuildVersion("gRPC Java", IMPLEMENTATION_VERSION);
1✔
512
  }
513

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

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

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

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

565
        @Override
566
        public void close(Executor instance) {
567
          ((ExecutorService) instance).shutdown();
1✔
568
        }
1✔
569

570
        @Override
571
        public String toString() {
572
          return NAME;
×
573
        }
574
      };
575

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

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

605
          return Executors.unconfigurableScheduledExecutorService(service);
1✔
606
        }
607

608
        @Override
609
        public void close(ScheduledExecutorService instance) {
610
          instance.shutdown();
1✔
611
        }
1✔
612
      };
613

614

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

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

639
  /**
640
   * Marshals a nanoseconds representation of the timeout to and from a string representation,
641
   * consisting of an ASCII decimal representation of a number with at most 8 digits, followed by a
642
   * unit. Available units:
643
   * n = nanoseconds
644
   * u = microseconds
645
   * m = milliseconds
646
   * S = seconds
647
   * M = minutes
648
   * H = hours
649
   *
650
   * <p>The representation is greedy with respect to precision. That is, 2 seconds will be
651
   * represented as `2000000u`.</p>
652
   *
653
   * <p>See <a href="https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests">the
654
   * request header definition</a></p>
655
   */
656
  @VisibleForTesting
657
  static class TimeoutMarshaller implements Metadata.AsciiMarshaller<Long> {
1✔
658

659
    @Override
660
    public String toAsciiString(Long timeoutNanosObject) {
661
      long cutoff = 100000000;
1✔
662
      // Timeout checking is inherently racy. RPCs with timeouts in the past ideally don't even get
663
      // here, but if the timeout is expired assume that happened recently and adjust it to the
664
      // smallest allowed timeout
665
      long timeoutNanos = Math.max(1, timeoutNanosObject);
1✔
666
      TimeUnit unit = TimeUnit.NANOSECONDS;
1✔
667
      if (timeoutNanos < cutoff) {
1✔
668
        return timeoutNanos + "n";
1✔
669
      } else if (timeoutNanos < cutoff * 1000L) {
1✔
670
        return unit.toMicros(timeoutNanos) + "u";
1✔
671
      } else if (timeoutNanos < cutoff * 1000L * 1000L) {
1✔
672
        return unit.toMillis(timeoutNanos) + "m";
1✔
673
      } else if (timeoutNanos < cutoff * 1000L * 1000L * 1000L) {
1✔
674
        return unit.toSeconds(timeoutNanos) + "S";
1✔
675
      } else if (timeoutNanos < cutoff * 1000L * 1000L * 1000L * 60L) {
1✔
676
        return unit.toMinutes(timeoutNanos) + "M";
1✔
677
      } else {
678
        return unit.toHours(timeoutNanos) + "H";
1✔
679
      }
680
    }
681

682
    @Override
683
    public Long parseAsciiString(String serialized) {
684
      checkArgument(serialized.length() > 0, "empty timeout");
1✔
685
      checkArgument(serialized.length() <= 9, "bad timeout format");
1✔
686
      long value = Long.parseLong(serialized.substring(0, serialized.length() - 1));
1✔
687
      char unit = serialized.charAt(serialized.length() - 1);
1✔
688
      switch (unit) {
1✔
689
        case 'n':
690
          return value;
1✔
691
        case 'u':
692
          return TimeUnit.MICROSECONDS.toNanos(value);
1✔
693
        case 'm':
694
          return TimeUnit.MILLISECONDS.toNanos(value);
1✔
695
        case 'S':
696
          return TimeUnit.SECONDS.toNanos(value);
1✔
697
        case 'M':
698
          return TimeUnit.MINUTES.toNanos(value);
1✔
699
        case 'H':
700
          return TimeUnit.HOURS.toNanos(value);
1✔
701
        default:
702
          throw new IllegalArgumentException(String.format("Invalid timeout unit: %s", unit));
×
703
      }
704
    }
705
  }
706

707
  /**
708
   * Returns a transport out of a PickResult, or {@code null} if the result is "buffer".
709
   */
710
  @Nullable
711
  static ClientTransport getTransportFromPickResult(PickResult result, boolean isWaitForReady) {
712
    final ClientTransport transport;
713
    Subchannel subchannel = result.getSubchannel();
1✔
714
    if (subchannel != null) {
1✔
715
      transport = ((TransportProvider) subchannel.getInternalSubchannel()).obtainActiveTransport();
1✔
716
    } else {
717
      transport = null;
1✔
718
    }
719
    if (transport != null) {
1✔
720
      final ClientStreamTracer.Factory streamTracerFactory = result.getStreamTracerFactory();
1✔
721
      if (streamTracerFactory == null) {
1✔
722
        return transport;
1✔
723
      }
724
      return new ClientTransport() {
1✔
725
        @Override
726
        public ClientStream newStream(
727
            MethodDescriptor<?, ?> method, Metadata headers, CallOptions callOptions,
728
            ClientStreamTracer[] tracers) {
729
          StreamInfo info = StreamInfo.newBuilder().setCallOptions(callOptions).build();
1✔
730
          ClientStreamTracer streamTracer =
1✔
731
              streamTracerFactory.newClientStreamTracer(info, headers);
1✔
732
          checkState(tracers[tracers.length - 1] == NOOP_TRACER, "lb tracer already assigned");
1✔
733
          tracers[tracers.length - 1] = streamTracer;
1✔
734
          return transport.newStream(method, headers, callOptions, tracers);
1✔
735
        }
736

737
        @Override
738
        public void ping(PingCallback callback, Executor executor) {
739
          transport.ping(callback, executor);
×
740
        }
×
741

742
        @Override
743
        public InternalLogId getLogId() {
744
          return transport.getLogId();
×
745
        }
746

747
        @Override
748
        public ListenableFuture<SocketStats> getStats() {
749
          return transport.getStats();
×
750
        }
751
      };
752
    }
753
    if (!result.getStatus().isOk()) {
1✔
754
      if (result.isDrop()) {
1✔
755
        return new FailingClientTransport(
1✔
756
            replaceInappropriateControlPlaneStatus(result.getStatus()), RpcProgress.DROPPED);
1✔
757
      }
758
      if (!isWaitForReady) {
1✔
759
        return new FailingClientTransport(
1✔
760
            replaceInappropriateControlPlaneStatus(result.getStatus()), RpcProgress.PROCESSED);
1✔
761
      }
762
    }
763
    return null;
1✔
764
  }
765

766
  /** Gets stream tracers based on CallOptions. */
767
  public static ClientStreamTracer[] getClientStreamTracers(
768
      CallOptions callOptions, Metadata headers, int previousAttempts, boolean isTransparentRetry,
769
      boolean isHedging) {
770
    List<ClientStreamTracer.Factory> factories = callOptions.getStreamTracerFactories();
1✔
771
    ClientStreamTracer[] tracers = new ClientStreamTracer[factories.size() + 1];
1✔
772
    StreamInfo streamInfo = StreamInfo.newBuilder()
1✔
773
        .setCallOptions(callOptions)
1✔
774
        .setPreviousAttempts(previousAttempts)
1✔
775
        .setIsTransparentRetry(isTransparentRetry)
1✔
776
        .setIsHedging(isHedging)
1✔
777
        .build();
1✔
778
    for (int i = 0; i < factories.size(); i++) {
1✔
779
      tracers[i] = factories.get(i).newClientStreamTracer(streamInfo, headers);
1✔
780
    }
781
    // Reserved to be set later by the lb as per the API contract of ClientTransport.newStream().
782
    // See also GrpcUtil.getTransportFromPickResult()
783
    tracers[tracers.length - 1] = NOOP_TRACER;
1✔
784
    return tracers;
1✔
785
  }
786

787
  /** Quietly closes all messages in MessageProducer. */
788
  static void closeQuietly(MessageProducer producer) {
789
    InputStream message;
790
    while ((message = producer.next()) != null) {
1✔
791
      closeQuietly(message);
1✔
792
    }
793
  }
1✔
794

795
  /**
796
   * Closes a Closeable, ignoring IOExceptions.
797
   * This method exists because Guava's {@code Closeables.closeQuietly()} is beta.
798
   */
799
  public static void closeQuietly(@Nullable Closeable message) {
800
    if (message == null) {
1✔
801
      return;
×
802
    }
803
    try {
804
      message.close();
1✔
805
    } catch (IOException ioException) {
×
806
      // do nothing except log
807
      log.log(Level.WARNING, "exception caught in closeQuietly", ioException);
×
808
    }
1✔
809
  }
1✔
810

811
  /** Reads {@code in} until end of stream. */
812
  public static void exhaust(InputStream in) throws IOException {
813
    byte[] buf = new byte[256];
1✔
814
    while (in.read(buf) != -1) {}
1✔
815
  }
1✔
816

817
  /**
818
   * Some status codes from the control plane are not appropritate to use in the data plane. If one
819
   * is given it will be replaced with INTERNAL, indicating a bug in the control plane
820
   * implementation.
821
   */
822
  public static Status replaceInappropriateControlPlaneStatus(Status status) {
823
    checkArgument(status != null);
1✔
824
    return INAPPROPRIATE_CONTROL_PLANE_STATUS.contains(status.getCode())
1✔
825
        ? Status.INTERNAL.withDescription(
1✔
826
        "Inappropriate status code from control plane: " + status.getCode() + " "
1✔
827
            + status.getDescription()).withCause(status.getCause()) : status;
1✔
828
  }
829

830
  /**
831
   * Returns a "clean" representation of a status code and description (not cause) like
832
   * "UNAVAILABLE: The description". Should be similar to Status.formatThrowableMessage().
833
   */
834
  public static String statusToPrettyString(Status status) {
835
    if (status.getDescription() == null) {
1✔
836
      return status.getCode().toString();
1✔
837
    } else {
838
      return status.getCode() + ": " + status.getDescription();
1✔
839
    }
840
  }
841

842
  /**
843
   * Create a status with contextual information, propagating details from a non-null status that
844
   * contributed to the failure. For example, if UNAVAILABLE, "Couldn't load bar", and status
845
   * "FAILED_PRECONDITION: Foo missing" were passed as arguments, then this method would produce the
846
   * status "UNAVAILABLE: Couldn't load bar: FAILED_PRECONDITION: Foo missing" with a cause if the
847
   * passed status had a cause.
848
   */
849
  public static Status statusWithDetails(Status.Code code, String description, Status causeStatus) {
850
    return code.toStatus()
1✔
851
        .withDescription(description + ": " + statusToPrettyString(causeStatus))
1✔
852
        .withCause(causeStatus.getCause());
1✔
853
  }
854

855
  /**
856
   * Checks whether the given item exists in the iterable.  This is copied from Guava Collect's
857
   * {@code Iterables.contains()} because Guava Collect is not Android-friendly thus core can't
858
   * depend on it.
859
   */
860
  static <T> boolean iterableContains(Iterable<T> iterable, T item) {
861
    if (iterable instanceof Collection) {
1✔
862
      Collection<?> collection = (Collection<?>) iterable;
×
863
      try {
864
        return collection.contains(item);
×
865
      } catch (NullPointerException e) {
×
866
        return false;
×
867
      } catch (ClassCastException e) {
×
868
        return false;
×
869
      }
870
    }
871
    for (T i : iterable) {
1✔
872
      if (Objects.equal(i, item)) {
1✔
873
        return true;
1✔
874
      }
875
    }
1✔
876
    return false;
1✔
877
  }
878

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

912
    private static boolean shouldEscape(char c) {
913
      // Only encode ASCII.
914
      if (c > 127) {
1✔
915
        return false;
1✔
916
      }
917
      // Letters don't need an escape.
918
      if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) {
1✔
919
        return false;
1✔
920
      }
921
      // Numbers don't need to be escaped.
922
      if ((c >= '0' && c <= '9'))  {
1✔
923
        return false;
1✔
924
      }
925
      // Don't escape allowed characters.
926
      if (UNRESERVED_CHARACTERS.contains(c)
1✔
927
          || SUB_DELIMS.contains(c)
1✔
928
          || AUTHORITY_DELIMS.contains(c)) {
1✔
929
        return false;
1✔
930
      }
931
      return true;
1✔
932
    }
933

934
    public static String encodeAuthority(String authority) {
935
      Preconditions.checkNotNull(authority, "authority");
1✔
936
      int authorityLength = authority.length();
1✔
937
      int hexCount = 0;
1✔
938
      // Calculate how many characters actually need escaping.
939
      for (int index = 0; index < authorityLength; index++) {
1✔
940
        char c = authority.charAt(index);
1✔
941
        if (shouldEscape(c)) {
1✔
942
          hexCount++;
1✔
943
        }
944
      }
945
      // If no char need escaping, just return the original string back.
946
      if (hexCount == 0) {
1✔
947
        return authority;
1✔
948
      }
949

950
      // Allocate enough space as encoded characters need 2 extra chars.
951
      StringBuilder encoded_authority = new StringBuilder((2 * hexCount) + authorityLength);
1✔
952
      for (int index = 0; index < authorityLength; index++) {
1✔
953
        char c = authority.charAt(index);
1✔
954
        if (shouldEscape(c)) {
1✔
955
          encoded_authority.append('%');
1✔
956
          encoded_authority.append(UPPER_HEX_DIGITS[c >>> 4]);
1✔
957
          encoded_authority.append(UPPER_HEX_DIGITS[c & 0xF]);
1✔
958
        } else {
959
          encoded_authority.append(c);
1✔
960
        }
961
      }
962
      return encoded_authority.toString();
1✔
963
    }
964
  }
965

966
  public static boolean getFlag(String envVarName, boolean enableByDefault) {
967
    return InternalFeatureFlags.getFlag(envVarName, enableByDefault);
1✔
968
  }
969

970

971

972
  private GrpcUtil() {}
973
}
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