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

grpc / grpc-java / #20023

21 Oct 2025 04:08PM UTC coverage: 88.596% (+0.03%) from 88.571%
#20023

push

github

web-flow
xds: Introduce flag for fallback to use the xds channel authority if no SNI is determined to be used. (#12422)

This is to allow the previous behavior if needed, and when the xds
channel authority is used as the SNI, it won't be used for the SAN
validation.

34951 of 39450 relevant lines covered (88.6%)

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
      if (authority != null) {
1✔
638
        HostPort hostPort = parseAuthority(authority);
1✔
639
        this.host = hostPort.host;
1✔
640
        this.port = hostPort.port;
1✔
641
      } else {
1✔
642
        this.host = null;
1✔
643
        this.port = 0;
1✔
644
      }
645
      this.executor = executor;
1✔
646
      this.handshakeCompleteRunnable = handshakeCompleteRunnable;
1✔
647
      this.x509TrustManager = x509TrustManager;
1✔
648
    }
1✔
649

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

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

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

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

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

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

772
  public static ProtocolNegotiator.ClientFactory tlsClientFactory(SslContext sslContext,
773
      X509TrustManager x509ExtendedTrustManager) {
774
    return new TlsProtocolNegotiatorClientFactory(sslContext, x509ExtendedTrustManager);
1✔
775
  }
776

777
  @VisibleForTesting
778
  static final class TlsProtocolNegotiatorClientFactory
779
      implements ProtocolNegotiator.ClientFactory {
780
    private final SslContext sslContext;
781
    private final X509TrustManager x509ExtendedTrustManager;
782

783
    public TlsProtocolNegotiatorClientFactory(SslContext sslContext,
784
                                              X509TrustManager x509ExtendedTrustManager) {
1✔
785
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
786
      this.x509ExtendedTrustManager = x509ExtendedTrustManager;
1✔
787
    }
1✔
788

789
    @Override public ProtocolNegotiator newNegotiator() {
790
      return tls(sslContext, x509ExtendedTrustManager);
1✔
791
    }
792

793
    @Override public int getDefaultPort() {
794
      return GrpcUtil.DEFAULT_PORT_SSL;
1✔
795
    }
796
  }
797

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

804
    public HostPort(String host, int port) {
1✔
805
      this.host = host;
1✔
806
      this.port = port;
1✔
807
    }
1✔
808
  }
809

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

817
  public static ProtocolNegotiator.ClientFactory plaintextUpgradeClientFactory() {
818
    return new PlaintextUpgradeProtocolNegotiatorClientFactory();
×
819
  }
820

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

827
    @Override public int getDefaultPort() {
828
      return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
×
829
    }
830
  }
831

832
  static final class PlaintextUpgradeProtocolNegotiator implements ProtocolNegotiator {
1✔
833

834
    @Override
835
    public AsciiString scheme() {
836
      return Utils.HTTP;
×
837
    }
838

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

848
    @Override
849
    public void close() {}
1✔
850
  }
851

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

859
    private final String authority;
860
    private final GrpcHttp2ConnectionHandler next;
861
    private final ChannelLogger negotiationLogger;
862

863
    private ProtocolNegotiationEvent pne;
864

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

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

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

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

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

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

919
  public static ProtocolNegotiator.ClientFactory plaintextClientFactory() {
920
    return new PlaintextProtocolNegotiatorClientFactory();
1✔
921
  }
922

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

930
    @Override public int getDefaultPort() {
931
      return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
1✔
932
    }
933
  }
934

935
  private static RuntimeException unavailableException(String msg) {
936
    return Status.UNAVAILABLE.withDescription(msg).asRuntimeException();
1✔
937
  }
938

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

946
    SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
1✔
947
    SSLEngine engine = sslHandler.engine();
1✔
948

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

981
    log.log(level, builder.toString(), t);
1✔
982
  }
1✔
983

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

990
    public GrpcNegotiationHandler(GrpcHttp2ConnectionHandler next) {
1✔
991
      this.next = Preconditions.checkNotNull(next, "next");
1✔
992
    }
1✔
993

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

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

1034
  /**
1035
   * A negotiator that only does plain text.
1036
   */
1037
  static final class PlaintextProtocolNegotiator implements ProtocolNegotiator {
1✔
1038

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

1049
    @Override
1050
    public void close() {}
1✔
1051

1052
    @Override
1053
    public AsciiString scheme() {
1054
      return Utils.HTTP;
1✔
1055
    }
1056
  }
1057

1058
  static final class PlaintextHandler extends ProtocolNegotiationHandler {
1059
    PlaintextHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1060
      super(next, negotiationLogger);
1✔
1061
    }
1✔
1062

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

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

1081
    boolean protocolNegotiationEventReceived;
1082

1083
    WaitUntilActiveHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1084
      super(next, negotiationLogger);
1✔
1085
    }
1✔
1086

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

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

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

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

1124
    private final ChannelHandler next;
1125
    private final String negotiatorName;
1126
    private ProtocolNegotiationEvent pne;
1127
    private final ChannelLogger negotiationLogger;
1128

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

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

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

1148
    @ForOverride
1149
    protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception {
1150
      super.handlerAdded(ctx);
1✔
1151
    }
1✔
1152

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

1164
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
1165
      super.userEventTriggered(ctx, evt);
1✔
1166
    }
1✔
1167

1168
    @ForOverride
1169
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1170
      // no-op
1171
    }
1✔
1172

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

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

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

1191
  static final class SslEngineWrapper extends NoopSslEngine {
1192
    private final SSLEngine sslEngine;
1193
    private final String peerHost;
1194

1195
    SslEngineWrapper(SSLEngine sslEngine, String peerHost) {
1✔
1196
      this.sslEngine = sslEngine;
1✔
1197
      this.peerHost = peerHost;
1✔
1198
    }
1✔
1199

1200
    @Override
1201
    public String getPeerHost() {
1202
      return peerHost;
×
1203
    }
1204

1205
    @Override
1206
    public SSLSession getHandshakeSession() {
1207
      return new FakeSslSession(peerHost);
1✔
1208
    }
1209

1210
    @Override
1211
    public SSLParameters getSSLParameters() {
1212
      return sslEngine.getSSLParameters();
1✔
1213
    }
1214
  }
1215

1216
  static final class FakeSslSession extends NoopSslSession {
1217
    private final String peerHost;
1218

1219
    FakeSslSession(String peerHost) {
1✔
1220
      this.peerHost = peerHost;
1✔
1221
    }
1✔
1222

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