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

grpc / grpc-java / #20112

05 Dec 2025 11:36PM UTC coverage: 88.709%. Remained the same
#20112

push

github

web-flow
netty: Run handshakeCompleteRunnable in success cases

b/458734698

35456 of 39969 relevant lines covered (88.71%)

0.89 hits per line

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

91.57
/../netty/src/main/java/io/grpc/netty/ProtocolNegotiators.java
1
/*
2
 * Copyright 2015 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.netty;
18

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

21
import com.google.common.annotations.VisibleForTesting;
22
import com.google.common.base.Optional;
23
import com.google.common.base.Preconditions;
24
import com.google.errorprone.annotations.ForOverride;
25
import io.grpc.Attributes;
26
import io.grpc.CallCredentials;
27
import io.grpc.ChannelCredentials;
28
import io.grpc.ChannelLogger;
29
import io.grpc.ChannelLogger.ChannelLogLevel;
30
import io.grpc.ChoiceChannelCredentials;
31
import io.grpc.ChoiceServerCredentials;
32
import io.grpc.CompositeCallCredentials;
33
import io.grpc.CompositeChannelCredentials;
34
import io.grpc.Grpc;
35
import io.grpc.InsecureChannelCredentials;
36
import io.grpc.InsecureServerCredentials;
37
import io.grpc.InternalChannelz.Security;
38
import io.grpc.InternalChannelz.Tls;
39
import io.grpc.SecurityLevel;
40
import io.grpc.ServerCredentials;
41
import io.grpc.Status;
42
import io.grpc.TlsChannelCredentials;
43
import io.grpc.TlsServerCredentials;
44
import io.grpc.internal.CertificateUtils;
45
import io.grpc.internal.GrpcAttributes;
46
import io.grpc.internal.GrpcUtil;
47
import io.grpc.internal.NoopSslSession;
48
import io.grpc.internal.ObjectPool;
49
import io.netty.channel.ChannelDuplexHandler;
50
import io.netty.channel.ChannelFutureListener;
51
import io.netty.channel.ChannelHandler;
52
import io.netty.channel.ChannelHandlerContext;
53
import io.netty.channel.ChannelInboundHandlerAdapter;
54
import io.netty.handler.codec.http.DefaultHttpHeaders;
55
import io.netty.handler.codec.http.DefaultHttpRequest;
56
import io.netty.handler.codec.http.HttpClientCodec;
57
import io.netty.handler.codec.http.HttpClientUpgradeHandler;
58
import io.netty.handler.codec.http.HttpHeaderNames;
59
import io.netty.handler.codec.http.HttpHeaders;
60
import io.netty.handler.codec.http.HttpMethod;
61
import io.netty.handler.codec.http.HttpVersion;
62
import io.netty.handler.codec.http2.Http2ClientUpgradeCodec;
63
import io.netty.handler.proxy.HttpProxyHandler;
64
import io.netty.handler.proxy.ProxyConnectionEvent;
65
import io.netty.handler.ssl.OpenSsl;
66
import io.netty.handler.ssl.OpenSslEngine;
67
import io.netty.handler.ssl.SslContext;
68
import io.netty.handler.ssl.SslContextBuilder;
69
import io.netty.handler.ssl.SslHandler;
70
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
71
import io.netty.handler.ssl.SslProvider;
72
import io.netty.util.AsciiString;
73
import java.io.ByteArrayInputStream;
74
import java.net.SocketAddress;
75
import java.net.URI;
76
import java.nio.channels.ClosedChannelException;
77
import java.security.GeneralSecurityException;
78
import java.security.KeyStore;
79
import java.util.Arrays;
80
import java.util.EnumSet;
81
import java.util.List;
82
import java.util.Map;
83
import java.util.Set;
84
import java.util.concurrent.Executor;
85
import java.util.logging.Level;
86
import java.util.logging.Logger;
87
import javax.annotation.Nullable;
88
import javax.net.ssl.SSLEngine;
89
import javax.net.ssl.SSLException;
90
import javax.net.ssl.SSLParameters;
91
import javax.net.ssl.SSLSession;
92
import javax.net.ssl.TrustManager;
93
import javax.net.ssl.TrustManagerFactory;
94
import javax.net.ssl.X509TrustManager;
95
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
96

97
/**
98
 * Common {@link ProtocolNegotiator}s used by gRPC.
99
 */
100
final class ProtocolNegotiators {
101
  private static final Logger log = Logger.getLogger(ProtocolNegotiators.class.getName());
1✔
102
  private static final EnumSet<TlsChannelCredentials.Feature> understoodTlsFeatures =
1✔
103
      EnumSet.of(
1✔
104
          TlsChannelCredentials.Feature.MTLS, TlsChannelCredentials.Feature.CUSTOM_MANAGERS);
105
  private static final EnumSet<TlsServerCredentials.Feature> understoodServerTlsFeatures =
1✔
106
      EnumSet.of(
1✔
107
          TlsServerCredentials.Feature.MTLS, TlsServerCredentials.Feature.CUSTOM_MANAGERS);
108

109
  private ProtocolNegotiators() {
110
  }
111

112
  public static FromChannelCredentialsResult from(ChannelCredentials creds) {
113
    if (creds instanceof TlsChannelCredentials) {
1✔
114
      TlsChannelCredentials tlsCreds = (TlsChannelCredentials) creds;
1✔
115
      Set<TlsChannelCredentials.Feature> incomprehensible =
1✔
116
          tlsCreds.incomprehensible(understoodTlsFeatures);
1✔
117
      if (!incomprehensible.isEmpty()) {
1✔
118
        return FromChannelCredentialsResult.error(
1✔
119
            "TLS features not understood: " + incomprehensible);
120
      }
121
      SslContextBuilder builder = GrpcSslContexts.forClient();
1✔
122
      if (tlsCreds.getKeyManagers() != null) {
1✔
123
        builder.keyManager(new FixedKeyManagerFactory(tlsCreds.getKeyManagers()));
1✔
124
      } else if (tlsCreds.getPrivateKey() != null) {
1✔
125
        builder.keyManager(
1✔
126
            new ByteArrayInputStream(tlsCreds.getCertificateChain()),
1✔
127
            new ByteArrayInputStream(tlsCreds.getPrivateKey()),
1✔
128
            tlsCreds.getPrivateKeyPassword());
1✔
129
      }
130
      try {
131
        List<TrustManager> trustManagers;
132
        if (tlsCreds.getTrustManagers() != null) {
1✔
133
          trustManagers = tlsCreds.getTrustManagers();
1✔
134
        } else if (tlsCreds.getRootCertificates() != null) {
1✔
135
          trustManagers = Arrays.asList(CertificateUtils.createTrustManager(
1✔
136
                  new ByteArrayInputStream(tlsCreds.getRootCertificates())));
1✔
137
        } else { // else use system default
138
          TrustManagerFactory tmf = TrustManagerFactory.getInstance(
1✔
139
              TrustManagerFactory.getDefaultAlgorithm());
1✔
140
          tmf.init((KeyStore) null);
1✔
141
          trustManagers = Arrays.asList(tmf.getTrustManagers());
1✔
142
        }
143
        builder.trustManager(new FixedTrustManagerFactory(trustManagers));
1✔
144
        TrustManager x509ExtendedTrustManager =
1✔
145
            CertificateUtils.getX509ExtendedTrustManager(trustManagers);
1✔
146
        return FromChannelCredentialsResult.negotiator(tlsClientFactory(builder.build(),
1✔
147
                (X509TrustManager) x509ExtendedTrustManager));
148
      } catch (SSLException | GeneralSecurityException ex) {
×
149
        log.log(Level.FINE, "Exception building SslContext", ex);
×
150
        return FromChannelCredentialsResult.error(
×
151
            "Unable to create SslContext: " + ex.getMessage());
×
152
      }
153

154
    } else if (creds instanceof InsecureChannelCredentials) {
1✔
155
      return FromChannelCredentialsResult.negotiator(plaintextClientFactory());
1✔
156

157
    } else if (creds instanceof CompositeChannelCredentials) {
1✔
158
      CompositeChannelCredentials compCreds = (CompositeChannelCredentials) creds;
1✔
159
      return from(compCreds.getChannelCredentials())
1✔
160
          .withCallCredentials(compCreds.getCallCredentials());
1✔
161

162
    } else if (creds instanceof NettyChannelCredentials) {
1✔
163
      NettyChannelCredentials nettyCreds = (NettyChannelCredentials) creds;
1✔
164
      return FromChannelCredentialsResult.negotiator(nettyCreds.getNegotiator());
1✔
165

166
    } else if (creds instanceof ChoiceChannelCredentials) {
1✔
167
      ChoiceChannelCredentials choiceCreds = (ChoiceChannelCredentials) creds;
1✔
168
      StringBuilder error = new StringBuilder();
1✔
169
      for (ChannelCredentials innerCreds : choiceCreds.getCredentialsList()) {
1✔
170
        FromChannelCredentialsResult result = from(innerCreds);
1✔
171
        if (result.error == null) {
1✔
172
          return result;
1✔
173
        }
174
        error.append(", ");
1✔
175
        error.append(result.error);
1✔
176
      }
1✔
177
      return FromChannelCredentialsResult.error(error.substring(2));
1✔
178

179
    } else {
180
      return FromChannelCredentialsResult.error(
1✔
181
          "Unsupported credential type: " + creds.getClass().getName());
1✔
182
    }
183
  }
184

185
  public static FromServerCredentialsResult from(ServerCredentials creds) {
186
    if (creds instanceof TlsServerCredentials) {
1✔
187
      TlsServerCredentials tlsCreds = (TlsServerCredentials) creds;
1✔
188
      Set<TlsServerCredentials.Feature> incomprehensible =
1✔
189
          tlsCreds.incomprehensible(understoodServerTlsFeatures);
1✔
190
      if (!incomprehensible.isEmpty()) {
1✔
191
        return FromServerCredentialsResult.error(
1✔
192
            "TLS features not understood: " + incomprehensible);
193
      }
194
      SslContextBuilder builder;
195
      if (tlsCreds.getKeyManagers() != null) {
1✔
196
        builder = GrpcSslContexts.configure(SslContextBuilder.forServer(
1✔
197
            new FixedKeyManagerFactory(tlsCreds.getKeyManagers())));
1✔
198
      } else if (tlsCreds.getPrivateKey() != null) {
1✔
199
        builder = GrpcSslContexts.forServer(
1✔
200
            new ByteArrayInputStream(tlsCreds.getCertificateChain()),
1✔
201
            new ByteArrayInputStream(tlsCreds.getPrivateKey()),
1✔
202
            tlsCreds.getPrivateKeyPassword());
1✔
203
      } else {
204
        throw new AssertionError("BUG! No key");
×
205
      }
206
      if (tlsCreds.getTrustManagers() != null) {
1✔
207
        builder.trustManager(new FixedTrustManagerFactory(tlsCreds.getTrustManagers()));
1✔
208
      } else if (tlsCreds.getRootCertificates() != null) {
1✔
209
        builder.trustManager(new ByteArrayInputStream(tlsCreds.getRootCertificates()));
1✔
210
      } // else use system default
211
      switch (tlsCreds.getClientAuth()) {
1✔
212
        case OPTIONAL:
213
          builder.clientAuth(io.netty.handler.ssl.ClientAuth.OPTIONAL);
1✔
214
          break;
1✔
215

216
        case REQUIRE:
217
          builder.clientAuth(io.netty.handler.ssl.ClientAuth.REQUIRE);
1✔
218
          break;
1✔
219

220
        case NONE:
221
          builder.clientAuth(io.netty.handler.ssl.ClientAuth.NONE);
1✔
222
          break;
1✔
223

224
        default:
225
          return FromServerCredentialsResult.error(
×
226
              "Unknown TlsServerCredentials.ClientAuth value: " + tlsCreds.getClientAuth());
×
227
      }
228
      SslContext sslContext;
229
      try {
230
        sslContext = builder.build();
1✔
231
      } catch (SSLException ex) {
×
232
        throw new IllegalArgumentException(
×
233
            "Unexpected error converting ServerCredentials to Netty SslContext", ex);
234
      }
1✔
235
      return FromServerCredentialsResult.negotiator(serverTlsFactory(sslContext));
1✔
236

237
    } else if (creds instanceof InsecureServerCredentials) {
1✔
238
      return FromServerCredentialsResult.negotiator(serverPlaintextFactory());
1✔
239

240
    } else if (creds instanceof NettyServerCredentials) {
1✔
241
      NettyServerCredentials nettyCreds = (NettyServerCredentials) creds;
1✔
242
      return FromServerCredentialsResult.negotiator(nettyCreds.getNegotiator());
1✔
243

244
    } else if (creds instanceof ChoiceServerCredentials) {
1✔
245
      ChoiceServerCredentials choiceCreds = (ChoiceServerCredentials) creds;
1✔
246
      StringBuilder error = new StringBuilder();
1✔
247
      for (ServerCredentials innerCreds : choiceCreds.getCredentialsList()) {
1✔
248
        FromServerCredentialsResult result = from(innerCreds);
1✔
249
        if (result.error == null) {
1✔
250
          return result;
1✔
251
        }
252
        error.append(", ");
1✔
253
        error.append(result.error);
1✔
254
      }
1✔
255
      return FromServerCredentialsResult.error(error.substring(2));
1✔
256

257
    } else {
258
      return FromServerCredentialsResult.error(
1✔
259
          "Unsupported credential type: " + creds.getClass().getName());
1✔
260
    }
261
  }
262

263
  public static final class FromChannelCredentialsResult {
264
    public final ProtocolNegotiator.ClientFactory negotiator;
265
    public final CallCredentials callCredentials;
266
    public final String error;
267

268
    private FromChannelCredentialsResult(ProtocolNegotiator.ClientFactory negotiator,
269
        CallCredentials creds, String error) {
1✔
270
      this.negotiator = negotiator;
1✔
271
      this.callCredentials = creds;
1✔
272
      this.error = error;
1✔
273
    }
1✔
274

275
    public static FromChannelCredentialsResult error(String error) {
276
      return new FromChannelCredentialsResult(
1✔
277
          null, null, Preconditions.checkNotNull(error, "error"));
1✔
278
    }
279

280
    public static FromChannelCredentialsResult negotiator(
281
        ProtocolNegotiator.ClientFactory factory) {
282
      return new FromChannelCredentialsResult(
1✔
283
          Preconditions.checkNotNull(factory, "factory"), null, null);
1✔
284
    }
285

286
    public FromChannelCredentialsResult withCallCredentials(CallCredentials callCreds) {
287
      Preconditions.checkNotNull(callCreds, "callCreds");
1✔
288
      if (error != null) {
1✔
289
        return this;
×
290
      }
291
      if (this.callCredentials != null) {
1✔
292
        callCreds = new CompositeCallCredentials(this.callCredentials, callCreds);
×
293
      }
294
      return new FromChannelCredentialsResult(negotiator, callCreds, null);
1✔
295
    }
296
  }
297

298
  public static final class FromServerCredentialsResult {
299
    public final ProtocolNegotiator.ServerFactory negotiator;
300
    public final String error;
301

302
    private FromServerCredentialsResult(ProtocolNegotiator.ServerFactory negotiator, String error) {
1✔
303
      this.negotiator = negotiator;
1✔
304
      this.error = error;
1✔
305
    }
1✔
306

307
    public static FromServerCredentialsResult error(String error) {
308
      return new FromServerCredentialsResult(null, Preconditions.checkNotNull(error, "error"));
1✔
309
    }
310

311
    public static FromServerCredentialsResult negotiator(ProtocolNegotiator.ServerFactory factory) {
312
      return new FromServerCredentialsResult(Preconditions.checkNotNull(factory, "factory"), null);
1✔
313
    }
314
  }
315

316
  public static ProtocolNegotiator.ServerFactory fixedServerFactory(
317
      ProtocolNegotiator negotiator) {
318
    return new FixedProtocolNegotiatorServerFactory(negotiator);
×
319
  }
320

321
  private static final class FixedProtocolNegotiatorServerFactory
322
      implements ProtocolNegotiator.ServerFactory {
323
    private final ProtocolNegotiator protocolNegotiator;
324

325
    public FixedProtocolNegotiatorServerFactory(ProtocolNegotiator protocolNegotiator) {
×
326
      this.protocolNegotiator =
×
327
          Preconditions.checkNotNull(protocolNegotiator, "protocolNegotiator");
×
328
    }
×
329

330
    @Override
331
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
332
      return protocolNegotiator;
×
333
    }
334
  }
335

336
  /**
337
   * Create a server plaintext handler for gRPC.
338
   */
339
  public static ProtocolNegotiator serverPlaintext() {
340
    return new PlaintextProtocolNegotiator();
1✔
341
  }
342

343
  /**
344
   * Create a server plaintext handler factory for gRPC.
345
   */
346
  public static ProtocolNegotiator.ServerFactory serverPlaintextFactory() {
347
    return new PlaintextProtocolNegotiatorServerFactory();
1✔
348
  }
349

350
  @VisibleForTesting
351
  static final class PlaintextProtocolNegotiatorServerFactory
1✔
352
      implements ProtocolNegotiator.ServerFactory {
353
    @Override
354
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
355
      return serverPlaintext();
1✔
356
    }
357
  }
358

359
  public static ProtocolNegotiator.ServerFactory serverTlsFactory(SslContext sslContext) {
360
    return new TlsProtocolNegotiatorServerFactory(sslContext);
1✔
361
  }
362

363
  @VisibleForTesting
364
  static final class TlsProtocolNegotiatorServerFactory
365
      implements ProtocolNegotiator.ServerFactory {
366
    private final SslContext sslContext;
367

368
    public TlsProtocolNegotiatorServerFactory(SslContext sslContext) {
1✔
369
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
370
    }
1✔
371

372
    @Override
373
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
374
      return serverTls(sslContext, offloadExecutorPool);
1✔
375
    }
376
  }
377

378
  /**
379
   * Create a server TLS handler for HTTP/2 capable of using ALPN/NPN.
380
   * @param executorPool a dedicated {@link Executor} pool for time-consuming TLS tasks
381
   */
382
  public static ProtocolNegotiator serverTls(final SslContext sslContext,
383
      final ObjectPool<? extends Executor> executorPool) {
384
    Preconditions.checkNotNull(sslContext, "sslContext");
1✔
385
    final Executor executor;
386
    if (executorPool != null) {
1✔
387
      // The handlers here can out-live the {@link ProtocolNegotiator}.
388
      // To keep their own reference to executor from executorPool, we use an extra (unused)
389
      // reference here forces the executor to stay alive, which prevents it from being re-created
390
      // for every connection.
391
      executor = executorPool.getObject();
1✔
392
    } else {
393
      executor = null;
1✔
394
    }
395
    return new ProtocolNegotiator() {
1✔
396
      @Override
397
      public ChannelHandler newHandler(GrpcHttp2ConnectionHandler handler) {
398
        ChannelHandler gnh = new GrpcNegotiationHandler(handler);
1✔
399
        ChannelHandler sth = new ServerTlsHandler(gnh, sslContext, executorPool);
1✔
400
        return new WaitUntilActiveHandler(sth, handler.getNegotiationLogger());
1✔
401
      }
402

403
      @Override
404
      public void close() {
405
        if (executorPool != null && executor != null) {
1✔
406
          executorPool.returnObject(executor);
1✔
407
        }
408
      }
1✔
409

410
      @Override
411
      public AsciiString scheme() {
412
        return Utils.HTTPS;
×
413
      }
414
    };
415
  }
416

417
  /**
418
   * Create a server TLS handler for HTTP/2 capable of using ALPN/NPN.
419
   */
420
  public static ProtocolNegotiator serverTls(final SslContext sslContext) {
421
    return serverTls(sslContext, null);
1✔
422
  }
423

424
  static final class ServerTlsHandler extends ChannelInboundHandlerAdapter {
425
    private Executor executor;
426
    private final ChannelHandler next;
427
    private final SslContext sslContext;
428

429
    private ProtocolNegotiationEvent pne = ProtocolNegotiationEvent.DEFAULT;
1✔
430

431
    ServerTlsHandler(ChannelHandler next,
432
        SslContext sslContext,
433
        final ObjectPool<? extends Executor> executorPool) {
1✔
434
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
435
      this.next = Preconditions.checkNotNull(next, "next");
1✔
436
      if (executorPool != null) {
1✔
437
        this.executor = executorPool.getObject();
1✔
438
      }
439
    }
1✔
440

441
    @Override
442
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
443
      super.handlerAdded(ctx);
1✔
444
      SSLEngine sslEngine = sslContext.newEngine(ctx.alloc());
1✔
445
      ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
1✔
446
          ? new SslHandler(sslEngine, false, this.executor)
1✔
447
          : new SslHandler(sslEngine, false));
1✔
448
    }
1✔
449

450
    @Override
451
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
452
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
453
        pne = (ProtocolNegotiationEvent) evt;
1✔
454
      } else if (evt instanceof SslHandshakeCompletionEvent) {
1✔
455
        SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
1✔
456
        if (!handshakeEvent.isSuccess()) {
1✔
457
          logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed for new client.", null);
1✔
458
          ctx.fireExceptionCaught(handshakeEvent.cause());
1✔
459
          return;
1✔
460
        }
461
        SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
1✔
462
        if (!sslContext.applicationProtocolNegotiator().protocols().contains(
1✔
463
                sslHandler.applicationProtocol())) {
1✔
464
          logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed for new client.", null);
1✔
465
          ctx.fireExceptionCaught(unavailableException(
1✔
466
              "Failed protocol negotiation: Unable to find compatible protocol"));
467
          return;
1✔
468
        }
469
        ctx.pipeline().replace(ctx.name(), null, next);
1✔
470
        fireProtocolNegotiationEvent(ctx, sslHandler.engine().getSession());
1✔
471
      } else {
1✔
472
        super.userEventTriggered(ctx, evt);
1✔
473
      }
474
    }
1✔
475

476
    private void fireProtocolNegotiationEvent(ChannelHandlerContext ctx, SSLSession session) {
477
      Security security = new Security(new Tls(session));
1✔
478
      Attributes attrs = pne.getAttributes().toBuilder()
1✔
479
          .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY)
1✔
480
          .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
1✔
481
          .build();
1✔
482
      ctx.fireUserEventTriggered(pne.withAttributes(attrs).withSecurity(security));
1✔
483
    }
1✔
484
  }
485

486
  /**
487
   * Returns a {@link ProtocolNegotiator} that does HTTP CONNECT proxy negotiation.
488
   */
489
  public static ProtocolNegotiator httpProxy(final SocketAddress proxyAddress,
490
      final @Nullable Map<String, String> headers, final @Nullable String proxyUsername,
491
      final @Nullable String proxyPassword,
492
      final ProtocolNegotiator negotiator) {
493
    Preconditions.checkNotNull(negotiator, "negotiator");
1✔
494
    Preconditions.checkNotNull(proxyAddress, "proxyAddress");
1✔
495
    final AsciiString scheme = negotiator.scheme();
1✔
496
    class ProxyNegotiator implements ProtocolNegotiator {
1✔
497
      @Override
498
      public ChannelHandler newHandler(GrpcHttp2ConnectionHandler http2Handler) {
499
        ChannelHandler protocolNegotiationHandler = negotiator.newHandler(http2Handler);
1✔
500
        ChannelLogger negotiationLogger = http2Handler.getNegotiationLogger();
1✔
501
        HttpHeaders httpHeaders = toHttpHeaders(headers);
1✔
502
        return new ProxyProtocolNegotiationHandler(
1✔
503
            proxyAddress, httpHeaders, proxyUsername, proxyPassword, protocolNegotiationHandler,
504
            negotiationLogger);
505
      }
506

507
      @Override
508
      public AsciiString scheme() {
509
        return scheme;
×
510
      }
511

512
      // This method is not normally called, because we use httpProxy on a per-connection basis in
513
      // NettyChannelBuilder. Instead, we expect `negotiator' to be closed by NettyTransportFactory.
514
      @Override
515
      public void close() {
516
        negotiator.close();
×
517
      }
×
518
    }
519

520
    return new ProxyNegotiator();
1✔
521
  }
522

523
  /**
524
   * Converts generic Map of headers to Netty's HttpHeaders.
525
   * Returns null if the map is null or empty.
526
   */
527
  @Nullable
528
  private static HttpHeaders toHttpHeaders(@Nullable Map<String, String> headers) {
529
    if (headers == null || headers.isEmpty()) {
1✔
530
      return null;
1✔
531
    }
532
    HttpHeaders httpHeaders = new DefaultHttpHeaders();
1✔
533
    for (Map.Entry<String, String> entry : headers.entrySet()) {
1✔
534
      httpHeaders.add(entry.getKey(), entry.getValue());
1✔
535
    }
1✔
536
    return httpHeaders;
1✔
537
  }
538

539
  /**
540
   * A Proxy handler follows {@link ProtocolNegotiationHandler} pattern. Upon successful proxy
541
   * connection, this handler will install {@code next} handler which should be a handler from
542
   * other type of {@link ProtocolNegotiator} to continue negotiating protocol using proxy.
543
   */
544
  static final class ProxyProtocolNegotiationHandler extends ProtocolNegotiationHandler {
545

546
    private final SocketAddress address;
547
    @Nullable private final HttpHeaders httpHeaders;
548
    @Nullable private final String userName;
549
    @Nullable private final String password;
550

551
    public ProxyProtocolNegotiationHandler(
552
        SocketAddress address,
553
        @Nullable HttpHeaders httpHeaders,
554
        @Nullable String userName,
555
        @Nullable String password,
556
        ChannelHandler next,
557
        ChannelLogger negotiationLogger) {
558
      super(next, negotiationLogger);
1✔
559
      this.address = Preconditions.checkNotNull(address, "address");
1✔
560
      this.httpHeaders = httpHeaders;
1✔
561
      this.userName = userName;
1✔
562
      this.password = password;
1✔
563
    }
1✔
564

565
    @Override
566
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
567
      HttpProxyHandler nettyProxyHandler;
568
      if (userName == null || password == null) {
1✔
569
        nettyProxyHandler = new HttpProxyHandler(address, httpHeaders);
1✔
570
      } else {
571
        nettyProxyHandler = new HttpProxyHandler(address, userName, password, httpHeaders);
×
572
      }
573
      ctx.pipeline().addBefore(ctx.name(), /* name= */ null, nettyProxyHandler);
1✔
574
    }
1✔
575

576
    @Override
577
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
578
      if (evt instanceof ProxyConnectionEvent) {
1✔
579
        fireProtocolNegotiationEvent(ctx);
1✔
580
      } else {
581
        super.userEventTriggered(ctx, evt);
×
582
      }
583
    }
1✔
584
  }
585

586
  static final class ClientTlsProtocolNegotiator implements ProtocolNegotiator {
587

588
    public ClientTlsProtocolNegotiator(SslContext sslContext,
589
        ObjectPool<? extends Executor> executorPool, Optional<Runnable> handshakeCompleteRunnable,
590
        X509TrustManager x509ExtendedTrustManager, String sni) {
1✔
591
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
592
      this.executorPool = executorPool;
1✔
593
      if (this.executorPool != null) {
1✔
594
        this.executor = this.executorPool.getObject();
1✔
595
      }
596
      this.handshakeCompleteRunnable = handshakeCompleteRunnable;
1✔
597
      this.x509ExtendedTrustManager = x509ExtendedTrustManager;
1✔
598
      this.sni = sni;
1✔
599
    }
1✔
600

601
    private final SslContext sslContext;
602
    private final ObjectPool<? extends Executor> executorPool;
603
    private final Optional<Runnable> handshakeCompleteRunnable;
604
    private final X509TrustManager x509ExtendedTrustManager;
605
    private final String sni;
606
    private Executor executor;
607

608
    @Override
609
    public AsciiString scheme() {
610
      return Utils.HTTPS;
1✔
611
    }
612

613
    @Override
614
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
615
      ChannelHandler gnh = new GrpcNegotiationHandler(grpcHandler);
1✔
616
      ChannelLogger negotiationLogger = grpcHandler.getNegotiationLogger();
1✔
617
      String authority;
618
      if ("".equals(sni)) {
1✔
619
        authority = null;
1✔
620
      } else if (sni != null) {
1✔
621
        authority = sni;
1✔
622
      } else {
623
        authority = grpcHandler.getAuthority();
1✔
624
      }
625
      ChannelHandler cth = new ClientTlsHandler(gnh, sslContext,
1✔
626
          authority, this.executor, negotiationLogger, handshakeCompleteRunnable, this,
627
          x509ExtendedTrustManager);
628
      return new WaitUntilActiveHandler(cth, negotiationLogger);
1✔
629
    }
630

631
    @Override
632
    public void close() {
633
      if (this.executorPool != null && this.executor != null) {
1✔
634
        this.executorPool.returnObject(this.executor);
1✔
635
      }
636
    }
1✔
637

638
    @VisibleForTesting
639
    boolean hasX509ExtendedTrustManager() {
640
      return x509ExtendedTrustManager != null;
1✔
641
    }
642
  }
643

644
  static final class ClientTlsHandler extends ProtocolNegotiationHandler {
645

646
    private final SslContext sslContext;
647
    private final String host;
648
    private final int port;
649
    private Executor executor;
650
    private final Optional<Runnable> handshakeCompleteRunnable;
651
    private final X509TrustManager x509TrustManager;
652
    private SSLEngine sslEngine;
653

654
    ClientTlsHandler(ChannelHandler next, SslContext sslContext, String authority,
655
        Executor executor, ChannelLogger negotiationLogger,
656
        Optional<Runnable> handshakeCompleteRunnable,
657
        ClientTlsProtocolNegotiator clientTlsProtocolNegotiator,
658
        X509TrustManager x509TrustManager) {
659
      super(next, negotiationLogger);
1✔
660
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
661
      if (authority != null) {
1✔
662
        HostPort hostPort = parseAuthority(authority);
1✔
663
        this.host = hostPort.host;
1✔
664
        this.port = hostPort.port;
1✔
665
      } else {
1✔
666
        this.host = null;
1✔
667
        this.port = 0;
1✔
668
      }
669
      this.executor = executor;
1✔
670
      this.handshakeCompleteRunnable = handshakeCompleteRunnable;
1✔
671
      this.x509TrustManager = x509TrustManager;
1✔
672
    }
1✔
673

674
    @Override
675
    @IgnoreJRERequirement
676
    protected void handlerAdded0(ChannelHandlerContext ctx) {
677
      if (host != null) {
1✔
678
        sslEngine = sslContext.newEngine(ctx.alloc(), host, port);
1✔
679
      } else {
680
        sslEngine = sslContext.newEngine(ctx.alloc());
1✔
681
      }
682
      SSLParameters sslParams = sslEngine.getSSLParameters();
1✔
683
      sslParams.setEndpointIdentificationAlgorithm("HTTPS");
1✔
684
      sslEngine.setSSLParameters(sslParams);
1✔
685
      ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
1✔
686
          ? new SslHandler(sslEngine, false, this.executor)
1✔
687
          : new SslHandler(sslEngine, false));
1✔
688
    }
1✔
689

690
    @Override
691
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
692
      if (evt instanceof SslHandshakeCompletionEvent) {
1✔
693
        SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
1✔
694
        if (handshakeEvent.isSuccess()) {
1✔
695
          SslHandler handler = ctx.pipeline().get(SslHandler.class);
1✔
696
          if (sslContext.applicationProtocolNegotiator().protocols()
1✔
697
              .contains(handler.applicationProtocol())) {
1✔
698
            // Successfully negotiated the protocol.
699
            logSslEngineDetails(Level.FINER, ctx, "TLS negotiation succeeded.", null);
1✔
700
            propagateTlsComplete(ctx, handler.engine().getSession());
1✔
701
          } else {
702
            Exception ex =
1✔
703
                unavailableException("Failed ALPN negotiation: Unable to find compatible protocol");
1✔
704
            logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed.", ex);
1✔
705
            ctx.fireExceptionCaught(ex);
1✔
706
          }
707
        } else {
1✔
708
          Throwable t = handshakeEvent.cause();
1✔
709
          if (t instanceof ClosedChannelException) {
1✔
710
            // On channelInactive(), SslHandler creates its own ClosedChannelException and
711
            // propagates it before the actual channelInactive(). So we assume here that any
712
            // such exception is from channelInactive() and emulate the normal behavior of
713
            // WriteBufferingAndExceptionHandler
714
            t = Status.UNAVAILABLE
1✔
715
                .withDescription("Connection closed while performing TLS negotiation")
1✔
716
                .withCause(t)
1✔
717
                .asRuntimeException();
1✔
718
          }
719
          ctx.fireExceptionCaught(t);
1✔
720
        }
721
        if (handshakeCompleteRunnable.isPresent()) {
1✔
722
          handshakeCompleteRunnable.get().run();
×
723
        }
724
      } else {
1✔
725
        super.userEventTriggered0(ctx, evt);
1✔
726
      }
727
    }
1✔
728

729
    private void propagateTlsComplete(ChannelHandlerContext ctx, SSLSession session) {
730
      Security security = new Security(new Tls(session));
1✔
731
      ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1✔
732
      Attributes attrs = existingPne.getAttributes().toBuilder()
1✔
733
          .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY)
1✔
734
          .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
1✔
735
          .set(GrpcAttributes.ATTR_AUTHORITY_VERIFIER, new X509AuthorityVerifier(
1✔
736
              sslEngine, x509TrustManager))
737
          .build();
1✔
738
      replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs).withSecurity(security));
1✔
739
      if (handshakeCompleteRunnable.isPresent()) {
1✔
740
        handshakeCompleteRunnable.get().run();
×
741
      }
742
      fireProtocolNegotiationEvent(ctx);
1✔
743
    }
1✔
744
  }
745

746
  @VisibleForTesting
747
  static HostPort parseAuthority(String authority) {
748
    URI uri = GrpcUtil.authorityToUri(Preconditions.checkNotNull(authority, "authority"));
1✔
749
    String host;
750
    int port;
751
    if (uri.getHost() != null) {
1✔
752
      host = uri.getHost();
1✔
753
      port = uri.getPort();
1✔
754
    } else {
755
      /*
756
       * Implementation note: We pick -1 as the port here rather than deriving it from the
757
       * original socket address.  The SSL engine doesn't use this port number when contacting the
758
       * remote server, but rather it is used for other things like SSL Session caching.  When an
759
       * invalid authority is provided (like "bad_cert"), picking the original port and passing it
760
       * in would mean that the port might used under the assumption that it was correct.   By
761
       * using -1 here, it forces the SSL implementation to treat it as invalid.
762
       */
763
      host = authority;
1✔
764
      port = -1;
1✔
765
    }
766
    return new HostPort(host, port);
1✔
767
  }
768

769
  /**
770
   * Returns a {@link ProtocolNegotiator} that ensures the pipeline is set up so that TLS will
771
   * be negotiated, the {@code handler} is added and writes to the {@link io.netty.channel.Channel}
772
   * may happen immediately, even before the TLS Handshake is complete.
773
   *
774
   * @param executorPool a dedicated {@link Executor} pool for time-consuming TLS tasks
775
   */
776
  public static ProtocolNegotiator tls(SslContext sslContext,
777
      ObjectPool<? extends Executor> executorPool, Optional<Runnable> handshakeCompleteRunnable,
778
      X509TrustManager x509ExtendedTrustManager, String sni) {
779
    return new ClientTlsProtocolNegotiator(sslContext, executorPool, handshakeCompleteRunnable,
1✔
780
        x509ExtendedTrustManager, sni);
781
  }
782

783
  /**
784
   * Returns a {@link ProtocolNegotiator} that ensures the pipeline is set up so that TLS will
785
   * be negotiated, the {@code handler} is added and writes to the {@link io.netty.channel.Channel}
786
   * may happen immediately, even before the TLS Handshake is complete.
787
   */
788
  public static ProtocolNegotiator tls(SslContext sslContext,
789
      X509TrustManager x509ExtendedTrustManager) {
790
    return tls(sslContext, null, Optional.absent(), x509ExtendedTrustManager, null);
1✔
791
  }
792

793
  public static ProtocolNegotiator.ClientFactory tlsClientFactory(SslContext sslContext,
794
      X509TrustManager x509ExtendedTrustManager) {
795
    return new TlsProtocolNegotiatorClientFactory(sslContext, x509ExtendedTrustManager);
1✔
796
  }
797

798
  @VisibleForTesting
799
  static final class TlsProtocolNegotiatorClientFactory
800
      implements ProtocolNegotiator.ClientFactory {
801
    private final SslContext sslContext;
802
    private final X509TrustManager x509ExtendedTrustManager;
803

804
    public TlsProtocolNegotiatorClientFactory(SslContext sslContext,
805
                                              X509TrustManager x509ExtendedTrustManager) {
1✔
806
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
807
      this.x509ExtendedTrustManager = x509ExtendedTrustManager;
1✔
808
    }
1✔
809

810
    @Override public ProtocolNegotiator newNegotiator() {
811
      return tls(sslContext, x509ExtendedTrustManager);
1✔
812
    }
813

814
    @Override public int getDefaultPort() {
815
      return GrpcUtil.DEFAULT_PORT_SSL;
1✔
816
    }
817
  }
818

819
  /** A tuple of (host, port). */
820
  @VisibleForTesting
821
  static final class HostPort {
822
    final String host;
823
    final int port;
824

825
    public HostPort(String host, int port) {
1✔
826
      this.host = host;
1✔
827
      this.port = port;
1✔
828
    }
1✔
829
  }
830

831
  /**
832
   * Returns a {@link ProtocolNegotiator} used for upgrading to HTTP/2 from HTTP/1.x.
833
   */
834
  public static ProtocolNegotiator plaintextUpgrade() {
835
    return new PlaintextUpgradeProtocolNegotiator();
1✔
836
  }
837

838
  public static ProtocolNegotiator.ClientFactory plaintextUpgradeClientFactory() {
839
    return new PlaintextUpgradeProtocolNegotiatorClientFactory();
×
840
  }
841

842
  private static final class PlaintextUpgradeProtocolNegotiatorClientFactory
843
      implements ProtocolNegotiator.ClientFactory {
844
    @Override public ProtocolNegotiator newNegotiator() {
845
      return plaintextUpgrade();
×
846
    }
847

848
    @Override public int getDefaultPort() {
849
      return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
×
850
    }
851
  }
852

853
  static final class PlaintextUpgradeProtocolNegotiator implements ProtocolNegotiator {
1✔
854

855
    @Override
856
    public AsciiString scheme() {
857
      return Utils.HTTP;
×
858
    }
859

860
    @Override
861
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
862
      ChannelHandler upgradeHandler =
1✔
863
          new Http2UpgradeAndGrpcHandler(grpcHandler.getAuthority(), grpcHandler);
1✔
864
      ChannelHandler plaintextHandler =
1✔
865
          new PlaintextHandler(upgradeHandler, grpcHandler.getNegotiationLogger());
1✔
866
      return new WaitUntilActiveHandler(plaintextHandler, grpcHandler.getNegotiationLogger());
1✔
867
    }
868

869
    @Override
870
    public void close() {}
1✔
871
  }
872

873
  /**
874
   * Acts as a combination of Http2Upgrade and {@link GrpcNegotiationHandler}.  Unfortunately,
875
   * this negotiator doesn't follow the pattern of "just one handler doing negotiation at a time."
876
   * This is due to the tight coupling between the upgrade handler and the HTTP/2 handler.
877
   */
878
  static final class Http2UpgradeAndGrpcHandler extends ChannelInboundHandlerAdapter {
879

880
    private final String authority;
881
    private final GrpcHttp2ConnectionHandler next;
882
    private final ChannelLogger negotiationLogger;
883

884
    private ProtocolNegotiationEvent pne;
885

886
    Http2UpgradeAndGrpcHandler(String authority, GrpcHttp2ConnectionHandler next) {
1✔
887
      this.authority = Preconditions.checkNotNull(authority, "authority");
1✔
888
      this.next = Preconditions.checkNotNull(next, "next");
1✔
889
      this.negotiationLogger = next.getNegotiationLogger();
1✔
890
    }
1✔
891

892
    @Override
893
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
894
      negotiationLogger.log(ChannelLogLevel.INFO, "Http2Upgrade started");
1✔
895
      HttpClientCodec httpClientCodec = new HttpClientCodec();
1✔
896
      ctx.pipeline().addBefore(ctx.name(), null, httpClientCodec);
1✔
897

898
      Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(next);
1✔
899
      HttpClientUpgradeHandler upgrader =
1✔
900
          new HttpClientUpgradeHandler(httpClientCodec, upgradeCodec, /*maxContentLength=*/ 1000);
901
      ctx.pipeline().addBefore(ctx.name(), null, upgrader);
1✔
902

903
      // Trigger the HTTP/1.1 plaintext upgrade protocol by issuing an HTTP request
904
      // which causes the upgrade headers to be added
905
      DefaultHttpRequest upgradeTrigger =
1✔
906
          new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
907
      upgradeTrigger.headers().add(HttpHeaderNames.HOST, authority);
1✔
908
      ctx.writeAndFlush(upgradeTrigger).addListener(
1✔
909
          ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
910
      super.handlerAdded(ctx);
1✔
911
    }
1✔
912

913
    @Override
914
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
915
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
916
        checkState(pne == null, "negotiation already started");
1✔
917
        pne = (ProtocolNegotiationEvent) evt;
1✔
918
      } else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_SUCCESSFUL) {
1✔
919
        checkState(pne != null, "negotiation not yet complete");
1✔
920
        negotiationLogger.log(ChannelLogLevel.INFO, "Http2Upgrade finished");
1✔
921
        ctx.pipeline().remove(ctx.name());
1✔
922
        next.handleProtocolNegotiationCompleted(pne.getAttributes(), pne.getSecurity());
1✔
923
      } else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_REJECTED) {
1✔
924
        ctx.fireExceptionCaught(unavailableException("HTTP/2 upgrade rejected"));
×
925
      } else {
926
        super.userEventTriggered(ctx, evt);
1✔
927
      }
928
    }
1✔
929
  }
930

931
  /**
932
   * Returns a {@link io.netty.channel.ChannelHandler} that ensures that the {@code handler} is
933
   * added to the pipeline writes to the {@link io.netty.channel.Channel} may happen immediately,
934
   * even before it is active.
935
   */
936
  public static ProtocolNegotiator plaintext() {
937
    return new PlaintextProtocolNegotiator();
1✔
938
  }
939

940
  public static ProtocolNegotiator.ClientFactory plaintextClientFactory() {
941
    return new PlaintextProtocolNegotiatorClientFactory();
1✔
942
  }
943

944
  @VisibleForTesting
945
  static final class PlaintextProtocolNegotiatorClientFactory
1✔
946
      implements ProtocolNegotiator.ClientFactory {
947
    @Override public ProtocolNegotiator newNegotiator() {
948
      return plaintext();
1✔
949
    }
950

951
    @Override public int getDefaultPort() {
952
      return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
1✔
953
    }
954
  }
955

956
  private static RuntimeException unavailableException(String msg) {
957
    return Status.UNAVAILABLE.withDescription(msg).asRuntimeException();
1✔
958
  }
959

960
  @VisibleForTesting
961
  static void logSslEngineDetails(Level level, ChannelHandlerContext ctx, String msg,
962
      @Nullable Throwable t) {
963
    if (!log.isLoggable(level)) {
1✔
964
      return;
1✔
965
    }
966

967
    SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
1✔
968
    SSLEngine engine = sslHandler.engine();
1✔
969

970
    StringBuilder builder = new StringBuilder(msg);
1✔
971
    builder.append("\nSSLEngine Details: [\n");
1✔
972
    if (engine instanceof OpenSslEngine) {
1✔
973
      builder.append("    OpenSSL, ");
1✔
974
      builder.append("Version: 0x").append(Integer.toHexString(OpenSsl.version()));
1✔
975
      builder.append(" (").append(OpenSsl.versionString()).append("), ");
1✔
976
      builder.append("ALPN supported: ").append(SslProvider.isAlpnSupported(SslProvider.OPENSSL));
1✔
977
    } else if (JettyTlsUtil.isJettyAlpnConfigured()) {
×
978
      builder.append("    Jetty ALPN");
×
979
    } else if (JettyTlsUtil.isJettyNpnConfigured()) {
×
980
      builder.append("    Jetty NPN");
×
981
    } else if (JettyTlsUtil.isJava9AlpnAvailable()) {
×
982
      builder.append("    JDK9 ALPN");
×
983
    }
984
    builder.append("\n    TLS Protocol: ");
1✔
985
    builder.append(engine.getSession().getProtocol());
1✔
986
    builder.append("\n    Application Protocol: ");
1✔
987
    builder.append(sslHandler.applicationProtocol());
1✔
988
    builder.append("\n    Need Client Auth: " );
1✔
989
    builder.append(engine.getNeedClientAuth());
1✔
990
    builder.append("\n    Want Client Auth: ");
1✔
991
    builder.append(engine.getWantClientAuth());
1✔
992
    builder.append("\n    Supported protocols=");
1✔
993
    builder.append(Arrays.toString(engine.getSupportedProtocols()));
1✔
994
    builder.append("\n    Enabled protocols=");
1✔
995
    builder.append(Arrays.toString(engine.getEnabledProtocols()));
1✔
996
    builder.append("\n    Supported ciphers=");
1✔
997
    builder.append(Arrays.toString(engine.getSupportedCipherSuites()));
1✔
998
    builder.append("\n    Enabled ciphers=");
1✔
999
    builder.append(Arrays.toString(engine.getEnabledCipherSuites()));
1✔
1000
    builder.append("\n]");
1✔
1001

1002
    log.log(level, builder.toString(), t);
1✔
1003
  }
1✔
1004

1005
  /**
1006
   * Adapts a {@link ProtocolNegotiationEvent} to the {@link GrpcHttp2ConnectionHandler}.
1007
   */
1008
  static final class GrpcNegotiationHandler extends ChannelInboundHandlerAdapter {
1009
    private final GrpcHttp2ConnectionHandler next;
1010

1011
    public GrpcNegotiationHandler(GrpcHttp2ConnectionHandler next) {
1✔
1012
      this.next = Preconditions.checkNotNull(next, "next");
1✔
1013
    }
1✔
1014

1015
    @Override
1016
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
1017
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
1018
        ProtocolNegotiationEvent protocolNegotiationEvent = (ProtocolNegotiationEvent) evt;
1✔
1019
        ctx.pipeline().replace(ctx.name(), null, next);
1✔
1020
        next.handleProtocolNegotiationCompleted(
1✔
1021
            protocolNegotiationEvent.getAttributes(), protocolNegotiationEvent.getSecurity());
1✔
1022
      } else {
1✔
1023
        super.userEventTriggered(ctx, evt);
×
1024
      }
1025
    }
1✔
1026
  }
1027

1028
  /*
1029
   * Common {@link ProtocolNegotiator}s used by gRPC.  Protocol negotiation follows a pattern to
1030
   * simplify the pipeline.   The pipeline should look like:
1031
   *
1032
   * 1.  {@link ProtocolNegotiator#newHandler() PN.H}, created.
1033
   * 2.  [Tail], {@link WriteBufferingAndExceptionHandler WBAEH}, [Head]
1034
   * 3.  [Tail], WBAEH, PN.H, [Head]
1035
   *
1036
   * <p>Typically, PN.H with be an instance of {@link InitHandler IH}, which is a trivial handler
1037
   * that can return the {@code scheme()} of the negotiation.  IH, and each handler after,
1038
   * replaces itself with a "next" handler once its part of negotiation is complete.  This keeps
1039
   * the pipeline small, and limits the interaction between handlers.
1040
   *
1041
   * <p>Additionally, each handler may fire a {@link ProtocolNegotiationEvent PNE} just after
1042
   * replacing itself.  Handlers should capture user events of type PNE, and re-trigger the events
1043
   * once that handler's part of negotiation is complete.  This can be seen in the
1044
   * {@link WaitUntilActiveHandler WUAH}, which waits until the channel is active.  Once active, it
1045
   * replaces itself with the next handler, and fires a PNE containing the addresses.  Continuing
1046
   * with IH and WUAH:
1047
   *
1048
   * 3.  [Tail], WBAEH, IH, [Head]
1049
   * 4.  [Tail], WBAEH, WUAH, [Head]
1050
   * 5.  [Tail], WBAEH, {@link GrpcNegotiationHandler}, [Head]
1051
   * 6a. [Tail], WBAEH, {@link GrpcHttp2ConnectionHandler GHCH}, [Head]
1052
   * 6b. [Tail], GHCH, [Head]
1053
   */
1054

1055
  /**
1056
   * A negotiator that only does plain text.
1057
   */
1058
  static final class PlaintextProtocolNegotiator implements ProtocolNegotiator {
1✔
1059

1060
    @Override
1061
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
1062
      ChannelHandler grpcNegotiationHandler = new GrpcNegotiationHandler(grpcHandler);
1✔
1063
      ChannelHandler plaintextHandler =
1✔
1064
          new PlaintextHandler(grpcNegotiationHandler, grpcHandler.getNegotiationLogger());
1✔
1065
      ChannelHandler activeHandler = new WaitUntilActiveHandler(plaintextHandler,
1✔
1066
          grpcHandler.getNegotiationLogger());
1✔
1067
      return activeHandler;
1✔
1068
    }
1069

1070
    @Override
1071
    public void close() {}
1✔
1072

1073
    @Override
1074
    public AsciiString scheme() {
1075
      return Utils.HTTP;
1✔
1076
    }
1077
  }
1078

1079
  static final class PlaintextHandler extends ProtocolNegotiationHandler {
1080
    PlaintextHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1081
      super(next, negotiationLogger);
1✔
1082
    }
1✔
1083

1084
    @Override
1085
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1086
      ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1✔
1087
      Attributes attrs = existingPne.getAttributes().toBuilder()
1✔
1088
              .set(GrpcAttributes.ATTR_AUTHORITY_VERIFIER, (authority) -> Status.OK)
1✔
1089
              .build();
1✔
1090
      replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs));
1✔
1091
      fireProtocolNegotiationEvent(ctx);
1✔
1092
    }
1✔
1093
  }
1094

1095
  /**
1096
   * Waits for the channel to be active, and then installs the next Handler.  Using this allows
1097
   * subsequent handlers to assume the channel is active and ready to send.  Additionally, this a
1098
   * {@link ProtocolNegotiationEvent}, with the connection addresses.
1099
   */
1100
  static final class WaitUntilActiveHandler extends ProtocolNegotiationHandler {
1101

1102
    boolean protocolNegotiationEventReceived;
1103

1104
    WaitUntilActiveHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1105
      super(next, negotiationLogger);
1✔
1106
    }
1✔
1107

1108
    @Override
1109
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
1110
      if (protocolNegotiationEventReceived) {
1✔
1111
        replaceOnActive(ctx);
1✔
1112
        fireProtocolNegotiationEvent(ctx);
1✔
1113
      }
1114
      // Still propagate channelActive to the new handler.
1115
      super.channelActive(ctx);
1✔
1116
    }
1✔
1117

1118
    @Override
1119
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1120
      protocolNegotiationEventReceived = true;
1✔
1121
      if (ctx.channel().isActive()) {
1✔
1122
        replaceOnActive(ctx);
1✔
1123
        fireProtocolNegotiationEvent(ctx);
1✔
1124
      }
1125
    }
1✔
1126

1127
    private void replaceOnActive(ChannelHandlerContext ctx) {
1128
      ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1✔
1129
      Attributes attrs = existingPne.getAttributes().toBuilder()
1✔
1130
          .set(Grpc.TRANSPORT_ATTR_LOCAL_ADDR, ctx.channel().localAddress())
1✔
1131
          .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
1✔
1132
          // Later handlers are expected to overwrite this.
1133
          .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.NONE)
1✔
1134
          .build();
1✔
1135
      replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs));
1✔
1136
    }
1✔
1137
  }
1138

1139
  /**
1140
   * ProtocolNegotiationHandler is a convenience handler that makes it easy to follow the rules for
1141
   * protocol negotiation.  Handlers should strongly consider extending this handler.
1142
   */
1143
  static class ProtocolNegotiationHandler extends ChannelDuplexHandler {
1144

1145
    private final ChannelHandler next;
1146
    private final String negotiatorName;
1147
    private ProtocolNegotiationEvent pne;
1148
    private final ChannelLogger negotiationLogger;
1149

1150
    protected ProtocolNegotiationHandler(ChannelHandler next, String negotiatorName,
1151
        ChannelLogger negotiationLogger) {
×
1152
      this.next = Preconditions.checkNotNull(next, "next");
×
1153
      this.negotiatorName = negotiatorName;
×
1154
      this.negotiationLogger = Preconditions.checkNotNull(negotiationLogger, "negotiationLogger");
×
1155
    }
×
1156

1157
    protected ProtocolNegotiationHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1✔
1158
      this.next = Preconditions.checkNotNull(next, "next");
1✔
1159
      this.negotiatorName = getClass().getSimpleName().replace("Handler", "");
1✔
1160
      this.negotiationLogger = Preconditions.checkNotNull(negotiationLogger, "negotiationLogger");
1✔
1161
    }
1✔
1162

1163
    @Override
1164
    public final void handlerAdded(ChannelHandlerContext ctx) throws Exception {
1165
      negotiationLogger.log(ChannelLogLevel.DEBUG, "{0} started", negotiatorName);
1✔
1166
      handlerAdded0(ctx);
1✔
1167
    }
1✔
1168

1169
    @ForOverride
1170
    protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception {
1171
      super.handlerAdded(ctx);
1✔
1172
    }
1✔
1173

1174
    @Override
1175
    public final void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
1176
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
1177
        checkState(pne == null, "pre-existing negotiation: %s < %s", pne, evt);
1✔
1178
        pne = (ProtocolNegotiationEvent) evt;
1✔
1179
        protocolNegotiationEventTriggered(ctx);
1✔
1180
      } else {
1181
        userEventTriggered0(ctx, evt);
1✔
1182
      }
1183
    }
1✔
1184

1185
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
1186
      super.userEventTriggered(ctx, evt);
1✔
1187
    }
1✔
1188

1189
    @ForOverride
1190
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1191
      // no-op
1192
    }
1✔
1193

1194
    protected final ProtocolNegotiationEvent getProtocolNegotiationEvent() {
1195
      checkState(pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1196
      return pne;
1✔
1197
    }
1198

1199
    protected final void replaceProtocolNegotiationEvent(ProtocolNegotiationEvent pne) {
1200
      checkState(this.pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1201
      this.pne = Preconditions.checkNotNull(pne);
1✔
1202
    }
1✔
1203

1204
    protected final void fireProtocolNegotiationEvent(ChannelHandlerContext ctx) {
1205
      checkState(pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1206
      negotiationLogger.log(ChannelLogLevel.INFO, "{0} completed", negotiatorName);
1✔
1207
      ctx.pipeline().replace(ctx.name(), /* newName= */ null, next);
1✔
1208
      ctx.fireUserEventTriggered(pne);
1✔
1209
    }
1✔
1210
  }
1211

1212
  static final class SslEngineWrapper extends NoopSslEngine {
1213
    private final SSLEngine sslEngine;
1214
    private final String peerHost;
1215

1216
    SslEngineWrapper(SSLEngine sslEngine, String peerHost) {
1✔
1217
      this.sslEngine = sslEngine;
1✔
1218
      this.peerHost = peerHost;
1✔
1219
    }
1✔
1220

1221
    @Override
1222
    public String getPeerHost() {
1223
      return peerHost;
×
1224
    }
1225

1226
    @Override
1227
    public SSLSession getHandshakeSession() {
1228
      return new FakeSslSession(peerHost);
1✔
1229
    }
1230

1231
    @Override
1232
    public SSLParameters getSSLParameters() {
1233
      return sslEngine.getSSLParameters();
1✔
1234
    }
1235
  }
1236

1237
  static final class FakeSslSession extends NoopSslSession {
1238
    private final String peerHost;
1239

1240
    FakeSslSession(String peerHost) {
1✔
1241
      this.peerHost = peerHost;
1✔
1242
    }
1✔
1243

1244
    @Override
1245
    public String getPeerHost() {
1246
      return peerHost;
1✔
1247
    }
1248
  }
1249
}
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