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

grpc / grpc-java / #19711

24 Feb 2025 02:58PM UTC coverage: 88.545% (-0.08%) from 88.622%
#19711

push

github

web-flow
netty: Per-rpc authority verification against peer cert subject names (#11724)

Per-rpc verification of authority specified via call options or set by the LB API against peer cert's subject names.

34437 of 38892 relevant lines covered (88.55%)

0.89 hits per line

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

91.0
/../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
  private static Class<?> x509ExtendedTrustManagerClass;
106

107
  static {
108
    try {
109
      x509ExtendedTrustManagerClass = Class.forName("javax.net.ssl.X509ExtendedTrustManager");
1✔
110
    } catch (ClassNotFoundException e) {
×
111
      // Will disallow per-rpc authority override via call option.
112
    }
1✔
113
  }
1✔
114

115
  private ProtocolNegotiators() {
116
  }
117

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

167
    } else if (creds instanceof InsecureChannelCredentials) {
1✔
168
      return FromChannelCredentialsResult.negotiator(plaintextClientFactory());
1✔
169

170
    } else if (creds instanceof CompositeChannelCredentials) {
1✔
171
      CompositeChannelCredentials compCreds = (CompositeChannelCredentials) creds;
1✔
172
      return from(compCreds.getChannelCredentials())
1✔
173
          .withCallCredentials(compCreds.getCallCredentials());
1✔
174

175
    } else if (creds instanceof NettyChannelCredentials) {
1✔
176
      NettyChannelCredentials nettyCreds = (NettyChannelCredentials) creds;
1✔
177
      return FromChannelCredentialsResult.negotiator(nettyCreds.getNegotiator());
1✔
178

179
    } else if (creds instanceof ChoiceChannelCredentials) {
1✔
180
      ChoiceChannelCredentials choiceCreds = (ChoiceChannelCredentials) creds;
1✔
181
      StringBuilder error = new StringBuilder();
1✔
182
      for (ChannelCredentials innerCreds : choiceCreds.getCredentialsList()) {
1✔
183
        FromChannelCredentialsResult result = from(innerCreds);
1✔
184
        if (result.error == null) {
1✔
185
          return result;
1✔
186
        }
187
        error.append(", ");
1✔
188
        error.append(result.error);
1✔
189
      }
1✔
190
      return FromChannelCredentialsResult.error(error.substring(2));
1✔
191

192
    } else {
193
      return FromChannelCredentialsResult.error(
1✔
194
          "Unsupported credential type: " + creds.getClass().getName());
1✔
195
    }
196
  }
197

198
  public static FromServerCredentialsResult from(ServerCredentials creds) {
199
    if (creds instanceof TlsServerCredentials) {
1✔
200
      TlsServerCredentials tlsCreds = (TlsServerCredentials) creds;
1✔
201
      Set<TlsServerCredentials.Feature> incomprehensible =
1✔
202
          tlsCreds.incomprehensible(understoodServerTlsFeatures);
1✔
203
      if (!incomprehensible.isEmpty()) {
1✔
204
        return FromServerCredentialsResult.error(
1✔
205
            "TLS features not understood: " + incomprehensible);
206
      }
207
      SslContextBuilder builder;
208
      if (tlsCreds.getKeyManagers() != null) {
1✔
209
        builder = GrpcSslContexts.configure(SslContextBuilder.forServer(
1✔
210
            new FixedKeyManagerFactory(tlsCreds.getKeyManagers())));
1✔
211
      } else if (tlsCreds.getPrivateKey() != null) {
1✔
212
        builder = GrpcSslContexts.forServer(
1✔
213
            new ByteArrayInputStream(tlsCreds.getCertificateChain()),
1✔
214
            new ByteArrayInputStream(tlsCreds.getPrivateKey()),
1✔
215
            tlsCreds.getPrivateKeyPassword());
1✔
216
      } else {
217
        throw new AssertionError("BUG! No key");
×
218
      }
219
      if (tlsCreds.getTrustManagers() != null) {
1✔
220
        builder.trustManager(new FixedTrustManagerFactory(tlsCreds.getTrustManagers()));
1✔
221
      } else if (tlsCreds.getRootCertificates() != null) {
1✔
222
        builder.trustManager(new ByteArrayInputStream(tlsCreds.getRootCertificates()));
1✔
223
      } // else use system default
224
      switch (tlsCreds.getClientAuth()) {
1✔
225
        case OPTIONAL:
226
          builder.clientAuth(io.netty.handler.ssl.ClientAuth.OPTIONAL);
1✔
227
          break;
1✔
228

229
        case REQUIRE:
230
          builder.clientAuth(io.netty.handler.ssl.ClientAuth.REQUIRE);
1✔
231
          break;
1✔
232

233
        case NONE:
234
          builder.clientAuth(io.netty.handler.ssl.ClientAuth.NONE);
1✔
235
          break;
1✔
236

237
        default:
238
          return FromServerCredentialsResult.error(
×
239
              "Unknown TlsServerCredentials.ClientAuth value: " + tlsCreds.getClientAuth());
×
240
      }
241
      SslContext sslContext;
242
      try {
243
        sslContext = builder.build();
1✔
244
      } catch (SSLException ex) {
×
245
        throw new IllegalArgumentException(
×
246
            "Unexpected error converting ServerCredentials to Netty SslContext", ex);
247
      }
1✔
248
      return FromServerCredentialsResult.negotiator(serverTlsFactory(sslContext));
1✔
249

250
    } else if (creds instanceof InsecureServerCredentials) {
1✔
251
      return FromServerCredentialsResult.negotiator(serverPlaintextFactory());
1✔
252

253
    } else if (creds instanceof NettyServerCredentials) {
1✔
254
      NettyServerCredentials nettyCreds = (NettyServerCredentials) creds;
1✔
255
      return FromServerCredentialsResult.negotiator(nettyCreds.getNegotiator());
1✔
256

257
    } else if (creds instanceof ChoiceServerCredentials) {
1✔
258
      ChoiceServerCredentials choiceCreds = (ChoiceServerCredentials) creds;
1✔
259
      StringBuilder error = new StringBuilder();
1✔
260
      for (ServerCredentials innerCreds : choiceCreds.getCredentialsList()) {
1✔
261
        FromServerCredentialsResult result = from(innerCreds);
1✔
262
        if (result.error == null) {
1✔
263
          return result;
1✔
264
        }
265
        error.append(", ");
1✔
266
        error.append(result.error);
1✔
267
      }
1✔
268
      return FromServerCredentialsResult.error(error.substring(2));
1✔
269

270
    } else {
271
      return FromServerCredentialsResult.error(
1✔
272
          "Unsupported credential type: " + creds.getClass().getName());
1✔
273
    }
274
  }
275

276
  public static final class FromChannelCredentialsResult {
277
    public final ProtocolNegotiator.ClientFactory negotiator;
278
    public final CallCredentials callCredentials;
279
    public final String error;
280

281
    private FromChannelCredentialsResult(ProtocolNegotiator.ClientFactory negotiator,
282
        CallCredentials creds, String error) {
1✔
283
      this.negotiator = negotiator;
1✔
284
      this.callCredentials = creds;
1✔
285
      this.error = error;
1✔
286
    }
1✔
287

288
    public static FromChannelCredentialsResult error(String error) {
289
      return new FromChannelCredentialsResult(
1✔
290
          null, null, Preconditions.checkNotNull(error, "error"));
1✔
291
    }
292

293
    public static FromChannelCredentialsResult negotiator(
294
        ProtocolNegotiator.ClientFactory factory) {
295
      return new FromChannelCredentialsResult(
1✔
296
          Preconditions.checkNotNull(factory, "factory"), null, null);
1✔
297
    }
298

299
    public FromChannelCredentialsResult withCallCredentials(CallCredentials callCreds) {
300
      Preconditions.checkNotNull(callCreds, "callCreds");
1✔
301
      if (error != null) {
1✔
302
        return this;
×
303
      }
304
      if (this.callCredentials != null) {
1✔
305
        callCreds = new CompositeCallCredentials(this.callCredentials, callCreds);
×
306
      }
307
      return new FromChannelCredentialsResult(negotiator, callCreds, null);
1✔
308
    }
309
  }
310

311
  public static final class FromServerCredentialsResult {
312
    public final ProtocolNegotiator.ServerFactory negotiator;
313
    public final String error;
314

315
    private FromServerCredentialsResult(ProtocolNegotiator.ServerFactory negotiator, String error) {
1✔
316
      this.negotiator = negotiator;
1✔
317
      this.error = error;
1✔
318
    }
1✔
319

320
    public static FromServerCredentialsResult error(String error) {
321
      return new FromServerCredentialsResult(null, Preconditions.checkNotNull(error, "error"));
1✔
322
    }
323

324
    public static FromServerCredentialsResult negotiator(ProtocolNegotiator.ServerFactory factory) {
325
      return new FromServerCredentialsResult(Preconditions.checkNotNull(factory, "factory"), null);
1✔
326
    }
327
  }
328

329
  public static ProtocolNegotiator.ServerFactory fixedServerFactory(
330
      ProtocolNegotiator negotiator) {
331
    return new FixedProtocolNegotiatorServerFactory(negotiator);
×
332
  }
333

334
  private static final class FixedProtocolNegotiatorServerFactory
335
      implements ProtocolNegotiator.ServerFactory {
336
    private final ProtocolNegotiator protocolNegotiator;
337

338
    public FixedProtocolNegotiatorServerFactory(ProtocolNegotiator protocolNegotiator) {
×
339
      this.protocolNegotiator =
×
340
          Preconditions.checkNotNull(protocolNegotiator, "protocolNegotiator");
×
341
    }
×
342

343
    @Override
344
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
345
      return protocolNegotiator;
×
346
    }
347
  }
348

349
  /**
350
   * Create a server plaintext handler for gRPC.
351
   */
352
  public static ProtocolNegotiator serverPlaintext() {
353
    return new PlaintextProtocolNegotiator();
1✔
354
  }
355

356
  /**
357
   * Create a server plaintext handler factory for gRPC.
358
   */
359
  public static ProtocolNegotiator.ServerFactory serverPlaintextFactory() {
360
    return new PlaintextProtocolNegotiatorServerFactory();
1✔
361
  }
362

363
  @VisibleForTesting
364
  static final class PlaintextProtocolNegotiatorServerFactory
1✔
365
      implements ProtocolNegotiator.ServerFactory {
366
    @Override
367
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
368
      return serverPlaintext();
1✔
369
    }
370
  }
371

372
  public static ProtocolNegotiator.ServerFactory serverTlsFactory(SslContext sslContext) {
373
    return new TlsProtocolNegotiatorServerFactory(sslContext);
1✔
374
  }
375

376
  @VisibleForTesting
377
  static final class TlsProtocolNegotiatorServerFactory
378
      implements ProtocolNegotiator.ServerFactory {
379
    private final SslContext sslContext;
380

381
    public TlsProtocolNegotiatorServerFactory(SslContext sslContext) {
1✔
382
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
383
    }
1✔
384

385
    @Override
386
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
387
      return serverTls(sslContext, offloadExecutorPool);
1✔
388
    }
389
  }
390

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

416
      @Override
417
      public void close() {
418
        if (executorPool != null && executor != null) {
1✔
419
          executorPool.returnObject(executor);
1✔
420
        }
421
      }
1✔
422

423
      @Override
424
      public AsciiString scheme() {
425
        return Utils.HTTPS;
×
426
      }
427
    };
428
  }
429

430
  /**
431
   * Create a server TLS handler for HTTP/2 capable of using ALPN/NPN.
432
   */
433
  public static ProtocolNegotiator serverTls(final SslContext sslContext) {
434
    return serverTls(sslContext, null);
1✔
435
  }
436

437
  static final class ServerTlsHandler extends ChannelInboundHandlerAdapter {
438
    private Executor executor;
439
    private final ChannelHandler next;
440
    private final SslContext sslContext;
441

442
    private ProtocolNegotiationEvent pne = ProtocolNegotiationEvent.DEFAULT;
1✔
443

444
    ServerTlsHandler(ChannelHandler next,
445
        SslContext sslContext,
446
        final ObjectPool<? extends Executor> executorPool) {
1✔
447
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
448
      this.next = Preconditions.checkNotNull(next, "next");
1✔
449
      if (executorPool != null) {
1✔
450
        this.executor = executorPool.getObject();
1✔
451
      }
452
    }
1✔
453

454
    @Override
455
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
456
      super.handlerAdded(ctx);
1✔
457
      SSLEngine sslEngine = sslContext.newEngine(ctx.alloc());
1✔
458
      ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
1✔
459
          ? new SslHandler(sslEngine, false, this.executor)
1✔
460
          : new SslHandler(sslEngine, false));
1✔
461
    }
1✔
462

463
    @Override
464
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
465
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
466
        pne = (ProtocolNegotiationEvent) evt;
1✔
467
      } else if (evt instanceof SslHandshakeCompletionEvent) {
1✔
468
        SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
1✔
469
        if (!handshakeEvent.isSuccess()) {
1✔
470
          logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed for new client.", null);
1✔
471
          ctx.fireExceptionCaught(handshakeEvent.cause());
1✔
472
          return;
1✔
473
        }
474
        SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
1✔
475
        if (!sslContext.applicationProtocolNegotiator().protocols().contains(
1✔
476
                sslHandler.applicationProtocol())) {
1✔
477
          logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed for new client.", null);
1✔
478
          ctx.fireExceptionCaught(unavailableException(
1✔
479
              "Failed protocol negotiation: Unable to find compatible protocol"));
480
          return;
1✔
481
        }
482
        ctx.pipeline().replace(ctx.name(), null, next);
1✔
483
        fireProtocolNegotiationEvent(ctx, sslHandler.engine().getSession());
1✔
484
      } else {
1✔
485
        super.userEventTriggered(ctx, evt);
1✔
486
      }
487
    }
1✔
488

489
    private void fireProtocolNegotiationEvent(ChannelHandlerContext ctx, SSLSession session) {
490
      Security security = new Security(new Tls(session));
1✔
491
      Attributes attrs = pne.getAttributes().toBuilder()
1✔
492
          .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY)
1✔
493
          .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
1✔
494
          .build();
1✔
495
      ctx.fireUserEventTriggered(pne.withAttributes(attrs).withSecurity(security));
1✔
496
    }
1✔
497
  }
498

499
  /**
500
   * Returns a {@link ProtocolNegotiator} that does HTTP CONNECT proxy negotiation.
501
   */
502
  public static ProtocolNegotiator httpProxy(final SocketAddress proxyAddress,
503
      final @Nullable String proxyUsername, final @Nullable String proxyPassword,
504
      final ProtocolNegotiator negotiator) {
505
    Preconditions.checkNotNull(negotiator, "negotiator");
1✔
506
    Preconditions.checkNotNull(proxyAddress, "proxyAddress");
1✔
507
    final AsciiString scheme = negotiator.scheme();
1✔
508
    class ProxyNegotiator implements ProtocolNegotiator {
1✔
509
      @Override
510
      public ChannelHandler newHandler(GrpcHttp2ConnectionHandler http2Handler) {
511
        ChannelHandler protocolNegotiationHandler = negotiator.newHandler(http2Handler);
1✔
512
        ChannelLogger negotiationLogger = http2Handler.getNegotiationLogger();
1✔
513
        return new ProxyProtocolNegotiationHandler(
1✔
514
            proxyAddress, proxyUsername, proxyPassword, protocolNegotiationHandler,
515
            negotiationLogger);
516
      }
517

518
      @Override
519
      public AsciiString scheme() {
520
        return scheme;
×
521
      }
522

523
      // This method is not normally called, because we use httpProxy on a per-connection basis in
524
      // NettyChannelBuilder. Instead, we expect `negotiator' to be closed by NettyTransportFactory.
525
      @Override
526
      public void close() {
527
        negotiator.close();
×
528
      }
×
529
    }
530

531
    return new ProxyNegotiator();
1✔
532
  }
533

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

541
    private final SocketAddress address;
542
    @Nullable private final String userName;
543
    @Nullable private final String password;
544

545
    public ProxyProtocolNegotiationHandler(
546
        SocketAddress address,
547
        @Nullable String userName,
548
        @Nullable String password,
549
        ChannelHandler next,
550
        ChannelLogger negotiationLogger) {
551
      super(next, negotiationLogger);
1✔
552
      this.address = Preconditions.checkNotNull(address, "address");
1✔
553
      this.userName = userName;
1✔
554
      this.password = password;
1✔
555
    }
1✔
556

557
    @Override
558
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
559
      HttpProxyHandler nettyProxyHandler;
560
      if (userName == null || password == null) {
1✔
561
        nettyProxyHandler = new HttpProxyHandler(address);
1✔
562
      } else {
563
        nettyProxyHandler = new HttpProxyHandler(address, userName, password);
×
564
      }
565
      ctx.pipeline().addBefore(ctx.name(), /* name= */ null, nettyProxyHandler);
1✔
566
    }
1✔
567

568
    @Override
569
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
570
      if (evt instanceof ProxyConnectionEvent) {
1✔
571
        fireProtocolNegotiationEvent(ctx);
1✔
572
      } else {
573
        super.userEventTriggered(ctx, evt);
×
574
      }
575
    }
1✔
576
  }
577

578
  static final class ClientTlsProtocolNegotiator implements ProtocolNegotiator {
579

580
    public ClientTlsProtocolNegotiator(SslContext sslContext,
581
        ObjectPool<? extends Executor> executorPool, Optional<Runnable> handshakeCompleteRunnable,
582
        X509TrustManager x509ExtendedTrustManager) {
1✔
583
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
584
      this.executorPool = executorPool;
1✔
585
      if (this.executorPool != null) {
1✔
586
        this.executor = this.executorPool.getObject();
1✔
587
      }
588
      this.handshakeCompleteRunnable = handshakeCompleteRunnable;
1✔
589
      this.x509ExtendedTrustManager = x509ExtendedTrustManager;
1✔
590
    }
1✔
591

592
    private final SslContext sslContext;
593
    private final ObjectPool<? extends Executor> executorPool;
594
    private final Optional<Runnable> handshakeCompleteRunnable;
595
    private final X509TrustManager x509ExtendedTrustManager;
596
    private Executor executor;
597

598
    @Override
599
    public AsciiString scheme() {
600
      return Utils.HTTPS;
1✔
601
    }
602

603
    @Override
604
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
605
      ChannelHandler gnh = new GrpcNegotiationHandler(grpcHandler);
1✔
606
      ChannelLogger negotiationLogger = grpcHandler.getNegotiationLogger();
1✔
607
      ChannelHandler cth = new ClientTlsHandler(gnh, sslContext, grpcHandler.getAuthority(),
1✔
608
          this.executor, negotiationLogger, handshakeCompleteRunnable, this,
609
              x509ExtendedTrustManager);
610
      return new WaitUntilActiveHandler(cth, negotiationLogger);
1✔
611
    }
612

613
    @Override
614
    public void close() {
615
      if (this.executorPool != null && this.executor != null) {
1✔
616
        this.executorPool.returnObject(this.executor);
1✔
617
      }
618
    }
1✔
619

620
    @VisibleForTesting
621
    boolean hasX509ExtendedTrustManager() {
622
      return x509ExtendedTrustManager != null;
1✔
623
    }
624
  }
625

626
  static final class ClientTlsHandler extends ProtocolNegotiationHandler {
627

628
    private final SslContext sslContext;
629
    private final String host;
630
    private final int port;
631
    private Executor executor;
632
    private final Optional<Runnable> handshakeCompleteRunnable;
633
    private final X509TrustManager x509ExtendedTrustManager;
634
    private SSLEngine sslEngine;
635

636
    ClientTlsHandler(ChannelHandler next, SslContext sslContext, String authority,
637
        Executor executor, ChannelLogger negotiationLogger,
638
        Optional<Runnable> handshakeCompleteRunnable,
639
        ClientTlsProtocolNegotiator clientTlsProtocolNegotiator,
640
         X509TrustManager x509ExtendedTrustManager) {
641
      super(next, negotiationLogger);
1✔
642
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
643
      HostPort hostPort = parseAuthority(authority);
1✔
644
      this.host = hostPort.host;
1✔
645
      this.port = hostPort.port;
1✔
646
      this.executor = executor;
1✔
647
      this.handshakeCompleteRunnable = handshakeCompleteRunnable;
1✔
648
      this.x509ExtendedTrustManager = x509ExtendedTrustManager;
1✔
649
    }
1✔
650

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

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

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

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

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

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

768
  public static ProtocolNegotiator.ClientFactory tlsClientFactory(SslContext sslContext,
769
      X509TrustManager x509ExtendedTrustManager) {
770
    return new TlsProtocolNegotiatorClientFactory(sslContext, x509ExtendedTrustManager);
1✔
771
  }
772

773
  @VisibleForTesting
774
  static final class TlsProtocolNegotiatorClientFactory
775
      implements ProtocolNegotiator.ClientFactory {
776
    private final SslContext sslContext;
777
    private final X509TrustManager x509ExtendedTrustManager;
778

779
    public TlsProtocolNegotiatorClientFactory(SslContext sslContext,
780
                                              X509TrustManager x509ExtendedTrustManager) {
1✔
781
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
782
      this.x509ExtendedTrustManager = x509ExtendedTrustManager;
1✔
783
    }
1✔
784

785
    @Override public ProtocolNegotiator newNegotiator() {
786
      return tls(sslContext, x509ExtendedTrustManager);
1✔
787
    }
788

789
    @Override public int getDefaultPort() {
790
      return GrpcUtil.DEFAULT_PORT_SSL;
1✔
791
    }
792
  }
793

794
  /** A tuple of (host, port). */
795
  @VisibleForTesting
796
  static final class HostPort {
797
    final String host;
798
    final int port;
799

800
    public HostPort(String host, int port) {
1✔
801
      this.host = host;
1✔
802
      this.port = port;
1✔
803
    }
1✔
804
  }
805

806
  /**
807
   * Returns a {@link ProtocolNegotiator} used for upgrading to HTTP/2 from HTTP/1.x.
808
   */
809
  public static ProtocolNegotiator plaintextUpgrade() {
810
    return new PlaintextUpgradeProtocolNegotiator();
1✔
811
  }
812

813
  public static ProtocolNegotiator.ClientFactory plaintextUpgradeClientFactory() {
814
    return new PlaintextUpgradeProtocolNegotiatorClientFactory();
×
815
  }
816

817
  private static final class PlaintextUpgradeProtocolNegotiatorClientFactory
818
      implements ProtocolNegotiator.ClientFactory {
819
    @Override public ProtocolNegotiator newNegotiator() {
820
      return plaintextUpgrade();
×
821
    }
822

823
    @Override public int getDefaultPort() {
824
      return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
×
825
    }
826
  }
827

828
  static final class PlaintextUpgradeProtocolNegotiator implements ProtocolNegotiator {
1✔
829

830
    @Override
831
    public AsciiString scheme() {
832
      return Utils.HTTP;
×
833
    }
834

835
    @Override
836
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
837
      ChannelHandler upgradeHandler =
1✔
838
          new Http2UpgradeAndGrpcHandler(grpcHandler.getAuthority(), grpcHandler);
1✔
839
      ChannelHandler plaintextHandler =
1✔
840
          new PlaintextHandler(upgradeHandler, grpcHandler.getNegotiationLogger());
1✔
841
      return new WaitUntilActiveHandler(plaintextHandler, grpcHandler.getNegotiationLogger());
1✔
842
    }
843

844
    @Override
845
    public void close() {}
1✔
846
  }
847

848
  /**
849
   * Acts as a combination of Http2Upgrade and {@link GrpcNegotiationHandler}.  Unfortunately,
850
   * this negotiator doesn't follow the pattern of "just one handler doing negotiation at a time."
851
   * This is due to the tight coupling between the upgrade handler and the HTTP/2 handler.
852
   */
853
  static final class Http2UpgradeAndGrpcHandler extends ChannelInboundHandlerAdapter {
854

855
    private final String authority;
856
    private final GrpcHttp2ConnectionHandler next;
857
    private final ChannelLogger negotiationLogger;
858

859
    private ProtocolNegotiationEvent pne;
860

861
    Http2UpgradeAndGrpcHandler(String authority, GrpcHttp2ConnectionHandler next) {
1✔
862
      this.authority = Preconditions.checkNotNull(authority, "authority");
1✔
863
      this.next = Preconditions.checkNotNull(next, "next");
1✔
864
      this.negotiationLogger = next.getNegotiationLogger();
1✔
865
    }
1✔
866

867
    @Override
868
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
869
      negotiationLogger.log(ChannelLogLevel.INFO, "Http2Upgrade started");
1✔
870
      HttpClientCodec httpClientCodec = new HttpClientCodec();
1✔
871
      ctx.pipeline().addBefore(ctx.name(), null, httpClientCodec);
1✔
872

873
      Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(next);
1✔
874
      HttpClientUpgradeHandler upgrader =
1✔
875
          new HttpClientUpgradeHandler(httpClientCodec, upgradeCodec, /*maxContentLength=*/ 1000);
876
      ctx.pipeline().addBefore(ctx.name(), null, upgrader);
1✔
877

878
      // Trigger the HTTP/1.1 plaintext upgrade protocol by issuing an HTTP request
879
      // which causes the upgrade headers to be added
880
      DefaultHttpRequest upgradeTrigger =
1✔
881
          new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
882
      upgradeTrigger.headers().add(HttpHeaderNames.HOST, authority);
1✔
883
      ctx.writeAndFlush(upgradeTrigger).addListener(
1✔
884
          ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
885
      super.handlerAdded(ctx);
1✔
886
    }
1✔
887

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

906
  /**
907
   * Returns a {@link io.netty.channel.ChannelHandler} that ensures that the {@code handler} is
908
   * added to the pipeline writes to the {@link io.netty.channel.Channel} may happen immediately,
909
   * even before it is active.
910
   */
911
  public static ProtocolNegotiator plaintext() {
912
    return new PlaintextProtocolNegotiator();
1✔
913
  }
914

915
  public static ProtocolNegotiator.ClientFactory plaintextClientFactory() {
916
    return new PlaintextProtocolNegotiatorClientFactory();
1✔
917
  }
918

919
  @VisibleForTesting
920
  static final class PlaintextProtocolNegotiatorClientFactory
1✔
921
      implements ProtocolNegotiator.ClientFactory {
922
    @Override public ProtocolNegotiator newNegotiator() {
923
      return plaintext();
1✔
924
    }
925

926
    @Override public int getDefaultPort() {
927
      return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
1✔
928
    }
929
  }
930

931
  private static RuntimeException unavailableException(String msg) {
932
    return Status.UNAVAILABLE.withDescription(msg).asRuntimeException();
1✔
933
  }
934

935
  @VisibleForTesting
936
  static void logSslEngineDetails(Level level, ChannelHandlerContext ctx, String msg,
937
      @Nullable Throwable t) {
938
    if (!log.isLoggable(level)) {
1✔
939
      return;
1✔
940
    }
941

942
    SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
1✔
943
    SSLEngine engine = sslHandler.engine();
1✔
944

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

977
    log.log(level, builder.toString(), t);
1✔
978
  }
1✔
979

980
  /**
981
   * Adapts a {@link ProtocolNegotiationEvent} to the {@link GrpcHttp2ConnectionHandler}.
982
   */
983
  static final class GrpcNegotiationHandler extends ChannelInboundHandlerAdapter {
984
    private final GrpcHttp2ConnectionHandler next;
985

986
    public GrpcNegotiationHandler(GrpcHttp2ConnectionHandler next) {
1✔
987
      this.next = Preconditions.checkNotNull(next, "next");
1✔
988
    }
1✔
989

990
    @Override
991
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
992
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
993
        ProtocolNegotiationEvent protocolNegotiationEvent = (ProtocolNegotiationEvent) evt;
1✔
994
        ctx.pipeline().replace(ctx.name(), null, next);
1✔
995
        next.handleProtocolNegotiationCompleted(
1✔
996
            protocolNegotiationEvent.getAttributes(), protocolNegotiationEvent.getSecurity());
1✔
997
      } else {
1✔
998
        super.userEventTriggered(ctx, evt);
×
999
      }
1000
    }
1✔
1001
  }
1002

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

1030
  /**
1031
   * A negotiator that only does plain text.
1032
   */
1033
  static final class PlaintextProtocolNegotiator implements ProtocolNegotiator {
1✔
1034

1035
    @Override
1036
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
1037
      ChannelHandler grpcNegotiationHandler = new GrpcNegotiationHandler(grpcHandler);
1✔
1038
      ChannelHandler plaintextHandler =
1✔
1039
          new PlaintextHandler(grpcNegotiationHandler, grpcHandler.getNegotiationLogger());
1✔
1040
      ChannelHandler activeHandler = new WaitUntilActiveHandler(plaintextHandler,
1✔
1041
          grpcHandler.getNegotiationLogger());
1✔
1042
      return activeHandler;
1✔
1043
    }
1044

1045
    @Override
1046
    public void close() {}
1✔
1047

1048
    @Override
1049
    public AsciiString scheme() {
1050
      return Utils.HTTP;
1✔
1051
    }
1052
  }
1053

1054
  static final class PlaintextHandler extends ProtocolNegotiationHandler {
1055
    PlaintextHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1056
      super(next, negotiationLogger);
1✔
1057
    }
1✔
1058

1059
    @Override
1060
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1061
      ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1✔
1062
      Attributes attrs = existingPne.getAttributes().toBuilder()
1✔
1063
              .set(GrpcAttributes.ATTR_AUTHORITY_VERIFIER, (authority) -> Status.OK)
1✔
1064
              .build();
1✔
1065
      replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs));
1✔
1066
      fireProtocolNegotiationEvent(ctx);
1✔
1067
    }
1✔
1068
  }
1069

1070
  /**
1071
   * Waits for the channel to be active, and then installs the next Handler.  Using this allows
1072
   * subsequent handlers to assume the channel is active and ready to send.  Additionally, this a
1073
   * {@link ProtocolNegotiationEvent}, with the connection addresses.
1074
   */
1075
  static final class WaitUntilActiveHandler extends ProtocolNegotiationHandler {
1076

1077
    boolean protocolNegotiationEventReceived;
1078

1079
    WaitUntilActiveHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1080
      super(next, negotiationLogger);
1✔
1081
    }
1✔
1082

1083
    @Override
1084
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
1085
      if (protocolNegotiationEventReceived) {
1✔
1086
        replaceOnActive(ctx);
1✔
1087
        fireProtocolNegotiationEvent(ctx);
1✔
1088
      }
1089
      // Still propagate channelActive to the new handler.
1090
      super.channelActive(ctx);
1✔
1091
    }
1✔
1092

1093
    @Override
1094
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1095
      protocolNegotiationEventReceived = true;
1✔
1096
      if (ctx.channel().isActive()) {
1✔
1097
        replaceOnActive(ctx);
1✔
1098
        fireProtocolNegotiationEvent(ctx);
1✔
1099
      }
1100
    }
1✔
1101

1102
    private void replaceOnActive(ChannelHandlerContext ctx) {
1103
      ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1✔
1104
      Attributes attrs = existingPne.getAttributes().toBuilder()
1✔
1105
          .set(Grpc.TRANSPORT_ATTR_LOCAL_ADDR, ctx.channel().localAddress())
1✔
1106
          .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
1✔
1107
          // Later handlers are expected to overwrite this.
1108
          .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.NONE)
1✔
1109
          .build();
1✔
1110
      replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs));
1✔
1111
    }
1✔
1112
  }
1113

1114
  /**
1115
   * ProtocolNegotiationHandler is a convenience handler that makes it easy to follow the rules for
1116
   * protocol negotiation.  Handlers should strongly consider extending this handler.
1117
   */
1118
  static class ProtocolNegotiationHandler extends ChannelDuplexHandler {
1119

1120
    private final ChannelHandler next;
1121
    private final String negotiatorName;
1122
    private ProtocolNegotiationEvent pne;
1123
    private final ChannelLogger negotiationLogger;
1124

1125
    protected ProtocolNegotiationHandler(ChannelHandler next, String negotiatorName,
1126
        ChannelLogger negotiationLogger) {
×
1127
      this.next = Preconditions.checkNotNull(next, "next");
×
1128
      this.negotiatorName = negotiatorName;
×
1129
      this.negotiationLogger = Preconditions.checkNotNull(negotiationLogger, "negotiationLogger");
×
1130
    }
×
1131

1132
    protected ProtocolNegotiationHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1✔
1133
      this.next = Preconditions.checkNotNull(next, "next");
1✔
1134
      this.negotiatorName = getClass().getSimpleName().replace("Handler", "");
1✔
1135
      this.negotiationLogger = Preconditions.checkNotNull(negotiationLogger, "negotiationLogger");
1✔
1136
    }
1✔
1137

1138
    @Override
1139
    public final void handlerAdded(ChannelHandlerContext ctx) throws Exception {
1140
      negotiationLogger.log(ChannelLogLevel.DEBUG, "{0} started", negotiatorName);
1✔
1141
      handlerAdded0(ctx);
1✔
1142
    }
1✔
1143

1144
    @ForOverride
1145
    protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception {
1146
      super.handlerAdded(ctx);
1✔
1147
    }
1✔
1148

1149
    @Override
1150
    public final void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
1151
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
1152
        checkState(pne == null, "pre-existing negotiation: %s < %s", pne, evt);
1✔
1153
        pne = (ProtocolNegotiationEvent) evt;
1✔
1154
        protocolNegotiationEventTriggered(ctx);
1✔
1155
      } else {
1156
        userEventTriggered0(ctx, evt);
1✔
1157
      }
1158
    }
1✔
1159

1160
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
1161
      super.userEventTriggered(ctx, evt);
1✔
1162
    }
1✔
1163

1164
    @ForOverride
1165
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1166
      // no-op
1167
    }
1✔
1168

1169
    protected final ProtocolNegotiationEvent getProtocolNegotiationEvent() {
1170
      checkState(pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1171
      return pne;
1✔
1172
    }
1173

1174
    protected final void replaceProtocolNegotiationEvent(ProtocolNegotiationEvent pne) {
1175
      checkState(this.pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1176
      this.pne = Preconditions.checkNotNull(pne);
1✔
1177
    }
1✔
1178

1179
    protected final void fireProtocolNegotiationEvent(ChannelHandlerContext ctx) {
1180
      checkState(pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1181
      negotiationLogger.log(ChannelLogLevel.INFO, "{0} completed", negotiatorName);
1✔
1182
      ctx.pipeline().replace(ctx.name(), /* newName= */ null, next);
1✔
1183
      ctx.fireUserEventTriggered(pne);
1✔
1184
    }
1✔
1185
  }
1186

1187
  static final class SslEngineWrapper extends NoopSslEngine {
1188
    private final SSLEngine sslEngine;
1189
    private final String peerHost;
1190

1191
    SslEngineWrapper(SSLEngine sslEngine, String peerHost) {
1✔
1192
      this.sslEngine = sslEngine;
1✔
1193
      this.peerHost = peerHost;
1✔
1194
    }
1✔
1195

1196
    @Override
1197
    public String getPeerHost() {
1198
      return peerHost;
×
1199
    }
1200

1201
    @Override
1202
    public SSLSession getHandshakeSession() {
1203
      return new FakeSslSession(peerHost);
1✔
1204
    }
1205

1206
    @Override
1207
    public SSLParameters getSSLParameters() {
1208
      return sslEngine.getSSLParameters();
1✔
1209
    }
1210
  }
1211

1212
  static final class FakeSslSession extends NoopSslSession {
1213
    private final String peerHost;
1214

1215
    FakeSslSession(String peerHost) {
1✔
1216
      this.peerHost = peerHost;
1✔
1217
    }
1✔
1218

1219
    @Override
1220
    public String getPeerHost() {
1221
      return peerHost;
1✔
1222
    }
1223
  }
1224
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc