• 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

67.55
/../okhttp/src/main/java/io/grpc/okhttp/OkHttpServerBuilder.java
1
/*
2
 * Copyright 2022 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.okhttp;
18

19
import static com.google.common.base.Preconditions.checkArgument;
20
import static io.grpc.internal.CertificateUtils.createTrustManager;
21
import static io.grpc.internal.GrpcUtil.DEFAULT_SERVER_PERMIT_KEEPALIVE_TIME_NANOS;
22

23
import com.google.common.base.Preconditions;
24
import com.google.errorprone.annotations.CanIgnoreReturnValue;
25
import com.google.errorprone.annotations.DoNotCall;
26
import io.grpc.ChoiceServerCredentials;
27
import io.grpc.ExperimentalApi;
28
import io.grpc.ForwardingServerBuilder;
29
import io.grpc.InsecureServerCredentials;
30
import io.grpc.Internal;
31
import io.grpc.MetricRecorder;
32
import io.grpc.ServerBuilder;
33
import io.grpc.ServerCredentials;
34
import io.grpc.ServerStreamTracer;
35
import io.grpc.TlsServerCredentials;
36
import io.grpc.internal.FixedObjectPool;
37
import io.grpc.internal.GrpcUtil;
38
import io.grpc.internal.InternalServer;
39
import io.grpc.internal.KeepAliveManager;
40
import io.grpc.internal.ObjectPool;
41
import io.grpc.internal.ServerImplBuilder;
42
import io.grpc.internal.SharedResourcePool;
43
import io.grpc.internal.TransportTracer;
44
import io.grpc.okhttp.internal.Platform;
45
import java.io.IOException;
46
import java.net.InetAddress;
47
import java.net.InetSocketAddress;
48
import java.net.Socket;
49
import java.net.SocketAddress;
50
import java.security.GeneralSecurityException;
51
import java.util.EnumSet;
52
import java.util.List;
53
import java.util.Set;
54
import java.util.concurrent.Executor;
55
import java.util.concurrent.ScheduledExecutorService;
56
import java.util.concurrent.TimeUnit;
57
import java.util.logging.Level;
58
import java.util.logging.Logger;
59
import javax.net.ServerSocketFactory;
60
import javax.net.ssl.KeyManager;
61
import javax.net.ssl.SSLContext;
62
import javax.net.ssl.SSLSocket;
63
import javax.net.ssl.SSLSocketFactory;
64
import javax.net.ssl.TrustManager;
65

66
/**
67
 * Build servers with the OkHttp transport.
68
 *
69
 * @since 1.49.0
70
 */
71
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1785")
72
public final class OkHttpServerBuilder extends ForwardingServerBuilder<OkHttpServerBuilder> {
73
  private static final Logger log = Logger.getLogger(OkHttpServerBuilder.class.getName());
1✔
74
  private static final int DEFAULT_FLOW_CONTROL_WINDOW = 65535;
75

76
  static final long MAX_CONNECTION_IDLE_NANOS_DISABLED = Long.MAX_VALUE;
77
  private static final long MIN_MAX_CONNECTION_IDLE_NANO = TimeUnit.SECONDS.toNanos(1L);
1✔
78
  static final long MAX_CONNECTION_AGE_NANOS_DISABLED = Long.MAX_VALUE;
79
  static final long MAX_CONNECTION_AGE_GRACE_NANOS_INFINITE = Long.MAX_VALUE;
80
  static final int MAX_CONCURRENT_STREAMS = Integer.MAX_VALUE;
81
  private static final long MIN_MAX_CONNECTION_AGE_NANO = TimeUnit.SECONDS.toNanos(1L);
1✔
82

83
  private static final long AS_LARGE_AS_INFINITE = TimeUnit.DAYS.toNanos(1000L);
1✔
84
  private static final ObjectPool<Executor> DEFAULT_TRANSPORT_EXECUTOR_POOL =
1✔
85
      OkHttpChannelBuilder.DEFAULT_TRANSPORT_EXECUTOR_POOL;
86

87
  /**
88
   * Always throws, to shadow {@code ServerBuilder.forPort()}.
89
   *
90
   * @deprecated Use {@link #forPort(int, ServerCredentials)} instead
91
   */
92
  @DoNotCall("Always throws. Use forPort(int, ServerCredentials) instead")
93
  @Deprecated
94
  public static OkHttpServerBuilder forPort(int port) {
95
    throw new UnsupportedOperationException("Use forPort(int, ServerCredentials) instead");
×
96
  }
97

98
  /**
99
   * Creates a builder for a server listening on {@code port}.
100
   */
101
  public static OkHttpServerBuilder forPort(int port, ServerCredentials creds) {
102
    return forPort(new InetSocketAddress(port), creds);
1✔
103
  }
104

105
  /**
106
   * Creates a builder for a server listening on {@code address}.
107
   */
108
  public static OkHttpServerBuilder forPort(SocketAddress address, ServerCredentials creds) {
109
    HandshakerSocketFactoryResult result = handshakerSocketFactoryFrom(creds);
1✔
110
    if (result.error != null) {
1✔
111
      throw new IllegalArgumentException(result.error);
×
112
    }
113
    return new OkHttpServerBuilder(address, result.factory);
1✔
114
  }
115

116
  final ServerImplBuilder serverImplBuilder = new ServerImplBuilder(
1✔
117
      new ServerImplBuilder.ClientTransportServersBuilder() {
1✔
118
        @Override
119
        public InternalServer buildClientTransportServers(
120
            List<? extends ServerStreamTracer.Factory> streamTracerFactories,
121
            MetricRecorder metricRecorder) {
122
          return buildTransportServers(streamTracerFactories);
1✔
123
        }
124
      });
125
  final SocketAddress listenAddress;
126
  final HandshakerSocketFactory handshakerSocketFactory;
127
  TransportTracer.Factory transportTracerFactory = TransportTracer.getDefaultFactory();
1✔
128

129
  ObjectPool<Executor> transportExecutorPool = DEFAULT_TRANSPORT_EXECUTOR_POOL;
1✔
130
  ObjectPool<ScheduledExecutorService> scheduledExecutorServicePool =
1✔
131
      SharedResourcePool.forResource(GrpcUtil.TIMER_SERVICE);
1✔
132

133
  ServerSocketFactory socketFactory = ServerSocketFactory.getDefault();
1✔
134
  long keepAliveTimeNanos = GrpcUtil.DEFAULT_SERVER_KEEPALIVE_TIME_NANOS;
1✔
135
  long keepAliveTimeoutNanos = GrpcUtil.DEFAULT_SERVER_KEEPALIVE_TIMEOUT_NANOS;
1✔
136
  int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW;
1✔
137
  int maxInboundMetadataSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE;
1✔
138
  int maxInboundMessageSize = GrpcUtil.DEFAULT_MAX_MESSAGE_SIZE;
1✔
139
  long maxConnectionIdleInNanos = MAX_CONNECTION_IDLE_NANOS_DISABLED;
1✔
140
  boolean permitKeepAliveWithoutCalls;
141
  long permitKeepAliveTimeInNanos = DEFAULT_SERVER_PERMIT_KEEPALIVE_TIME_NANOS;
1✔
142
  long maxConnectionAgeInNanos = MAX_CONNECTION_AGE_NANOS_DISABLED;
1✔
143
  long maxConnectionAgeGraceInNanos = MAX_CONNECTION_AGE_GRACE_NANOS_INFINITE;
1✔
144
  int maxConcurrentCallsPerConnection = MAX_CONCURRENT_STREAMS;
1✔
145

146
  OkHttpServerBuilder(
147
      SocketAddress address, HandshakerSocketFactory handshakerSocketFactory) {
1✔
148
    this.listenAddress = Preconditions.checkNotNull(address, "address");
1✔
149
    this.handshakerSocketFactory =
1✔
150
        Preconditions.checkNotNull(handshakerSocketFactory, "handshakerSocketFactory");
1✔
151
  }
1✔
152

153
  @Internal
154
  @Override
155
  protected ServerBuilder<?> delegate() {
156
    return serverImplBuilder;
1✔
157
  }
158

159
  // @VisibleForTesting
160
  OkHttpServerBuilder setTransportTracerFactory(TransportTracer.Factory transportTracerFactory) {
161
    this.transportTracerFactory = transportTracerFactory;
1✔
162
    return this;
1✔
163
  }
164

165
  /**
166
   * Override the default executor necessary for internal transport use.
167
   *
168
   * <p>The channel does not take ownership of the given executor. It is the caller' responsibility
169
   * to shutdown the executor when appropriate.
170
   */
171
  public OkHttpServerBuilder transportExecutor(Executor transportExecutor) {
172
    if (transportExecutor == null) {
1✔
173
      this.transportExecutorPool = DEFAULT_TRANSPORT_EXECUTOR_POOL;
×
174
    } else {
175
      this.transportExecutorPool = new FixedObjectPool<>(transportExecutor);
1✔
176
    }
177
    return this;
1✔
178
  }
179

180
  /**
181
   * Override the default {@link ServerSocketFactory} used to listen. If the socket factory is not
182
   * set or set to null, a default one will be used.
183
   */
184
  public OkHttpServerBuilder socketFactory(ServerSocketFactory socketFactory) {
185
    if (socketFactory == null) {
×
186
      this.socketFactory = ServerSocketFactory.getDefault();
×
187
    } else {
188
      this.socketFactory = socketFactory;
×
189
    }
190
    return this;
×
191
  }
192

193
  /**
194
   * Sets the time without read activity before sending a keepalive ping. An unreasonably small
195
   * value might be increased, and {@code Long.MAX_VALUE} nano seconds or an unreasonably large
196
   * value will disable keepalive. Defaults to two hours.
197
   *
198
   * @throws IllegalArgumentException if time is not positive
199
   */
200
  @Override
201
  public OkHttpServerBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) {
202
    Preconditions.checkArgument(keepAliveTime > 0L, "keepalive time must be positive");
×
203
    keepAliveTimeNanos = timeUnit.toNanos(keepAliveTime);
×
204
    keepAliveTimeNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeNanos);
×
205
    if (keepAliveTimeNanos >= AS_LARGE_AS_INFINITE) {
×
206
      // Bump keepalive time to infinite. This disables keepalive.
207
      keepAliveTimeNanos = GrpcUtil.KEEPALIVE_TIME_NANOS_DISABLED;
×
208
    }
209
    return this;
×
210
  }
211

212
  /**
213
   * Sets a custom max connection idle time, connection being idle for longer than which will be
214
   * gracefully terminated. Idleness duration is defined since the most recent time the number of
215
   * outstanding RPCs became zero or the connection establishment. An unreasonably small value might
216
   * be increased. {@code Long.MAX_VALUE} nano seconds or an unreasonably large value will disable
217
   * max connection idle.
218
   */
219
  @Override
220
  public OkHttpServerBuilder maxConnectionIdle(long maxConnectionIdle, TimeUnit timeUnit) {
221
    checkArgument(maxConnectionIdle > 0L, "max connection idle must be positive: %s",
1✔
222
        maxConnectionIdle);
223
    maxConnectionIdleInNanos = timeUnit.toNanos(maxConnectionIdle);
1✔
224
    if (maxConnectionIdleInNanos >= AS_LARGE_AS_INFINITE) {
1✔
225
      maxConnectionIdleInNanos = MAX_CONNECTION_IDLE_NANOS_DISABLED;
×
226
    }
227
    if (maxConnectionIdleInNanos < MIN_MAX_CONNECTION_IDLE_NANO) {
1✔
228
      maxConnectionIdleInNanos = MIN_MAX_CONNECTION_IDLE_NANO;
×
229
    }
230
    return this;
1✔
231
  }
232

233
  /**
234
   * Sets a custom max connection age, connection lasting longer than which will be gracefully
235
   * terminated. An unreasonably small value might be increased.  A random jitter of +/-10% will be
236
   * added to it. {@code Long.MAX_VALUE} nano seconds or an unreasonably large value will disable
237
   * max connection age.
238
   */
239
  @Override
240
  public OkHttpServerBuilder maxConnectionAge(long maxConnectionAge, TimeUnit timeUnit) {
241
    checkArgument(maxConnectionAge > 0L, "max connection age must be positive: %s",
1✔
242
        maxConnectionAge);
243
    maxConnectionAgeInNanos = timeUnit.toNanos(maxConnectionAge);
1✔
244
    if (maxConnectionAgeInNanos >= AS_LARGE_AS_INFINITE) {
1✔
245
      maxConnectionAgeInNanos = MAX_CONNECTION_AGE_NANOS_DISABLED;
×
246
    }
247
    if (maxConnectionAgeInNanos < MIN_MAX_CONNECTION_AGE_NANO) {
1✔
248
      maxConnectionAgeInNanos = MIN_MAX_CONNECTION_AGE_NANO;
×
249
    }
250
    return this;
1✔
251
  }
252

253
  /**
254
   * Sets a custom grace time for the graceful connection termination. Once the max connection age
255
   * is reached, RPCs have the grace time to complete. RPCs that do not complete in time will be
256
   * cancelled, allowing the connection to terminate. {@code Long.MAX_VALUE} nano seconds or an
257
   * unreasonably large value are considered infinite.
258
   *
259
   * @see #maxConnectionAge(long, TimeUnit)
260
   */
261
  @Override
262
  public OkHttpServerBuilder maxConnectionAgeGrace(long maxConnectionAgeGrace, TimeUnit timeUnit) {
263
    checkArgument(maxConnectionAgeGrace >= 0L, "max connection age grace must be non-negative: %s",
1✔
264
        maxConnectionAgeGrace);
265
    maxConnectionAgeGraceInNanos = timeUnit.toNanos(maxConnectionAgeGrace);
1✔
266
    if (maxConnectionAgeGraceInNanos >= AS_LARGE_AS_INFINITE) {
1✔
267
      maxConnectionAgeGraceInNanos = MAX_CONNECTION_AGE_GRACE_NANOS_INFINITE;
×
268
    }
269
    return this;
1✔
270
  }
271

272
  /**
273
   * Sets a time waiting for read activity after sending a keepalive ping. If the time expires
274
   * without any read activity on the connection, the connection is considered dead. An unreasonably
275
   * small value might be increased. Defaults to 20 seconds.
276
   *
277
   * <p>This value should be at least multiple times the RTT to allow for lost packets.
278
   *
279
   * @throws IllegalArgumentException if timeout is not positive
280
   */
281
  @Override
282
  public OkHttpServerBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) {
283
    Preconditions.checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive");
×
284
    keepAliveTimeoutNanos = timeUnit.toNanos(keepAliveTimeout);
×
285
    keepAliveTimeoutNanos = KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutNanos);
×
286
    return this;
×
287
  }
288

289
  /**
290
   * Specify the most aggressive keep-alive time clients are permitted to configure. The server will
291
   * try to detect clients exceeding this rate and when detected will forcefully close the
292
   * connection. The default is 5 minutes.
293
   *
294
   * <p>Even though a default is defined that allows some keep-alives, clients must not use
295
   * keep-alive without approval from the service owner. Otherwise, they may experience failures in
296
   * the future if the service becomes more restrictive. When unthrottled, keep-alives can cause a
297
   * significant amount of traffic and CPU usage, so clients and servers should be conservative in
298
   * what they use and accept.
299
   *
300
   * @see #permitKeepAliveWithoutCalls(boolean)
301
   */
302
  @CanIgnoreReturnValue
303
  @Override
304
  public OkHttpServerBuilder permitKeepAliveTime(long keepAliveTime, TimeUnit timeUnit) {
305
    checkArgument(keepAliveTime >= 0, "permit keepalive time must be non-negative: %s",
1✔
306
        keepAliveTime);
307
    permitKeepAliveTimeInNanos = timeUnit.toNanos(keepAliveTime);
1✔
308
    return this;
1✔
309
  }
310

311
  /**
312
   * Sets whether to allow clients to send keep-alive HTTP/2 PINGs even if there are no outstanding
313
   * RPCs on the connection. Defaults to {@code false}.
314
   *
315
   * @see #permitKeepAliveTime(long, TimeUnit)
316
   */
317
  @CanIgnoreReturnValue
318
  @Override
319
  public OkHttpServerBuilder permitKeepAliveWithoutCalls(boolean permit) {
320
    permitKeepAliveWithoutCalls = permit;
1✔
321
    return this;
1✔
322
  }
323

324
  /**
325
   * Sets the flow control window in bytes. If not called, the default value is 64 KiB.
326
   */
327
  public OkHttpServerBuilder flowControlWindow(int flowControlWindow) {
328
    Preconditions.checkState(flowControlWindow > 0, "flowControlWindow must be positive");
1✔
329
    this.flowControlWindow = flowControlWindow;
1✔
330
    return this;
1✔
331
  }
332

333
  /**
334
   * Provides a custom scheduled executor service.
335
   *
336
   * <p>It's an optional parameter. If the user has not provided a scheduled executor service when
337
   * the channel is built, the builder will use a static thread pool.
338
   *
339
   * @return this
340
   */
341
  public OkHttpServerBuilder scheduledExecutorService(
342
      ScheduledExecutorService scheduledExecutorService) {
343
    this.scheduledExecutorServicePool = new FixedObjectPool<>(
1✔
344
        Preconditions.checkNotNull(scheduledExecutorService, "scheduledExecutorService"));
1✔
345
    return this;
1✔
346
  }
347

348
  /**
349
   * Sets the maximum size of metadata allowed to be received. Defaults to 8 KiB.
350
   *
351
   * <p>The implementation does not currently limit memory usage; this value is checked only after
352
   * the metadata is decoded from the wire. It does prevent large metadata from being passed to the
353
   * application.
354
   *
355
   * @param bytes the maximum size of received metadata
356
   * @return this
357
   * @throws IllegalArgumentException if bytes is non-positive
358
   */
359
  @Override
360
  public OkHttpServerBuilder maxInboundMetadataSize(int bytes) {
361
    Preconditions.checkArgument(bytes > 0, "maxInboundMetadataSize must be > 0");
×
362
    this.maxInboundMetadataSize = bytes;
×
363
    return this;
×
364
  }
365

366
  /**
367
   * The maximum number of concurrent calls permitted for each incoming connection. Defaults to no
368
   * limit.
369
   */
370
  @CanIgnoreReturnValue
371
  public OkHttpServerBuilder maxConcurrentCallsPerConnection(int maxConcurrentCallsPerConnection) {
372
    checkArgument(maxConcurrentCallsPerConnection > 0,
1✔
373
        "max must be positive: %s", maxConcurrentCallsPerConnection);
374
    this.maxConcurrentCallsPerConnection = maxConcurrentCallsPerConnection;
1✔
375
    return this;
1✔
376
  }
377

378
  /**
379
   * Sets the maximum message size allowed to be received on the server. If not called, defaults to
380
   * defaults to 4 MiB. The default provides protection to servers who haven't considered the
381
   * possibility of receiving large messages while trying to be large enough to not be hit in normal
382
   * usage.
383
   *
384
   * @param bytes the maximum number of bytes a single message can be.
385
   * @return this
386
   * @throws IllegalArgumentException if bytes is negative.
387
   */
388
  @Override
389
  public OkHttpServerBuilder maxInboundMessageSize(int bytes) {
390
    Preconditions.checkArgument(bytes >= 0, "negative max bytes");
1✔
391
    maxInboundMessageSize = bytes;
1✔
392
    return this;
1✔
393
  }
394

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

399
  InternalServer buildTransportServers(
400
      List<? extends ServerStreamTracer.Factory> streamTracerFactories) {
401
    return new OkHttpServer(this, streamTracerFactories, serverImplBuilder.getChannelz());
1✔
402
  }
403

404
  private static final EnumSet<TlsServerCredentials.Feature> understoodTlsFeatures =
1✔
405
      EnumSet.of(
1✔
406
          TlsServerCredentials.Feature.MTLS, TlsServerCredentials.Feature.CUSTOM_MANAGERS);
407

408
  static HandshakerSocketFactoryResult handshakerSocketFactoryFrom(ServerCredentials creds) {
409
    if (creds instanceof TlsServerCredentials) {
1✔
410
      TlsServerCredentials tlsCreds = (TlsServerCredentials) creds;
1✔
411
      Set<TlsServerCredentials.Feature> incomprehensible =
1✔
412
          tlsCreds.incomprehensible(understoodTlsFeatures);
1✔
413
      if (!incomprehensible.isEmpty()) {
1✔
414
        return HandshakerSocketFactoryResult.error(
×
415
            "TLS features not understood: " + incomprehensible);
416
      }
417
      KeyManager[] km = null;
1✔
418
      if (tlsCreds.getKeyManagers() != null) {
1✔
419
        km = tlsCreds.getKeyManagers().toArray(new KeyManager[0]);
×
420
      } else if (tlsCreds.getPrivateKey() != null) {
1✔
421
        if (tlsCreds.getPrivateKeyPassword() != null) {
1✔
422
          return HandshakerSocketFactoryResult.error("byte[]-based private key with password "
×
423
              + "unsupported. Use unencrypted file or KeyManager");
424
        }
425
        try {
426
          km = OkHttpChannelBuilder.createKeyManager(
1✔
427
              tlsCreds.getCertificateChain(), tlsCreds.getPrivateKey());
1✔
428
        } catch (GeneralSecurityException gse) {
×
429
          log.log(Level.FINE, "Exception loading private key from credential", gse);
×
430
          return HandshakerSocketFactoryResult.error(
×
431
              "Unable to load private key: " + gse.getMessage());
×
432
        }
1✔
433
      } // else don't have a client cert
434
      TrustManager[] tm = null;
1✔
435
      if (tlsCreds.getTrustManagers() != null) {
1✔
436
        tm = tlsCreds.getTrustManagers().toArray(new TrustManager[0]);
×
437
      } else if (tlsCreds.getRootCertificates() != null) {
1✔
438
        try {
439
          tm = createTrustManager(tlsCreds.getRootCertificates());
1✔
440
        } catch (GeneralSecurityException gse) {
×
441
          log.log(Level.FINE, "Exception loading root certificates from credential", gse);
×
442
          return HandshakerSocketFactoryResult.error(
×
443
              "Unable to load root certificates: " + gse.getMessage());
×
444
        }
1✔
445
      } // else use system default
446
      SSLContext sslContext;
447
      try {
448
        sslContext = SSLContext.getInstance("TLS", Platform.get().getProvider());
1✔
449
        sslContext.init(km, tm, null);
1✔
450
      } catch (GeneralSecurityException gse) {
×
451
        throw new RuntimeException("TLS Provider failure", gse);
×
452
      }
1✔
453
      SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
1✔
454
      switch (tlsCreds.getClientAuth()) {
1✔
455
        case OPTIONAL:
456
          sslSocketFactory = new ClientCertRequestingSocketFactory(sslSocketFactory, false);
1✔
457
          break;
1✔
458

459
        case REQUIRE:
460
          sslSocketFactory = new ClientCertRequestingSocketFactory(sslSocketFactory, true);
1✔
461
          break;
1✔
462

463
        case NONE:
464
          // NOOP; this is the SSLContext default
465
          break;
1✔
466

467
        default:
468
          return HandshakerSocketFactoryResult.error(
×
469
              "Unknown TlsServerCredentials.ClientAuth value: " + tlsCreds.getClientAuth());
×
470
      }
471
      return HandshakerSocketFactoryResult.factory(new TlsServerHandshakerSocketFactory(
1✔
472
          new SslSocketFactoryServerCredentials.ServerCredentials(sslSocketFactory)));
473

474
    } else if (creds instanceof InsecureServerCredentials) {
1✔
475
      return HandshakerSocketFactoryResult.factory(new PlaintextHandshakerSocketFactory());
1✔
476

477
    } else if (creds instanceof SslSocketFactoryServerCredentials.ServerCredentials) {
1✔
478
      SslSocketFactoryServerCredentials.ServerCredentials factoryCreds =
×
479
          (SslSocketFactoryServerCredentials.ServerCredentials) creds;
480
      return HandshakerSocketFactoryResult.factory(
×
481
          new TlsServerHandshakerSocketFactory(factoryCreds));
482

483
    } else if (creds instanceof ChoiceServerCredentials) {
1✔
484
      ChoiceServerCredentials choiceCreds = (ChoiceServerCredentials) creds;
×
485
      StringBuilder error = new StringBuilder();
×
486
      for (ServerCredentials innerCreds : choiceCreds.getCredentialsList()) {
×
487
        HandshakerSocketFactoryResult result = handshakerSocketFactoryFrom(innerCreds);
×
488
        if (result.error == null) {
×
489
          return result;
×
490
        }
491
        error.append(", ");
×
492
        error.append(result.error);
×
493
      }
×
494
      return HandshakerSocketFactoryResult.error(error.substring(2));
×
495

496
    } else {
497
      return HandshakerSocketFactoryResult.error(
1✔
498
          "Unsupported credential type: " + creds.getClass().getName());
1✔
499
    }
500
  }
501

502
  static final class HandshakerSocketFactoryResult {
503
    public final HandshakerSocketFactory factory;
504
    public final String error;
505

506
    private HandshakerSocketFactoryResult(HandshakerSocketFactory factory, String error) {
1✔
507
      this.factory = factory;
1✔
508
      this.error = error;
1✔
509
    }
1✔
510

511
    public static HandshakerSocketFactoryResult error(String error) {
512
      return new HandshakerSocketFactoryResult(
1✔
513
          null, Preconditions.checkNotNull(error, "error"));
1✔
514
    }
515

516
    public static HandshakerSocketFactoryResult factory(HandshakerSocketFactory factory) {
517
      return new HandshakerSocketFactoryResult(
1✔
518
          Preconditions.checkNotNull(factory, "factory"), null);
1✔
519
    }
520
  }
521

522
  static final class ClientCertRequestingSocketFactory extends SSLSocketFactory {
523
    private final SSLSocketFactory socketFactory;
524
    private final boolean required;
525

526
    public ClientCertRequestingSocketFactory(SSLSocketFactory socketFactory, boolean required) {
1✔
527
      this.socketFactory = Preconditions.checkNotNull(socketFactory, "socketFactory");
1✔
528
      this.required = required;
1✔
529
    }
1✔
530

531
    private Socket apply(Socket s) throws IOException {
532
      if (!(s instanceof SSLSocket)) {
1✔
533
        throw new IOException(
×
534
            "SocketFactory " + socketFactory + " did not produce an SSLSocket: " + s.getClass());
×
535
      }
536
      SSLSocket sslSocket = (SSLSocket) s;
1✔
537
      if (required) {
1✔
538
        sslSocket.setNeedClientAuth(true);
1✔
539
      } else {
540
        sslSocket.setWantClientAuth(true);
1✔
541
      }
542
      return sslSocket;
1✔
543
    }
544

545
    @Override public Socket createSocket(Socket s, String host, int port, boolean autoClose)
546
        throws IOException {
547
      return apply(socketFactory.createSocket(s, host, port, autoClose));
1✔
548
    }
549

550
    @Override public Socket createSocket(String host, int port) throws IOException {
551
      return apply(socketFactory.createSocket(host, port));
×
552
    }
553

554
    @Override public Socket createSocket(
555
        String host, int port, InetAddress localHost, int localPort) throws IOException {
556
      return apply(socketFactory.createSocket(host, port, localHost, localPort));
×
557
    }
558

559
    @Override public Socket createSocket(InetAddress host, int port) throws IOException {
560
      return apply(socketFactory.createSocket(host, port));
×
561
    }
562

563
    @Override public Socket createSocket(
564
        InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException {
565
      return apply(socketFactory.createSocket(host, port, localAddress, localPort));
×
566
    }
567

568
    @Override public String[] getDefaultCipherSuites() {
569
      return socketFactory.getDefaultCipherSuites();
×
570
    }
571

572
    @Override public String[] getSupportedCipherSuites() {
573
      return socketFactory.getSupportedCipherSuites();
×
574
    }
575
  }
576
}
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