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

grpc / grpc-java / #20002

29 Sep 2025 04:21PM UTC coverage: 88.592% (+0.02%) from 88.575%
#20002

push

github

web-flow
xds: xDS based SNI setting and SAN validation (#12378)

When using xDS credentials make SNI for the Tls handshake to be
configured via xDS, rather than use the channel authority as the SNI,
and make SAN validation to be able to use the SNI sent when so
instructed via xDS.

Implements A101.

34877 of 39368 relevant lines covered (88.59%)

0.89 hits per line

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

91.25
/../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.DefaultHttpRequest;
55
import io.netty.handler.codec.http.HttpClientCodec;
56
import io.netty.handler.codec.http.HttpClientUpgradeHandler;
57
import io.netty.handler.codec.http.HttpHeaderNames;
58
import io.netty.handler.codec.http.HttpMethod;
59
import io.netty.handler.codec.http.HttpVersion;
60
import io.netty.handler.codec.http2.Http2ClientUpgradeCodec;
61
import io.netty.handler.proxy.HttpProxyHandler;
62
import io.netty.handler.proxy.ProxyConnectionEvent;
63
import io.netty.handler.ssl.OpenSsl;
64
import io.netty.handler.ssl.OpenSslEngine;
65
import io.netty.handler.ssl.SslContext;
66
import io.netty.handler.ssl.SslContextBuilder;
67
import io.netty.handler.ssl.SslHandler;
68
import io.netty.handler.ssl.SslHandshakeCompletionEvent;
69
import io.netty.handler.ssl.SslProvider;
70
import io.netty.util.AsciiString;
71
import java.io.ByteArrayInputStream;
72
import java.net.SocketAddress;
73
import java.net.URI;
74
import java.nio.channels.ClosedChannelException;
75
import java.security.GeneralSecurityException;
76
import java.security.KeyStore;
77
import java.util.Arrays;
78
import java.util.EnumSet;
79
import java.util.List;
80
import java.util.Set;
81
import java.util.concurrent.Executor;
82
import java.util.logging.Level;
83
import java.util.logging.Logger;
84
import javax.annotation.Nullable;
85
import javax.net.ssl.SSLEngine;
86
import javax.net.ssl.SSLException;
87
import javax.net.ssl.SSLParameters;
88
import javax.net.ssl.SSLSession;
89
import javax.net.ssl.TrustManager;
90
import javax.net.ssl.TrustManagerFactory;
91
import javax.net.ssl.X509TrustManager;
92
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
93

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

106
  private ProtocolNegotiators() {
107
  }
108

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

151
    } else if (creds instanceof InsecureChannelCredentials) {
1✔
152
      return FromChannelCredentialsResult.negotiator(plaintextClientFactory());
1✔
153

154
    } else if (creds instanceof CompositeChannelCredentials) {
1✔
155
      CompositeChannelCredentials compCreds = (CompositeChannelCredentials) creds;
1✔
156
      return from(compCreds.getChannelCredentials())
1✔
157
          .withCallCredentials(compCreds.getCallCredentials());
1✔
158

159
    } else if (creds instanceof NettyChannelCredentials) {
1✔
160
      NettyChannelCredentials nettyCreds = (NettyChannelCredentials) creds;
1✔
161
      return FromChannelCredentialsResult.negotiator(nettyCreds.getNegotiator());
1✔
162

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

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

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

213
        case REQUIRE:
214
          builder.clientAuth(io.netty.handler.ssl.ClientAuth.REQUIRE);
1✔
215
          break;
1✔
216

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

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

234
    } else if (creds instanceof InsecureServerCredentials) {
1✔
235
      return FromServerCredentialsResult.negotiator(serverPlaintextFactory());
1✔
236

237
    } else if (creds instanceof NettyServerCredentials) {
1✔
238
      NettyServerCredentials nettyCreds = (NettyServerCredentials) creds;
1✔
239
      return FromServerCredentialsResult.negotiator(nettyCreds.getNegotiator());
1✔
240

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

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

260
  public static final class FromChannelCredentialsResult {
261
    public final ProtocolNegotiator.ClientFactory negotiator;
262
    public final CallCredentials callCredentials;
263
    public final String error;
264

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

272
    public static FromChannelCredentialsResult error(String error) {
273
      return new FromChannelCredentialsResult(
1✔
274
          null, null, Preconditions.checkNotNull(error, "error"));
1✔
275
    }
276

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

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

295
  public static final class FromServerCredentialsResult {
296
    public final ProtocolNegotiator.ServerFactory negotiator;
297
    public final String error;
298

299
    private FromServerCredentialsResult(ProtocolNegotiator.ServerFactory negotiator, String error) {
1✔
300
      this.negotiator = negotiator;
1✔
301
      this.error = error;
1✔
302
    }
1✔
303

304
    public static FromServerCredentialsResult error(String error) {
305
      return new FromServerCredentialsResult(null, Preconditions.checkNotNull(error, "error"));
1✔
306
    }
307

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

313
  public static ProtocolNegotiator.ServerFactory fixedServerFactory(
314
      ProtocolNegotiator negotiator) {
315
    return new FixedProtocolNegotiatorServerFactory(negotiator);
×
316
  }
317

318
  private static final class FixedProtocolNegotiatorServerFactory
319
      implements ProtocolNegotiator.ServerFactory {
320
    private final ProtocolNegotiator protocolNegotiator;
321

322
    public FixedProtocolNegotiatorServerFactory(ProtocolNegotiator protocolNegotiator) {
×
323
      this.protocolNegotiator =
×
324
          Preconditions.checkNotNull(protocolNegotiator, "protocolNegotiator");
×
325
    }
×
326

327
    @Override
328
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
329
      return protocolNegotiator;
×
330
    }
331
  }
332

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

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

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

356
  public static ProtocolNegotiator.ServerFactory serverTlsFactory(SslContext sslContext) {
357
    return new TlsProtocolNegotiatorServerFactory(sslContext);
1✔
358
  }
359

360
  @VisibleForTesting
361
  static final class TlsProtocolNegotiatorServerFactory
362
      implements ProtocolNegotiator.ServerFactory {
363
    private final SslContext sslContext;
364

365
    public TlsProtocolNegotiatorServerFactory(SslContext sslContext) {
1✔
366
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
367
    }
1✔
368

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

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

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

407
      @Override
408
      public AsciiString scheme() {
409
        return Utils.HTTPS;
×
410
      }
411
    };
412
  }
413

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

421
  static final class ServerTlsHandler extends ChannelInboundHandlerAdapter {
422
    private Executor executor;
423
    private final ChannelHandler next;
424
    private final SslContext sslContext;
425

426
    private ProtocolNegotiationEvent pne = ProtocolNegotiationEvent.DEFAULT;
1✔
427

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

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

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

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

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

502
      @Override
503
      public AsciiString scheme() {
504
        return scheme;
×
505
      }
506

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

515
    return new ProxyNegotiator();
1✔
516
  }
517

518
  /**
519
   * A Proxy handler follows {@link ProtocolNegotiationHandler} pattern. Upon successful proxy
520
   * connection, this handler will install {@code next} handler which should be a handler from
521
   * other type of {@link ProtocolNegotiator} to continue negotiating protocol using proxy.
522
   */
523
  static final class ProxyProtocolNegotiationHandler extends ProtocolNegotiationHandler {
524

525
    private final SocketAddress address;
526
    @Nullable private final String userName;
527
    @Nullable private final String password;
528

529
    public ProxyProtocolNegotiationHandler(
530
        SocketAddress address,
531
        @Nullable String userName,
532
        @Nullable String password,
533
        ChannelHandler next,
534
        ChannelLogger negotiationLogger) {
535
      super(next, negotiationLogger);
1✔
536
      this.address = Preconditions.checkNotNull(address, "address");
1✔
537
      this.userName = userName;
1✔
538
      this.password = password;
1✔
539
    }
1✔
540

541
    @Override
542
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
543
      HttpProxyHandler nettyProxyHandler;
544
      if (userName == null || password == null) {
1✔
545
        nettyProxyHandler = new HttpProxyHandler(address);
1✔
546
      } else {
547
        nettyProxyHandler = new HttpProxyHandler(address, userName, password);
×
548
      }
549
      ctx.pipeline().addBefore(ctx.name(), /* name= */ null, nettyProxyHandler);
1✔
550
    }
1✔
551

552
    @Override
553
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
554
      if (evt instanceof ProxyConnectionEvent) {
1✔
555
        fireProtocolNegotiationEvent(ctx);
1✔
556
      } else {
557
        super.userEventTriggered(ctx, evt);
×
558
      }
559
    }
1✔
560
  }
561

562
  static final class ClientTlsProtocolNegotiator implements ProtocolNegotiator {
563

564
    public ClientTlsProtocolNegotiator(SslContext sslContext,
565
        ObjectPool<? extends Executor> executorPool, Optional<Runnable> handshakeCompleteRunnable,
566
        X509TrustManager x509ExtendedTrustManager, String sni) {
1✔
567
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
568
      this.executorPool = executorPool;
1✔
569
      if (this.executorPool != null) {
1✔
570
        this.executor = this.executorPool.getObject();
1✔
571
      }
572
      this.handshakeCompleteRunnable = handshakeCompleteRunnable;
1✔
573
      this.x509ExtendedTrustManager = x509ExtendedTrustManager;
1✔
574
      this.sni = sni;
1✔
575
    }
1✔
576

577
    private final SslContext sslContext;
578
    private final ObjectPool<? extends Executor> executorPool;
579
    private final Optional<Runnable> handshakeCompleteRunnable;
580
    private final X509TrustManager x509ExtendedTrustManager;
581
    private final String sni;
582
    private Executor executor;
583

584
    @Override
585
    public AsciiString scheme() {
586
      return Utils.HTTPS;
1✔
587
    }
588

589
    @Override
590
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
591
      ChannelHandler gnh = new GrpcNegotiationHandler(grpcHandler);
1✔
592
      ChannelLogger negotiationLogger = grpcHandler.getNegotiationLogger();
1✔
593
      String authority;
594
      if ("".equals(sni)) {
1✔
595
        authority = null;
1✔
596
      } else if (sni != null) {
1✔
597
        authority = sni;
1✔
598
      } else {
599
        authority = grpcHandler.getAuthority();
1✔
600
      }
601
      ChannelHandler cth = new ClientTlsHandler(gnh, sslContext,
1✔
602
          authority, this.executor, negotiationLogger, handshakeCompleteRunnable, this,
603
          x509ExtendedTrustManager);
604
      return new WaitUntilActiveHandler(cth, negotiationLogger);
1✔
605
    }
606

607
    @Override
608
    public void close() {
609
      if (this.executorPool != null && this.executor != null) {
1✔
610
        this.executorPool.returnObject(this.executor);
1✔
611
      }
612
    }
1✔
613

614
    @VisibleForTesting
615
    boolean hasX509ExtendedTrustManager() {
616
      return x509ExtendedTrustManager != null;
1✔
617
    }
618
  }
619

620
  static final class ClientTlsHandler extends ProtocolNegotiationHandler {
621

622
    private final SslContext sslContext;
623
    private final String host;
624
    private final int port;
625
    private Executor executor;
626
    private final Optional<Runnable> handshakeCompleteRunnable;
627
    private final X509TrustManager x509TrustManager;
628
    private SSLEngine sslEngine;
629

630
    ClientTlsHandler(ChannelHandler next, SslContext sslContext, String authority,
631
        Executor executor, ChannelLogger negotiationLogger,
632
        Optional<Runnable> handshakeCompleteRunnable,
633
        ClientTlsProtocolNegotiator clientTlsProtocolNegotiator,
634
        X509TrustManager x509TrustManager) {
635
      super(next, negotiationLogger);
1✔
636
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
637
      // TODO: For empty authority and fallback flag
638
      // GRPC_USE_CHANNEL_AUTHORITY_IF_NO_SNI_APPLICABLE present, we should parse authority
639
      // but prevent it from being used for SAN validation in the TrustManager.
640
      if (authority != null) {
1✔
641
        HostPort hostPort = parseAuthority(authority);
1✔
642
        this.host = hostPort.host;
1✔
643
        this.port = hostPort.port;
1✔
644
      } else {
1✔
645
        this.host = null;
1✔
646
        this.port = 0;
1✔
647
      }
648
      this.executor = executor;
1✔
649
      this.handshakeCompleteRunnable = handshakeCompleteRunnable;
1✔
650
      this.x509TrustManager = x509TrustManager;
1✔
651
    }
1✔
652

653
    @Override
654
    @IgnoreJRERequirement
655
    protected void handlerAdded0(ChannelHandlerContext ctx) {
656
      if (host != null) {
1✔
657
        sslEngine = sslContext.newEngine(ctx.alloc(), host, port);
1✔
658
      } else {
659
        sslEngine = sslContext.newEngine(ctx.alloc());
1✔
660
      }
661
      SSLParameters sslParams = sslEngine.getSSLParameters();
1✔
662
      sslParams.setEndpointIdentificationAlgorithm("HTTPS");
1✔
663
      sslEngine.setSSLParameters(sslParams);
1✔
664
      ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
1✔
665
          ? new SslHandler(sslEngine, false, this.executor)
1✔
666
          : new SslHandler(sslEngine, false));
1✔
667
    }
1✔
668

669
    @Override
670
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
671
      if (evt instanceof SslHandshakeCompletionEvent) {
1✔
672
        SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
1✔
673
        if (handshakeEvent.isSuccess()) {
1✔
674
          SslHandler handler = ctx.pipeline().get(SslHandler.class);
1✔
675
          if (sslContext.applicationProtocolNegotiator().protocols()
1✔
676
              .contains(handler.applicationProtocol())) {
1✔
677
            // Successfully negotiated the protocol.
678
            logSslEngineDetails(Level.FINER, ctx, "TLS negotiation succeeded.", null);
1✔
679
            propagateTlsComplete(ctx, handler.engine().getSession());
1✔
680
          } else {
681
            Exception ex =
1✔
682
                unavailableException("Failed ALPN negotiation: Unable to find compatible protocol");
1✔
683
            logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed.", ex);
1✔
684
            if (handshakeCompleteRunnable.isPresent()) {
1✔
685
              handshakeCompleteRunnable.get().run();
×
686
            }
687
            ctx.fireExceptionCaught(ex);
1✔
688
          }
689
        } else {
1✔
690
          Throwable t = handshakeEvent.cause();
1✔
691
          if (t instanceof ClosedChannelException) {
1✔
692
            // On channelInactive(), SslHandler creates its own ClosedChannelException and
693
            // propagates it before the actual channelInactive(). So we assume here that any
694
            // such exception is from channelInactive() and emulate the normal behavior of
695
            // WriteBufferingAndExceptionHandler
696
            t = Status.UNAVAILABLE
1✔
697
                .withDescription("Connection closed while performing TLS negotiation")
1✔
698
                .withCause(t)
1✔
699
                .asRuntimeException();
1✔
700
          }
701
          if (handshakeCompleteRunnable.isPresent()) {
1✔
702
            handshakeCompleteRunnable.get().run();
×
703
          }
704
          ctx.fireExceptionCaught(t);
1✔
705
        }
706
      } else {
1✔
707
        super.userEventTriggered0(ctx, evt);
1✔
708
      }
709
    }
1✔
710

711
    private void propagateTlsComplete(ChannelHandlerContext ctx, SSLSession session) {
712
      Security security = new Security(new Tls(session));
1✔
713
      ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1✔
714
      Attributes attrs = existingPne.getAttributes().toBuilder()
1✔
715
          .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY)
1✔
716
          .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
1✔
717
          .set(GrpcAttributes.ATTR_AUTHORITY_VERIFIER, new X509AuthorityVerifier(
1✔
718
              sslEngine, x509TrustManager))
719
          .build();
1✔
720
      replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs).withSecurity(security));
1✔
721
      if (handshakeCompleteRunnable.isPresent()) {
1✔
722
        handshakeCompleteRunnable.get().run();
×
723
      }
724
      fireProtocolNegotiationEvent(ctx);
1✔
725
    }
1✔
726
  }
727

728
  @VisibleForTesting
729
  static HostPort parseAuthority(String authority) {
730
    URI uri = GrpcUtil.authorityToUri(Preconditions.checkNotNull(authority, "authority"));
1✔
731
    String host;
732
    int port;
733
    if (uri.getHost() != null) {
1✔
734
      host = uri.getHost();
1✔
735
      port = uri.getPort();
1✔
736
    } else {
737
      /*
738
       * Implementation note: We pick -1 as the port here rather than deriving it from the
739
       * original socket address.  The SSL engine doesn't use this port number when contacting the
740
       * remote server, but rather it is used for other things like SSL Session caching.  When an
741
       * invalid authority is provided (like "bad_cert"), picking the original port and passing it
742
       * in would mean that the port might used under the assumption that it was correct.   By
743
       * using -1 here, it forces the SSL implementation to treat it as invalid.
744
       */
745
      host = authority;
1✔
746
      port = -1;
1✔
747
    }
748
    return new HostPort(host, port);
1✔
749
  }
750

751
  /**
752
   * Returns a {@link ProtocolNegotiator} that ensures the pipeline is set up so that TLS will
753
   * be negotiated, the {@code handler} is added and writes to the {@link io.netty.channel.Channel}
754
   * may happen immediately, even before the TLS Handshake is complete.
755
   *
756
   * @param executorPool a dedicated {@link Executor} pool for time-consuming TLS tasks
757
   */
758
  public static ProtocolNegotiator tls(SslContext sslContext,
759
      ObjectPool<? extends Executor> executorPool, Optional<Runnable> handshakeCompleteRunnable,
760
      X509TrustManager x509ExtendedTrustManager, String sni) {
761
    return new ClientTlsProtocolNegotiator(sslContext, executorPool, handshakeCompleteRunnable,
1✔
762
        x509ExtendedTrustManager, sni);
763
  }
764

765
  /**
766
   * Returns a {@link ProtocolNegotiator} that ensures the pipeline is set up so that TLS will
767
   * be negotiated, the {@code handler} is added and writes to the {@link io.netty.channel.Channel}
768
   * may happen immediately, even before the TLS Handshake is complete.
769
   */
770
  public static ProtocolNegotiator tls(SslContext sslContext,
771
      X509TrustManager x509ExtendedTrustManager) {
772
    return tls(sslContext, null, Optional.absent(), x509ExtendedTrustManager, null);
1✔
773
  }
774

775
  public static ProtocolNegotiator.ClientFactory tlsClientFactory(SslContext sslContext,
776
      X509TrustManager x509ExtendedTrustManager) {
777
    return new TlsProtocolNegotiatorClientFactory(sslContext, x509ExtendedTrustManager);
1✔
778
  }
779

780
  @VisibleForTesting
781
  static final class TlsProtocolNegotiatorClientFactory
782
      implements ProtocolNegotiator.ClientFactory {
783
    private final SslContext sslContext;
784
    private final X509TrustManager x509ExtendedTrustManager;
785

786
    public TlsProtocolNegotiatorClientFactory(SslContext sslContext,
787
                                              X509TrustManager x509ExtendedTrustManager) {
1✔
788
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
789
      this.x509ExtendedTrustManager = x509ExtendedTrustManager;
1✔
790
    }
1✔
791

792
    @Override public ProtocolNegotiator newNegotiator() {
793
      return tls(sslContext, x509ExtendedTrustManager);
1✔
794
    }
795

796
    @Override public int getDefaultPort() {
797
      return GrpcUtil.DEFAULT_PORT_SSL;
1✔
798
    }
799
  }
800

801
  /** A tuple of (host, port). */
802
  @VisibleForTesting
803
  static final class HostPort {
804
    final String host;
805
    final int port;
806

807
    public HostPort(String host, int port) {
1✔
808
      this.host = host;
1✔
809
      this.port = port;
1✔
810
    }
1✔
811
  }
812

813
  /**
814
   * Returns a {@link ProtocolNegotiator} used for upgrading to HTTP/2 from HTTP/1.x.
815
   */
816
  public static ProtocolNegotiator plaintextUpgrade() {
817
    return new PlaintextUpgradeProtocolNegotiator();
1✔
818
  }
819

820
  public static ProtocolNegotiator.ClientFactory plaintextUpgradeClientFactory() {
821
    return new PlaintextUpgradeProtocolNegotiatorClientFactory();
×
822
  }
823

824
  private static final class PlaintextUpgradeProtocolNegotiatorClientFactory
825
      implements ProtocolNegotiator.ClientFactory {
826
    @Override public ProtocolNegotiator newNegotiator() {
827
      return plaintextUpgrade();
×
828
    }
829

830
    @Override public int getDefaultPort() {
831
      return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
×
832
    }
833
  }
834

835
  static final class PlaintextUpgradeProtocolNegotiator implements ProtocolNegotiator {
1✔
836

837
    @Override
838
    public AsciiString scheme() {
839
      return Utils.HTTP;
×
840
    }
841

842
    @Override
843
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
844
      ChannelHandler upgradeHandler =
1✔
845
          new Http2UpgradeAndGrpcHandler(grpcHandler.getAuthority(), grpcHandler);
1✔
846
      ChannelHandler plaintextHandler =
1✔
847
          new PlaintextHandler(upgradeHandler, grpcHandler.getNegotiationLogger());
1✔
848
      return new WaitUntilActiveHandler(plaintextHandler, grpcHandler.getNegotiationLogger());
1✔
849
    }
850

851
    @Override
852
    public void close() {}
1✔
853
  }
854

855
  /**
856
   * Acts as a combination of Http2Upgrade and {@link GrpcNegotiationHandler}.  Unfortunately,
857
   * this negotiator doesn't follow the pattern of "just one handler doing negotiation at a time."
858
   * This is due to the tight coupling between the upgrade handler and the HTTP/2 handler.
859
   */
860
  static final class Http2UpgradeAndGrpcHandler extends ChannelInboundHandlerAdapter {
861

862
    private final String authority;
863
    private final GrpcHttp2ConnectionHandler next;
864
    private final ChannelLogger negotiationLogger;
865

866
    private ProtocolNegotiationEvent pne;
867

868
    Http2UpgradeAndGrpcHandler(String authority, GrpcHttp2ConnectionHandler next) {
1✔
869
      this.authority = Preconditions.checkNotNull(authority, "authority");
1✔
870
      this.next = Preconditions.checkNotNull(next, "next");
1✔
871
      this.negotiationLogger = next.getNegotiationLogger();
1✔
872
    }
1✔
873

874
    @Override
875
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
876
      negotiationLogger.log(ChannelLogLevel.INFO, "Http2Upgrade started");
1✔
877
      HttpClientCodec httpClientCodec = new HttpClientCodec();
1✔
878
      ctx.pipeline().addBefore(ctx.name(), null, httpClientCodec);
1✔
879

880
      Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(next);
1✔
881
      HttpClientUpgradeHandler upgrader =
1✔
882
          new HttpClientUpgradeHandler(httpClientCodec, upgradeCodec, /*maxContentLength=*/ 1000);
883
      ctx.pipeline().addBefore(ctx.name(), null, upgrader);
1✔
884

885
      // Trigger the HTTP/1.1 plaintext upgrade protocol by issuing an HTTP request
886
      // which causes the upgrade headers to be added
887
      DefaultHttpRequest upgradeTrigger =
1✔
888
          new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
889
      upgradeTrigger.headers().add(HttpHeaderNames.HOST, authority);
1✔
890
      ctx.writeAndFlush(upgradeTrigger).addListener(
1✔
891
          ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
892
      super.handlerAdded(ctx);
1✔
893
    }
1✔
894

895
    @Override
896
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
897
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
898
        checkState(pne == null, "negotiation already started");
1✔
899
        pne = (ProtocolNegotiationEvent) evt;
1✔
900
      } else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_SUCCESSFUL) {
1✔
901
        checkState(pne != null, "negotiation not yet complete");
1✔
902
        negotiationLogger.log(ChannelLogLevel.INFO, "Http2Upgrade finished");
1✔
903
        ctx.pipeline().remove(ctx.name());
1✔
904
        next.handleProtocolNegotiationCompleted(pne.getAttributes(), pne.getSecurity());
1✔
905
      } else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_REJECTED) {
1✔
906
        ctx.fireExceptionCaught(unavailableException("HTTP/2 upgrade rejected"));
×
907
      } else {
908
        super.userEventTriggered(ctx, evt);
1✔
909
      }
910
    }
1✔
911
  }
912

913
  /**
914
   * Returns a {@link io.netty.channel.ChannelHandler} that ensures that the {@code handler} is
915
   * added to the pipeline writes to the {@link io.netty.channel.Channel} may happen immediately,
916
   * even before it is active.
917
   */
918
  public static ProtocolNegotiator plaintext() {
919
    return new PlaintextProtocolNegotiator();
1✔
920
  }
921

922
  public static ProtocolNegotiator.ClientFactory plaintextClientFactory() {
923
    return new PlaintextProtocolNegotiatorClientFactory();
1✔
924
  }
925

926
  @VisibleForTesting
927
  static final class PlaintextProtocolNegotiatorClientFactory
1✔
928
      implements ProtocolNegotiator.ClientFactory {
929
    @Override public ProtocolNegotiator newNegotiator() {
930
      return plaintext();
1✔
931
    }
932

933
    @Override public int getDefaultPort() {
934
      return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
1✔
935
    }
936
  }
937

938
  private static RuntimeException unavailableException(String msg) {
939
    return Status.UNAVAILABLE.withDescription(msg).asRuntimeException();
1✔
940
  }
941

942
  @VisibleForTesting
943
  static void logSslEngineDetails(Level level, ChannelHandlerContext ctx, String msg,
944
      @Nullable Throwable t) {
945
    if (!log.isLoggable(level)) {
1✔
946
      return;
1✔
947
    }
948

949
    SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
1✔
950
    SSLEngine engine = sslHandler.engine();
1✔
951

952
    StringBuilder builder = new StringBuilder(msg);
1✔
953
    builder.append("\nSSLEngine Details: [\n");
1✔
954
    if (engine instanceof OpenSslEngine) {
1✔
955
      builder.append("    OpenSSL, ");
1✔
956
      builder.append("Version: 0x").append(Integer.toHexString(OpenSsl.version()));
1✔
957
      builder.append(" (").append(OpenSsl.versionString()).append("), ");
1✔
958
      builder.append("ALPN supported: ").append(SslProvider.isAlpnSupported(SslProvider.OPENSSL));
1✔
959
    } else if (JettyTlsUtil.isJettyAlpnConfigured()) {
×
960
      builder.append("    Jetty ALPN");
×
961
    } else if (JettyTlsUtil.isJettyNpnConfigured()) {
×
962
      builder.append("    Jetty NPN");
×
963
    } else if (JettyTlsUtil.isJava9AlpnAvailable()) {
×
964
      builder.append("    JDK9 ALPN");
×
965
    }
966
    builder.append("\n    TLS Protocol: ");
1✔
967
    builder.append(engine.getSession().getProtocol());
1✔
968
    builder.append("\n    Application Protocol: ");
1✔
969
    builder.append(sslHandler.applicationProtocol());
1✔
970
    builder.append("\n    Need Client Auth: " );
1✔
971
    builder.append(engine.getNeedClientAuth());
1✔
972
    builder.append("\n    Want Client Auth: ");
1✔
973
    builder.append(engine.getWantClientAuth());
1✔
974
    builder.append("\n    Supported protocols=");
1✔
975
    builder.append(Arrays.toString(engine.getSupportedProtocols()));
1✔
976
    builder.append("\n    Enabled protocols=");
1✔
977
    builder.append(Arrays.toString(engine.getEnabledProtocols()));
1✔
978
    builder.append("\n    Supported ciphers=");
1✔
979
    builder.append(Arrays.toString(engine.getSupportedCipherSuites()));
1✔
980
    builder.append("\n    Enabled ciphers=");
1✔
981
    builder.append(Arrays.toString(engine.getEnabledCipherSuites()));
1✔
982
    builder.append("\n]");
1✔
983

984
    log.log(level, builder.toString(), t);
1✔
985
  }
1✔
986

987
  /**
988
   * Adapts a {@link ProtocolNegotiationEvent} to the {@link GrpcHttp2ConnectionHandler}.
989
   */
990
  static final class GrpcNegotiationHandler extends ChannelInboundHandlerAdapter {
991
    private final GrpcHttp2ConnectionHandler next;
992

993
    public GrpcNegotiationHandler(GrpcHttp2ConnectionHandler next) {
1✔
994
      this.next = Preconditions.checkNotNull(next, "next");
1✔
995
    }
1✔
996

997
    @Override
998
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
999
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
1000
        ProtocolNegotiationEvent protocolNegotiationEvent = (ProtocolNegotiationEvent) evt;
1✔
1001
        ctx.pipeline().replace(ctx.name(), null, next);
1✔
1002
        next.handleProtocolNegotiationCompleted(
1✔
1003
            protocolNegotiationEvent.getAttributes(), protocolNegotiationEvent.getSecurity());
1✔
1004
      } else {
1✔
1005
        super.userEventTriggered(ctx, evt);
×
1006
      }
1007
    }
1✔
1008
  }
1009

1010
  /*
1011
   * Common {@link ProtocolNegotiator}s used by gRPC.  Protocol negotiation follows a pattern to
1012
   * simplify the pipeline.   The pipeline should look like:
1013
   *
1014
   * 1.  {@link ProtocolNegotiator#newHandler() PN.H}, created.
1015
   * 2.  [Tail], {@link WriteBufferingAndExceptionHandler WBAEH}, [Head]
1016
   * 3.  [Tail], WBAEH, PN.H, [Head]
1017
   *
1018
   * <p>Typically, PN.H with be an instance of {@link InitHandler IH}, which is a trivial handler
1019
   * that can return the {@code scheme()} of the negotiation.  IH, and each handler after,
1020
   * replaces itself with a "next" handler once its part of negotiation is complete.  This keeps
1021
   * the pipeline small, and limits the interaction between handlers.
1022
   *
1023
   * <p>Additionally, each handler may fire a {@link ProtocolNegotiationEvent PNE} just after
1024
   * replacing itself.  Handlers should capture user events of type PNE, and re-trigger the events
1025
   * once that handler's part of negotiation is complete.  This can be seen in the
1026
   * {@link WaitUntilActiveHandler WUAH}, which waits until the channel is active.  Once active, it
1027
   * replaces itself with the next handler, and fires a PNE containing the addresses.  Continuing
1028
   * with IH and WUAH:
1029
   *
1030
   * 3.  [Tail], WBAEH, IH, [Head]
1031
   * 4.  [Tail], WBAEH, WUAH, [Head]
1032
   * 5.  [Tail], WBAEH, {@link GrpcNegotiationHandler}, [Head]
1033
   * 6a. [Tail], WBAEH, {@link GrpcHttp2ConnectionHandler GHCH}, [Head]
1034
   * 6b. [Tail], GHCH, [Head]
1035
   */
1036

1037
  /**
1038
   * A negotiator that only does plain text.
1039
   */
1040
  static final class PlaintextProtocolNegotiator implements ProtocolNegotiator {
1✔
1041

1042
    @Override
1043
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
1044
      ChannelHandler grpcNegotiationHandler = new GrpcNegotiationHandler(grpcHandler);
1✔
1045
      ChannelHandler plaintextHandler =
1✔
1046
          new PlaintextHandler(grpcNegotiationHandler, grpcHandler.getNegotiationLogger());
1✔
1047
      ChannelHandler activeHandler = new WaitUntilActiveHandler(plaintextHandler,
1✔
1048
          grpcHandler.getNegotiationLogger());
1✔
1049
      return activeHandler;
1✔
1050
    }
1051

1052
    @Override
1053
    public void close() {}
1✔
1054

1055
    @Override
1056
    public AsciiString scheme() {
1057
      return Utils.HTTP;
1✔
1058
    }
1059
  }
1060

1061
  static final class PlaintextHandler extends ProtocolNegotiationHandler {
1062
    PlaintextHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1063
      super(next, negotiationLogger);
1✔
1064
    }
1✔
1065

1066
    @Override
1067
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1068
      ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1✔
1069
      Attributes attrs = existingPne.getAttributes().toBuilder()
1✔
1070
              .set(GrpcAttributes.ATTR_AUTHORITY_VERIFIER, (authority) -> Status.OK)
1✔
1071
              .build();
1✔
1072
      replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs));
1✔
1073
      fireProtocolNegotiationEvent(ctx);
1✔
1074
    }
1✔
1075
  }
1076

1077
  /**
1078
   * Waits for the channel to be active, and then installs the next Handler.  Using this allows
1079
   * subsequent handlers to assume the channel is active and ready to send.  Additionally, this a
1080
   * {@link ProtocolNegotiationEvent}, with the connection addresses.
1081
   */
1082
  static final class WaitUntilActiveHandler extends ProtocolNegotiationHandler {
1083

1084
    boolean protocolNegotiationEventReceived;
1085

1086
    WaitUntilActiveHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1087
      super(next, negotiationLogger);
1✔
1088
    }
1✔
1089

1090
    @Override
1091
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
1092
      if (protocolNegotiationEventReceived) {
1✔
1093
        replaceOnActive(ctx);
1✔
1094
        fireProtocolNegotiationEvent(ctx);
1✔
1095
      }
1096
      // Still propagate channelActive to the new handler.
1097
      super.channelActive(ctx);
1✔
1098
    }
1✔
1099

1100
    @Override
1101
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1102
      protocolNegotiationEventReceived = true;
1✔
1103
      if (ctx.channel().isActive()) {
1✔
1104
        replaceOnActive(ctx);
1✔
1105
        fireProtocolNegotiationEvent(ctx);
1✔
1106
      }
1107
    }
1✔
1108

1109
    private void replaceOnActive(ChannelHandlerContext ctx) {
1110
      ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1✔
1111
      Attributes attrs = existingPne.getAttributes().toBuilder()
1✔
1112
          .set(Grpc.TRANSPORT_ATTR_LOCAL_ADDR, ctx.channel().localAddress())
1✔
1113
          .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
1✔
1114
          // Later handlers are expected to overwrite this.
1115
          .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.NONE)
1✔
1116
          .build();
1✔
1117
      replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs));
1✔
1118
    }
1✔
1119
  }
1120

1121
  /**
1122
   * ProtocolNegotiationHandler is a convenience handler that makes it easy to follow the rules for
1123
   * protocol negotiation.  Handlers should strongly consider extending this handler.
1124
   */
1125
  static class ProtocolNegotiationHandler extends ChannelDuplexHandler {
1126

1127
    private final ChannelHandler next;
1128
    private final String negotiatorName;
1129
    private ProtocolNegotiationEvent pne;
1130
    private final ChannelLogger negotiationLogger;
1131

1132
    protected ProtocolNegotiationHandler(ChannelHandler next, String negotiatorName,
1133
        ChannelLogger negotiationLogger) {
×
1134
      this.next = Preconditions.checkNotNull(next, "next");
×
1135
      this.negotiatorName = negotiatorName;
×
1136
      this.negotiationLogger = Preconditions.checkNotNull(negotiationLogger, "negotiationLogger");
×
1137
    }
×
1138

1139
    protected ProtocolNegotiationHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1✔
1140
      this.next = Preconditions.checkNotNull(next, "next");
1✔
1141
      this.negotiatorName = getClass().getSimpleName().replace("Handler", "");
1✔
1142
      this.negotiationLogger = Preconditions.checkNotNull(negotiationLogger, "negotiationLogger");
1✔
1143
    }
1✔
1144

1145
    @Override
1146
    public final void handlerAdded(ChannelHandlerContext ctx) throws Exception {
1147
      negotiationLogger.log(ChannelLogLevel.DEBUG, "{0} started", negotiatorName);
1✔
1148
      handlerAdded0(ctx);
1✔
1149
    }
1✔
1150

1151
    @ForOverride
1152
    protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception {
1153
      super.handlerAdded(ctx);
1✔
1154
    }
1✔
1155

1156
    @Override
1157
    public final void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
1158
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
1159
        checkState(pne == null, "pre-existing negotiation: %s < %s", pne, evt);
1✔
1160
        pne = (ProtocolNegotiationEvent) evt;
1✔
1161
        protocolNegotiationEventTriggered(ctx);
1✔
1162
      } else {
1163
        userEventTriggered0(ctx, evt);
1✔
1164
      }
1165
    }
1✔
1166

1167
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
1168
      super.userEventTriggered(ctx, evt);
1✔
1169
    }
1✔
1170

1171
    @ForOverride
1172
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1173
      // no-op
1174
    }
1✔
1175

1176
    protected final ProtocolNegotiationEvent getProtocolNegotiationEvent() {
1177
      checkState(pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1178
      return pne;
1✔
1179
    }
1180

1181
    protected final void replaceProtocolNegotiationEvent(ProtocolNegotiationEvent pne) {
1182
      checkState(this.pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1183
      this.pne = Preconditions.checkNotNull(pne);
1✔
1184
    }
1✔
1185

1186
    protected final void fireProtocolNegotiationEvent(ChannelHandlerContext ctx) {
1187
      checkState(pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1188
      negotiationLogger.log(ChannelLogLevel.INFO, "{0} completed", negotiatorName);
1✔
1189
      ctx.pipeline().replace(ctx.name(), /* newName= */ null, next);
1✔
1190
      ctx.fireUserEventTriggered(pne);
1✔
1191
    }
1✔
1192
  }
1193

1194
  static final class SslEngineWrapper extends NoopSslEngine {
1195
    private final SSLEngine sslEngine;
1196
    private final String peerHost;
1197

1198
    SslEngineWrapper(SSLEngine sslEngine, String peerHost) {
1✔
1199
      this.sslEngine = sslEngine;
1✔
1200
      this.peerHost = peerHost;
1✔
1201
    }
1✔
1202

1203
    @Override
1204
    public String getPeerHost() {
1205
      return peerHost;
×
1206
    }
1207

1208
    @Override
1209
    public SSLSession getHandshakeSession() {
1210
      return new FakeSslSession(peerHost);
1✔
1211
    }
1212

1213
    @Override
1214
    public SSLParameters getSSLParameters() {
1215
      return sslEngine.getSSLParameters();
1✔
1216
    }
1217
  }
1218

1219
  static final class FakeSslSession extends NoopSslSession {
1220
    private final String peerHost;
1221

1222
    FakeSslSession(String peerHost) {
1✔
1223
      this.peerHost = peerHost;
1✔
1224
    }
1✔
1225

1226
    @Override
1227
    public String getPeerHost() {
1228
      return peerHost;
1✔
1229
    }
1230
  }
1231
}
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