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

grpc / grpc-java / #19596

19 Dec 2024 03:54PM UTC coverage: 88.598% (-0.009%) from 88.607%
#19596

push

github

web-flow
Re-enable animalsniffer, fixing violations

In 61f19d707a I swapped the signatures to use the version catalog. But I
failed to preserve the `@signature` extension and it all seemed to
work... But in fact all the animalsniffer tasks were completing as
SKIPPED as they lacked signatures. The build.gradle changes in this
commit are to fix that while still using version catalog.

But while it was broken violations crept in. Most violations weren't
too important and we're not surprised went unnoticed. For example, Netty
with TLS has long required the Java 8 API
`setEndpointIdentificationAlgorithm()`, so using `Optional` in the same
code path didn't harm anything in particular. I still swapped it to
Guava's `Optional` to avoid overuse of `@IgnoreJRERequirement`.

One important violation has not been fixed and instead I've disabled the
android signature in api/build.gradle for the moment.  The violation is
in StatusException using the `fillInStackTrace` overload of Exception.
This problem [had been noticed][PR11066], but we couldn't figure out
what was going on. AnimalSniffer is now noticing this and agreeing with
the internal linter. There is still a question of why our interop tests
failed to notice this, but given they are no longer running on pre-API
level 24, that may forever be a mystery.

[PR11066]: https://github.com/grpc/grpc-java/pull/11066

33481 of 37790 relevant lines covered (88.6%)

0.89 hits per line

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

90.53
/../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.checkNotNull;
20
import static com.google.common.base.Preconditions.checkState;
21

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

87
/**
88
 * Common {@link ProtocolNegotiator}s used by gRPC.
89
 */
90
final class ProtocolNegotiators {
91
  private static final Logger log = Logger.getLogger(ProtocolNegotiators.class.getName());
1✔
92
  private static final EnumSet<TlsChannelCredentials.Feature> understoodTlsFeatures =
1✔
93
      EnumSet.of(
1✔
94
          TlsChannelCredentials.Feature.MTLS, TlsChannelCredentials.Feature.CUSTOM_MANAGERS);
95
  private static final EnumSet<TlsServerCredentials.Feature> understoodServerTlsFeatures =
1✔
96
      EnumSet.of(
1✔
97
          TlsServerCredentials.Feature.MTLS, TlsServerCredentials.Feature.CUSTOM_MANAGERS);
98

99

100
  private ProtocolNegotiators() {
101
  }
102

103
  public static FromChannelCredentialsResult from(ChannelCredentials creds) {
104
    if (creds instanceof TlsChannelCredentials) {
1✔
105
      TlsChannelCredentials tlsCreds = (TlsChannelCredentials) creds;
1✔
106
      Set<TlsChannelCredentials.Feature> incomprehensible =
1✔
107
          tlsCreds.incomprehensible(understoodTlsFeatures);
1✔
108
      if (!incomprehensible.isEmpty()) {
1✔
109
        return FromChannelCredentialsResult.error(
1✔
110
            "TLS features not understood: " + incomprehensible);
111
      }
112
      SslContextBuilder builder = GrpcSslContexts.forClient();
1✔
113
      if (tlsCreds.getKeyManagers() != null) {
1✔
114
        builder.keyManager(new FixedKeyManagerFactory(tlsCreds.getKeyManagers()));
1✔
115
      } else if (tlsCreds.getPrivateKey() != null) {
1✔
116
        builder.keyManager(
1✔
117
            new ByteArrayInputStream(tlsCreds.getCertificateChain()),
1✔
118
            new ByteArrayInputStream(tlsCreds.getPrivateKey()),
1✔
119
            tlsCreds.getPrivateKeyPassword());
1✔
120
      }
121
      if (tlsCreds.getTrustManagers() != null) {
1✔
122
        builder.trustManager(new FixedTrustManagerFactory(tlsCreds.getTrustManagers()));
1✔
123
      } else if (tlsCreds.getRootCertificates() != null) {
1✔
124
        builder.trustManager(new ByteArrayInputStream(tlsCreds.getRootCertificates()));
1✔
125
      } // else use system default
126
      try {
127
        return FromChannelCredentialsResult.negotiator(tlsClientFactory(builder.build()));
1✔
128
      } catch (SSLException ex) {
×
129
        log.log(Level.FINE, "Exception building SslContext", ex);
×
130
        return FromChannelCredentialsResult.error(
×
131
            "Unable to create SslContext: " + ex.getMessage());
×
132
      }
133

134
    } else if (creds instanceof InsecureChannelCredentials) {
1✔
135
      return FromChannelCredentialsResult.negotiator(plaintextClientFactory());
1✔
136

137
    } else if (creds instanceof CompositeChannelCredentials) {
1✔
138
      CompositeChannelCredentials compCreds = (CompositeChannelCredentials) creds;
1✔
139
      return from(compCreds.getChannelCredentials())
1✔
140
          .withCallCredentials(compCreds.getCallCredentials());
1✔
141

142
    } else if (creds instanceof NettyChannelCredentials) {
1✔
143
      NettyChannelCredentials nettyCreds = (NettyChannelCredentials) creds;
1✔
144
      return FromChannelCredentialsResult.negotiator(nettyCreds.getNegotiator());
1✔
145

146
    } else if (creds instanceof ChoiceChannelCredentials) {
1✔
147
      ChoiceChannelCredentials choiceCreds = (ChoiceChannelCredentials) creds;
1✔
148
      StringBuilder error = new StringBuilder();
1✔
149
      for (ChannelCredentials innerCreds : choiceCreds.getCredentialsList()) {
1✔
150
        FromChannelCredentialsResult result = from(innerCreds);
1✔
151
        if (result.error == null) {
1✔
152
          return result;
1✔
153
        }
154
        error.append(", ");
1✔
155
        error.append(result.error);
1✔
156
      }
1✔
157
      return FromChannelCredentialsResult.error(error.substring(2));
1✔
158

159
    } else {
160
      return FromChannelCredentialsResult.error(
1✔
161
          "Unsupported credential type: " + creds.getClass().getName());
1✔
162
    }
163
  }
164

165
  public static FromServerCredentialsResult from(ServerCredentials creds) {
166
    if (creds instanceof TlsServerCredentials) {
1✔
167
      TlsServerCredentials tlsCreds = (TlsServerCredentials) creds;
1✔
168
      Set<TlsServerCredentials.Feature> incomprehensible =
1✔
169
          tlsCreds.incomprehensible(understoodServerTlsFeatures);
1✔
170
      if (!incomprehensible.isEmpty()) {
1✔
171
        return FromServerCredentialsResult.error(
1✔
172
            "TLS features not understood: " + incomprehensible);
173
      }
174
      SslContextBuilder builder;
175
      if (tlsCreds.getKeyManagers() != null) {
1✔
176
        builder = GrpcSslContexts.configure(SslContextBuilder.forServer(
1✔
177
            new FixedKeyManagerFactory(tlsCreds.getKeyManagers())));
1✔
178
      } else if (tlsCreds.getPrivateKey() != null) {
1✔
179
        builder = GrpcSslContexts.forServer(
1✔
180
            new ByteArrayInputStream(tlsCreds.getCertificateChain()),
1✔
181
            new ByteArrayInputStream(tlsCreds.getPrivateKey()),
1✔
182
            tlsCreds.getPrivateKeyPassword());
1✔
183
      } else {
184
        throw new AssertionError("BUG! No key");
×
185
      }
186
      if (tlsCreds.getTrustManagers() != null) {
1✔
187
        builder.trustManager(new FixedTrustManagerFactory(tlsCreds.getTrustManagers()));
1✔
188
      } else if (tlsCreds.getRootCertificates() != null) {
1✔
189
        builder.trustManager(new ByteArrayInputStream(tlsCreds.getRootCertificates()));
1✔
190
      } // else use system default
191
      switch (tlsCreds.getClientAuth()) {
1✔
192
        case OPTIONAL:
193
          builder.clientAuth(io.netty.handler.ssl.ClientAuth.OPTIONAL);
1✔
194
          break;
1✔
195

196
        case REQUIRE:
197
          builder.clientAuth(io.netty.handler.ssl.ClientAuth.REQUIRE);
1✔
198
          break;
1✔
199

200
        case NONE:
201
          builder.clientAuth(io.netty.handler.ssl.ClientAuth.NONE);
1✔
202
          break;
1✔
203

204
        default:
205
          return FromServerCredentialsResult.error(
×
206
              "Unknown TlsServerCredentials.ClientAuth value: " + tlsCreds.getClientAuth());
×
207
      }
208
      SslContext sslContext;
209
      try {
210
        sslContext = builder.build();
1✔
211
      } catch (SSLException ex) {
×
212
        throw new IllegalArgumentException(
×
213
            "Unexpected error converting ServerCredentials to Netty SslContext", ex);
214
      }
1✔
215
      return FromServerCredentialsResult.negotiator(serverTlsFactory(sslContext));
1✔
216

217
    } else if (creds instanceof InsecureServerCredentials) {
1✔
218
      return FromServerCredentialsResult.negotiator(serverPlaintextFactory());
1✔
219

220
    } else if (creds instanceof NettyServerCredentials) {
1✔
221
      NettyServerCredentials nettyCreds = (NettyServerCredentials) creds;
1✔
222
      return FromServerCredentialsResult.negotiator(nettyCreds.getNegotiator());
1✔
223

224
    } else if (creds instanceof ChoiceServerCredentials) {
1✔
225
      ChoiceServerCredentials choiceCreds = (ChoiceServerCredentials) creds;
1✔
226
      StringBuilder error = new StringBuilder();
1✔
227
      for (ServerCredentials innerCreds : choiceCreds.getCredentialsList()) {
1✔
228
        FromServerCredentialsResult result = from(innerCreds);
1✔
229
        if (result.error == null) {
1✔
230
          return result;
1✔
231
        }
232
        error.append(", ");
1✔
233
        error.append(result.error);
1✔
234
      }
1✔
235
      return FromServerCredentialsResult.error(error.substring(2));
1✔
236

237
    } else {
238
      return FromServerCredentialsResult.error(
1✔
239
          "Unsupported credential type: " + creds.getClass().getName());
1✔
240
    }
241
  }
242

243
  public static final class FromChannelCredentialsResult {
244
    public final ProtocolNegotiator.ClientFactory negotiator;
245
    public final CallCredentials callCredentials;
246
    public final String error;
247

248
    private FromChannelCredentialsResult(ProtocolNegotiator.ClientFactory negotiator,
249
        CallCredentials creds, String error) {
1✔
250
      this.negotiator = negotiator;
1✔
251
      this.callCredentials = creds;
1✔
252
      this.error = error;
1✔
253
    }
1✔
254

255
    public static FromChannelCredentialsResult error(String error) {
256
      return new FromChannelCredentialsResult(
1✔
257
          null, null, Preconditions.checkNotNull(error, "error"));
1✔
258
    }
259

260
    public static FromChannelCredentialsResult negotiator(
261
        ProtocolNegotiator.ClientFactory factory) {
262
      return new FromChannelCredentialsResult(
1✔
263
          Preconditions.checkNotNull(factory, "factory"), null, null);
1✔
264
    }
265

266
    public FromChannelCredentialsResult withCallCredentials(CallCredentials callCreds) {
267
      Preconditions.checkNotNull(callCreds, "callCreds");
1✔
268
      if (error != null) {
1✔
269
        return this;
×
270
      }
271
      if (this.callCredentials != null) {
1✔
272
        callCreds = new CompositeCallCredentials(this.callCredentials, callCreds);
×
273
      }
274
      return new FromChannelCredentialsResult(negotiator, callCreds, null);
1✔
275
    }
276
  }
277

278
  public static final class FromServerCredentialsResult {
279
    public final ProtocolNegotiator.ServerFactory negotiator;
280
    public final String error;
281

282
    private FromServerCredentialsResult(ProtocolNegotiator.ServerFactory negotiator, String error) {
1✔
283
      this.negotiator = negotiator;
1✔
284
      this.error = error;
1✔
285
    }
1✔
286

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

291
    public static FromServerCredentialsResult negotiator(ProtocolNegotiator.ServerFactory factory) {
292
      return new FromServerCredentialsResult(Preconditions.checkNotNull(factory, "factory"), null);
1✔
293
    }
294
  }
295

296
  public static ProtocolNegotiator.ServerFactory fixedServerFactory(
297
      ProtocolNegotiator negotiator) {
298
    return new FixedProtocolNegotiatorServerFactory(negotiator);
×
299
  }
300

301
  private static final class FixedProtocolNegotiatorServerFactory
302
      implements ProtocolNegotiator.ServerFactory {
303
    private final ProtocolNegotiator protocolNegotiator;
304

305
    public FixedProtocolNegotiatorServerFactory(ProtocolNegotiator protocolNegotiator) {
×
306
      this.protocolNegotiator =
×
307
          Preconditions.checkNotNull(protocolNegotiator, "protocolNegotiator");
×
308
    }
×
309

310
    @Override
311
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
312
      return protocolNegotiator;
×
313
    }
314
  }
315

316
  /**
317
   * Create a server plaintext handler for gRPC.
318
   */
319
  public static ProtocolNegotiator serverPlaintext() {
320
    return new PlaintextProtocolNegotiator();
1✔
321
  }
322

323
  /**
324
   * Create a server plaintext handler factory for gRPC.
325
   */
326
  public static ProtocolNegotiator.ServerFactory serverPlaintextFactory() {
327
    return new PlaintextProtocolNegotiatorServerFactory();
1✔
328
  }
329

330
  @VisibleForTesting
331
  static final class PlaintextProtocolNegotiatorServerFactory
1✔
332
      implements ProtocolNegotiator.ServerFactory {
333
    @Override
334
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
335
      return serverPlaintext();
1✔
336
    }
337
  }
338

339
  public static ProtocolNegotiator.ServerFactory serverTlsFactory(SslContext sslContext) {
340
    return new TlsProtocolNegotiatorServerFactory(sslContext);
1✔
341
  }
342

343
  @VisibleForTesting
344
  static final class TlsProtocolNegotiatorServerFactory
345
      implements ProtocolNegotiator.ServerFactory {
346
    private final SslContext sslContext;
347

348
    public TlsProtocolNegotiatorServerFactory(SslContext sslContext) {
1✔
349
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
350
    }
1✔
351

352
    @Override
353
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
354
      return serverTls(sslContext, offloadExecutorPool);
1✔
355
    }
356
  }
357

358
  /**
359
   * Create a server TLS handler for HTTP/2 capable of using ALPN/NPN.
360
   * @param executorPool a dedicated {@link Executor} pool for time-consuming TLS tasks
361
   */
362
  public static ProtocolNegotiator serverTls(final SslContext sslContext,
363
      final ObjectPool<? extends Executor> executorPool) {
364
    Preconditions.checkNotNull(sslContext, "sslContext");
1✔
365
    final Executor executor;
366
    if (executorPool != null) {
1✔
367
      // The handlers here can out-live the {@link ProtocolNegotiator}.
368
      // To keep their own reference to executor from executorPool, we use an extra (unused)
369
      // reference here forces the executor to stay alive, which prevents it from being re-created
370
      // for every connection.
371
      executor = executorPool.getObject();
1✔
372
    } else {
373
      executor = null;
1✔
374
    }
375
    return new ProtocolNegotiator() {
1✔
376
      @Override
377
      public ChannelHandler newHandler(GrpcHttp2ConnectionHandler handler) {
378
        ChannelHandler gnh = new GrpcNegotiationHandler(handler);
1✔
379
        ChannelHandler sth = new ServerTlsHandler(gnh, sslContext, executorPool);
1✔
380
        return new WaitUntilActiveHandler(sth, handler.getNegotiationLogger());
1✔
381
      }
382

383
      @Override
384
      public void close() {
385
        if (executorPool != null && executor != null) {
1✔
386
          executorPool.returnObject(executor);
1✔
387
        }
388
      }
1✔
389

390
      @Override
391
      public AsciiString scheme() {
392
        return Utils.HTTPS;
×
393
      }
394
    };
395
  }
396

397
  /**
398
   * Create a server TLS handler for HTTP/2 capable of using ALPN/NPN.
399
   */
400
  public static ProtocolNegotiator serverTls(final SslContext sslContext) {
401
    return serverTls(sslContext, null);
1✔
402
  }
403

404
  static final class ServerTlsHandler extends ChannelInboundHandlerAdapter {
405
    private Executor executor;
406
    private final ChannelHandler next;
407
    private final SslContext sslContext;
408

409
    private ProtocolNegotiationEvent pne = ProtocolNegotiationEvent.DEFAULT;
1✔
410

411
    ServerTlsHandler(ChannelHandler next,
412
        SslContext sslContext,
413
        final ObjectPool<? extends Executor> executorPool) {
1✔
414
      this.sslContext = checkNotNull(sslContext, "sslContext");
1✔
415
      this.next = checkNotNull(next, "next");
1✔
416
      if (executorPool != null) {
1✔
417
        this.executor = executorPool.getObject();
1✔
418
      }
419
    }
1✔
420

421
    @Override
422
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
423
      super.handlerAdded(ctx);
1✔
424
      SSLEngine sslEngine = sslContext.newEngine(ctx.alloc());
1✔
425
      ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
1✔
426
          ? new SslHandler(sslEngine, false, this.executor)
1✔
427
          : new SslHandler(sslEngine, false));
1✔
428
    }
1✔
429

430
    @Override
431
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
432
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
433
        pne = (ProtocolNegotiationEvent) evt;
1✔
434
      } else if (evt instanceof SslHandshakeCompletionEvent) {
1✔
435
        SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
1✔
436
        if (!handshakeEvent.isSuccess()) {
1✔
437
          logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed for new client.", null);
1✔
438
          ctx.fireExceptionCaught(handshakeEvent.cause());
1✔
439
          return;
1✔
440
        }
441
        SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
1✔
442
        if (!sslContext.applicationProtocolNegotiator().protocols().contains(
1✔
443
                sslHandler.applicationProtocol())) {
1✔
444
          logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed for new client.", null);
1✔
445
          ctx.fireExceptionCaught(unavailableException(
1✔
446
              "Failed protocol negotiation: Unable to find compatible protocol"));
447
          return;
1✔
448
        }
449
        ctx.pipeline().replace(ctx.name(), null, next);
1✔
450
        fireProtocolNegotiationEvent(ctx, sslHandler.engine().getSession());
1✔
451
      } else {
1✔
452
        super.userEventTriggered(ctx, evt);
1✔
453
      }
454
    }
1✔
455

456
    private void fireProtocolNegotiationEvent(ChannelHandlerContext ctx, SSLSession session) {
457
      Security security = new Security(new Tls(session));
1✔
458
      Attributes attrs = pne.getAttributes().toBuilder()
1✔
459
          .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY)
1✔
460
          .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
1✔
461
          .build();
1✔
462
      ctx.fireUserEventTriggered(pne.withAttributes(attrs).withSecurity(security));
1✔
463
    }
1✔
464
  }
465

466
  /**
467
   * Returns a {@link ProtocolNegotiator} that does HTTP CONNECT proxy negotiation.
468
   */
469
  public static ProtocolNegotiator httpProxy(final SocketAddress proxyAddress,
470
      final @Nullable String proxyUsername, final @Nullable String proxyPassword,
471
      final ProtocolNegotiator negotiator) {
472
    checkNotNull(negotiator, "negotiator");
1✔
473
    checkNotNull(proxyAddress, "proxyAddress");
1✔
474
    final AsciiString scheme = negotiator.scheme();
1✔
475
    class ProxyNegotiator implements ProtocolNegotiator {
1✔
476
      @Override
477
      public ChannelHandler newHandler(GrpcHttp2ConnectionHandler http2Handler) {
478
        ChannelHandler protocolNegotiationHandler = negotiator.newHandler(http2Handler);
1✔
479
        ChannelLogger negotiationLogger = http2Handler.getNegotiationLogger();
1✔
480
        return new ProxyProtocolNegotiationHandler(
1✔
481
            proxyAddress, proxyUsername, proxyPassword, protocolNegotiationHandler,
482
            negotiationLogger);
483
      }
484

485
      @Override
486
      public AsciiString scheme() {
487
        return scheme;
×
488
      }
489

490
      // This method is not normally called, because we use httpProxy on a per-connection basis in
491
      // NettyChannelBuilder. Instead, we expect `negotiator' to be closed by NettyTransportFactory.
492
      @Override
493
      public void close() {
494
        negotiator.close();
×
495
      }
×
496
    }
497

498
    return new ProxyNegotiator();
1✔
499
  }
500

501
  /**
502
   * A Proxy handler follows {@link ProtocolNegotiationHandler} pattern. Upon successful proxy
503
   * connection, this handler will install {@code next} handler which should be a handler from
504
   * other type of {@link ProtocolNegotiator} to continue negotiating protocol using proxy.
505
   */
506
  static final class ProxyProtocolNegotiationHandler extends ProtocolNegotiationHandler {
507

508
    private final SocketAddress address;
509
    @Nullable private final String userName;
510
    @Nullable private final String password;
511

512
    public ProxyProtocolNegotiationHandler(
513
        SocketAddress address,
514
        @Nullable String userName,
515
        @Nullable String password,
516
        ChannelHandler next,
517
        ChannelLogger negotiationLogger) {
518
      super(next, negotiationLogger);
1✔
519
      this.address = checkNotNull(address, "address");
1✔
520
      this.userName = userName;
1✔
521
      this.password = password;
1✔
522
    }
1✔
523

524
    @Override
525
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
526
      HttpProxyHandler nettyProxyHandler;
527
      if (userName == null || password == null) {
1✔
528
        nettyProxyHandler = new HttpProxyHandler(address);
1✔
529
      } else {
530
        nettyProxyHandler = new HttpProxyHandler(address, userName, password);
×
531
      }
532
      ctx.pipeline().addBefore(ctx.name(), /* name= */ null, nettyProxyHandler);
1✔
533
    }
1✔
534

535
    @Override
536
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
537
      if (evt instanceof ProxyConnectionEvent) {
1✔
538
        fireProtocolNegotiationEvent(ctx);
1✔
539
      } else {
540
        super.userEventTriggered(ctx, evt);
×
541
      }
542
    }
1✔
543
  }
544

545
  static final class ClientTlsProtocolNegotiator implements ProtocolNegotiator {
546

547
    public ClientTlsProtocolNegotiator(SslContext sslContext,
548
        ObjectPool<? extends Executor> executorPool, Optional<Runnable> handshakeCompleteRunnable) {
1✔
549
      this.sslContext = checkNotNull(sslContext, "sslContext");
1✔
550
      this.executorPool = executorPool;
1✔
551
      if (this.executorPool != null) {
1✔
552
        this.executor = this.executorPool.getObject();
1✔
553
      }
554
      this.handshakeCompleteRunnable = handshakeCompleteRunnable;
1✔
555
    }
1✔
556

557
    private final SslContext sslContext;
558
    private final ObjectPool<? extends Executor> executorPool;
559
    private final Optional<Runnable> handshakeCompleteRunnable;
560
    private Executor executor;
561

562
    @Override
563
    public AsciiString scheme() {
564
      return Utils.HTTPS;
1✔
565
    }
566

567
    @Override
568
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
569
      ChannelHandler gnh = new GrpcNegotiationHandler(grpcHandler);
1✔
570
      ChannelLogger negotiationLogger = grpcHandler.getNegotiationLogger();
1✔
571
      ChannelHandler cth = new ClientTlsHandler(gnh, sslContext, grpcHandler.getAuthority(),
1✔
572
          this.executor, negotiationLogger, handshakeCompleteRunnable);
573
      return new WaitUntilActiveHandler(cth, negotiationLogger);
1✔
574
    }
575

576
    @Override
577
    public void close() {
578
      if (this.executorPool != null && this.executor != null) {
1✔
579
        this.executorPool.returnObject(this.executor);
1✔
580
      }
581
    }
1✔
582
  }
583

584
  static final class ClientTlsHandler extends ProtocolNegotiationHandler {
585

586
    private final SslContext sslContext;
587
    private final String host;
588
    private final int port;
589
    private Executor executor;
590
    private final Optional<Runnable> handshakeCompleteRunnable;
591

592
    ClientTlsHandler(ChannelHandler next, SslContext sslContext, String authority,
593
        Executor executor, ChannelLogger negotiationLogger,
594
        Optional<Runnable> handshakeCompleteRunnable) {
595
      super(next, negotiationLogger);
1✔
596
      this.sslContext = checkNotNull(sslContext, "sslContext");
1✔
597
      HostPort hostPort = parseAuthority(authority);
1✔
598
      this.host = hostPort.host;
1✔
599
      this.port = hostPort.port;
1✔
600
      this.executor = executor;
1✔
601
      this.handshakeCompleteRunnable = handshakeCompleteRunnable;
1✔
602
    }
1✔
603

604
    @Override
605
    @IgnoreJRERequirement
606
    protected void handlerAdded0(ChannelHandlerContext ctx) {
607
      SSLEngine sslEngine = sslContext.newEngine(ctx.alloc(), host, port);
1✔
608
      SSLParameters sslParams = sslEngine.getSSLParameters();
1✔
609
      sslParams.setEndpointIdentificationAlgorithm("HTTPS");
1✔
610
      sslEngine.setSSLParameters(sslParams);
1✔
611
      ctx.pipeline().addBefore(ctx.name(), /* name= */ null, this.executor != null
1✔
612
          ? new SslHandler(sslEngine, false, this.executor)
1✔
613
          : new SslHandler(sslEngine, false));
1✔
614
    }
1✔
615

616
    @Override
617
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
618
      if (evt instanceof SslHandshakeCompletionEvent) {
1✔
619
        SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt;
1✔
620
        if (handshakeEvent.isSuccess()) {
1✔
621
          SslHandler handler = ctx.pipeline().get(SslHandler.class);
1✔
622
          if (sslContext.applicationProtocolNegotiator().protocols()
1✔
623
              .contains(handler.applicationProtocol())) {
1✔
624
            // Successfully negotiated the protocol.
625
            logSslEngineDetails(Level.FINER, ctx, "TLS negotiation succeeded.", null);
1✔
626
            propagateTlsComplete(ctx, handler.engine().getSession());
1✔
627
          } else {
628
            Exception ex =
1✔
629
                unavailableException("Failed ALPN negotiation: Unable to find compatible protocol");
1✔
630
            logSslEngineDetails(Level.FINE, ctx, "TLS negotiation failed.", ex);
1✔
631
            if (handshakeCompleteRunnable.isPresent()) {
1✔
632
              handshakeCompleteRunnable.get().run();
×
633
            }
634
            ctx.fireExceptionCaught(ex);
1✔
635
          }
636
        } else {
1✔
637
          Throwable t = handshakeEvent.cause();
1✔
638
          if (t instanceof ClosedChannelException) {
1✔
639
            // On channelInactive(), SslHandler creates its own ClosedChannelException and
640
            // propagates it before the actual channelInactive(). So we assume here that any
641
            // such exception is from channelInactive() and emulate the normal behavior of
642
            // WriteBufferingAndExceptionHandler
643
            t = Status.UNAVAILABLE
1✔
644
                .withDescription("Connection closed while performing TLS negotiation")
1✔
645
                .withCause(t)
1✔
646
                .asRuntimeException();
1✔
647
          }
648
          if (handshakeCompleteRunnable.isPresent()) {
1✔
649
            handshakeCompleteRunnable.get().run();
×
650
          }
651
          ctx.fireExceptionCaught(t);
1✔
652
        }
653
      } else {
1✔
654
        super.userEventTriggered0(ctx, evt);
1✔
655
      }
656
    }
1✔
657

658
    private void propagateTlsComplete(ChannelHandlerContext ctx, SSLSession session) {
659
      Security security = new Security(new Tls(session));
1✔
660
      ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1✔
661
      Attributes attrs = existingPne.getAttributes().toBuilder()
1✔
662
          .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.PRIVACY_AND_INTEGRITY)
1✔
663
          .set(Grpc.TRANSPORT_ATTR_SSL_SESSION, session)
1✔
664
          .build();
1✔
665
      replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs).withSecurity(security));
1✔
666
      if (handshakeCompleteRunnable.isPresent()) {
1✔
667
        handshakeCompleteRunnable.get().run();
×
668
      }
669
      fireProtocolNegotiationEvent(ctx);
1✔
670
    }
1✔
671
  }
672

673
  @VisibleForTesting
674
  static HostPort parseAuthority(String authority) {
675
    URI uri = GrpcUtil.authorityToUri(Preconditions.checkNotNull(authority, "authority"));
1✔
676
    String host;
677
    int port;
678
    if (uri.getHost() != null) {
1✔
679
      host = uri.getHost();
1✔
680
      port = uri.getPort();
1✔
681
    } else {
682
      /*
683
       * Implementation note: We pick -1 as the port here rather than deriving it from the
684
       * original socket address.  The SSL engine doesn't use this port number when contacting the
685
       * remote server, but rather it is used for other things like SSL Session caching.  When an
686
       * invalid authority is provided (like "bad_cert"), picking the original port and passing it
687
       * in would mean that the port might used under the assumption that it was correct.   By
688
       * using -1 here, it forces the SSL implementation to treat it as invalid.
689
       */
690
      host = authority;
1✔
691
      port = -1;
1✔
692
    }
693
    return new HostPort(host, port);
1✔
694
  }
695

696
  /**
697
   * Returns a {@link ProtocolNegotiator} that ensures the pipeline is set up so that TLS will
698
   * be negotiated, the {@code handler} is added and writes to the {@link io.netty.channel.Channel}
699
   * may happen immediately, even before the TLS Handshake is complete.
700
   * @param executorPool a dedicated {@link Executor} pool for time-consuming TLS tasks
701
   */
702
  public static ProtocolNegotiator tls(SslContext sslContext,
703
      ObjectPool<? extends Executor> executorPool, Optional<Runnable> handshakeCompleteRunnable) {
704
    return new ClientTlsProtocolNegotiator(sslContext, executorPool, handshakeCompleteRunnable);
1✔
705
  }
706

707
  /**
708
   * Returns a {@link ProtocolNegotiator} that ensures the pipeline is set up so that TLS will
709
   * be negotiated, the {@code handler} is added and writes to the {@link io.netty.channel.Channel}
710
   * may happen immediately, even before the TLS Handshake is complete.
711
   */
712
  public static ProtocolNegotiator tls(SslContext sslContext) {
713
    return tls(sslContext, null, Optional.absent());
1✔
714
  }
715

716
  public static ProtocolNegotiator.ClientFactory tlsClientFactory(SslContext sslContext) {
717
    return new TlsProtocolNegotiatorClientFactory(sslContext);
1✔
718
  }
719

720
  @VisibleForTesting
721
  static final class TlsProtocolNegotiatorClientFactory
722
      implements ProtocolNegotiator.ClientFactory {
723
    private final SslContext sslContext;
724

725
    public TlsProtocolNegotiatorClientFactory(SslContext sslContext) {
1✔
726
      this.sslContext = Preconditions.checkNotNull(sslContext, "sslContext");
1✔
727
    }
1✔
728

729
    @Override public ProtocolNegotiator newNegotiator() {
730
      return tls(sslContext);
1✔
731
    }
732

733
    @Override public int getDefaultPort() {
734
      return GrpcUtil.DEFAULT_PORT_SSL;
1✔
735
    }
736
  }
737

738
  /** A tuple of (host, port). */
739
  @VisibleForTesting
740
  static final class HostPort {
741
    final String host;
742
    final int port;
743

744
    public HostPort(String host, int port) {
1✔
745
      this.host = host;
1✔
746
      this.port = port;
1✔
747
    }
1✔
748
  }
749

750
  /**
751
   * Returns a {@link ProtocolNegotiator} used for upgrading to HTTP/2 from HTTP/1.x.
752
   */
753
  public static ProtocolNegotiator plaintextUpgrade() {
754
    return new PlaintextUpgradeProtocolNegotiator();
1✔
755
  }
756

757
  public static ProtocolNegotiator.ClientFactory plaintextUpgradeClientFactory() {
758
    return new PlaintextUpgradeProtocolNegotiatorClientFactory();
×
759
  }
760

761
  private static final class PlaintextUpgradeProtocolNegotiatorClientFactory
762
      implements ProtocolNegotiator.ClientFactory {
763
    @Override public ProtocolNegotiator newNegotiator() {
764
      return plaintextUpgrade();
×
765
    }
766

767
    @Override public int getDefaultPort() {
768
      return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
×
769
    }
770
  }
771

772
  static final class PlaintextUpgradeProtocolNegotiator implements ProtocolNegotiator {
1✔
773

774
    @Override
775
    public AsciiString scheme() {
776
      return Utils.HTTP;
×
777
    }
778

779
    @Override
780
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
781
      ChannelHandler upgradeHandler =
1✔
782
          new Http2UpgradeAndGrpcHandler(grpcHandler.getAuthority(), grpcHandler);
1✔
783
      return new WaitUntilActiveHandler(upgradeHandler, grpcHandler.getNegotiationLogger());
1✔
784
    }
785

786
    @Override
787
    public void close() {}
1✔
788
  }
789

790
  /**
791
   * Acts as a combination of Http2Upgrade and {@link GrpcNegotiationHandler}.  Unfortunately,
792
   * this negotiator doesn't follow the pattern of "just one handler doing negotiation at a time."
793
   * This is due to the tight coupling between the upgrade handler and the HTTP/2 handler.
794
   */
795
  static final class Http2UpgradeAndGrpcHandler extends ChannelInboundHandlerAdapter {
796

797
    private final String authority;
798
    private final GrpcHttp2ConnectionHandler next;
799
    private final ChannelLogger negotiationLogger;
800

801
    private ProtocolNegotiationEvent pne;
802

803
    Http2UpgradeAndGrpcHandler(String authority, GrpcHttp2ConnectionHandler next) {
1✔
804
      this.authority = checkNotNull(authority, "authority");
1✔
805
      this.next = checkNotNull(next, "next");
1✔
806
      this.negotiationLogger = next.getNegotiationLogger();
1✔
807
    }
1✔
808

809
    @Override
810
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
811
      negotiationLogger.log(ChannelLogLevel.INFO, "Http2Upgrade started");
1✔
812
      HttpClientCodec httpClientCodec = new HttpClientCodec();
1✔
813
      ctx.pipeline().addBefore(ctx.name(), null, httpClientCodec);
1✔
814

815
      Http2ClientUpgradeCodec upgradeCodec = new Http2ClientUpgradeCodec(next);
1✔
816
      HttpClientUpgradeHandler upgrader =
1✔
817
          new HttpClientUpgradeHandler(httpClientCodec, upgradeCodec, /*maxContentLength=*/ 1000);
818
      ctx.pipeline().addBefore(ctx.name(), null, upgrader);
1✔
819

820
      // Trigger the HTTP/1.1 plaintext upgrade protocol by issuing an HTTP request
821
      // which causes the upgrade headers to be added
822
      DefaultHttpRequest upgradeTrigger =
1✔
823
          new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
824
      upgradeTrigger.headers().add(HttpHeaderNames.HOST, authority);
1✔
825
      ctx.writeAndFlush(upgradeTrigger).addListener(
1✔
826
          ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
827
      super.handlerAdded(ctx);
1✔
828
    }
1✔
829

830
    @Override
831
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
832
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
833
        checkState(pne == null, "negotiation already started");
1✔
834
        pne = (ProtocolNegotiationEvent) evt;
1✔
835
      } else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_SUCCESSFUL) {
1✔
836
        checkState(pne != null, "negotiation not yet complete");
1✔
837
        negotiationLogger.log(ChannelLogLevel.INFO, "Http2Upgrade finished");
1✔
838
        ctx.pipeline().remove(ctx.name());
1✔
839
        next.handleProtocolNegotiationCompleted(pne.getAttributes(), pne.getSecurity());
1✔
840
      } else if (evt == HttpClientUpgradeHandler.UpgradeEvent.UPGRADE_REJECTED) {
1✔
841
        ctx.fireExceptionCaught(unavailableException("HTTP/2 upgrade rejected"));
×
842
      } else {
843
        super.userEventTriggered(ctx, evt);
1✔
844
      }
845
    }
1✔
846
  }
847

848
  /**
849
   * Returns a {@link ChannelHandler} that ensures that the {@code handler} is added to the
850
   * pipeline writes to the {@link io.netty.channel.Channel} may happen immediately, even before it
851
   * is active.
852
   */
853
  public static ProtocolNegotiator plaintext() {
854
    return new PlaintextProtocolNegotiator();
1✔
855
  }
856

857
  public static ProtocolNegotiator.ClientFactory plaintextClientFactory() {
858
    return new PlaintextProtocolNegotiatorClientFactory();
1✔
859
  }
860

861
  @VisibleForTesting
862
  static final class PlaintextProtocolNegotiatorClientFactory
1✔
863
      implements ProtocolNegotiator.ClientFactory {
864
    @Override public ProtocolNegotiator newNegotiator() {
865
      return plaintext();
1✔
866
    }
867

868
    @Override public int getDefaultPort() {
869
      return GrpcUtil.DEFAULT_PORT_PLAINTEXT;
1✔
870
    }
871
  }
872

873
  private static RuntimeException unavailableException(String msg) {
874
    return Status.UNAVAILABLE.withDescription(msg).asRuntimeException();
1✔
875
  }
876

877
  @VisibleForTesting
878
  static void logSslEngineDetails(Level level, ChannelHandlerContext ctx, String msg,
879
      @Nullable Throwable t) {
880
    if (!log.isLoggable(level)) {
1✔
881
      return;
1✔
882
    }
883

884
    SslHandler sslHandler = ctx.pipeline().get(SslHandler.class);
1✔
885
    SSLEngine engine = sslHandler.engine();
1✔
886

887
    StringBuilder builder = new StringBuilder(msg);
1✔
888
    builder.append("\nSSLEngine Details: [\n");
1✔
889
    if (engine instanceof OpenSslEngine) {
1✔
890
      builder.append("    OpenSSL, ");
1✔
891
      builder.append("Version: 0x").append(Integer.toHexString(OpenSsl.version()));
1✔
892
      builder.append(" (").append(OpenSsl.versionString()).append("), ");
1✔
893
      builder.append("ALPN supported: ").append(SslProvider.isAlpnSupported(SslProvider.OPENSSL));
1✔
894
    } else if (JettyTlsUtil.isJettyAlpnConfigured()) {
×
895
      builder.append("    Jetty ALPN");
×
896
    } else if (JettyTlsUtil.isJettyNpnConfigured()) {
×
897
      builder.append("    Jetty NPN");
×
898
    } else if (JettyTlsUtil.isJava9AlpnAvailable()) {
×
899
      builder.append("    JDK9 ALPN");
×
900
    }
901
    builder.append("\n    TLS Protocol: ");
1✔
902
    builder.append(engine.getSession().getProtocol());
1✔
903
    builder.append("\n    Application Protocol: ");
1✔
904
    builder.append(sslHandler.applicationProtocol());
1✔
905
    builder.append("\n    Need Client Auth: " );
1✔
906
    builder.append(engine.getNeedClientAuth());
1✔
907
    builder.append("\n    Want Client Auth: ");
1✔
908
    builder.append(engine.getWantClientAuth());
1✔
909
    builder.append("\n    Supported protocols=");
1✔
910
    builder.append(Arrays.toString(engine.getSupportedProtocols()));
1✔
911
    builder.append("\n    Enabled protocols=");
1✔
912
    builder.append(Arrays.toString(engine.getEnabledProtocols()));
1✔
913
    builder.append("\n    Supported ciphers=");
1✔
914
    builder.append(Arrays.toString(engine.getSupportedCipherSuites()));
1✔
915
    builder.append("\n    Enabled ciphers=");
1✔
916
    builder.append(Arrays.toString(engine.getEnabledCipherSuites()));
1✔
917
    builder.append("\n]");
1✔
918

919
    log.log(level, builder.toString(), t);
1✔
920
  }
1✔
921

922
  /**
923
   * Adapts a {@link ProtocolNegotiationEvent} to the {@link GrpcHttp2ConnectionHandler}.
924
   */
925
  static final class GrpcNegotiationHandler extends ChannelInboundHandlerAdapter {
926
    private final GrpcHttp2ConnectionHandler next;
927

928
    public GrpcNegotiationHandler(GrpcHttp2ConnectionHandler next) {
1✔
929
      this.next = checkNotNull(next, "next");
1✔
930
    }
1✔
931

932
    @Override
933
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
934
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
935
        ProtocolNegotiationEvent protocolNegotiationEvent = (ProtocolNegotiationEvent) evt;
1✔
936
        ctx.pipeline().replace(ctx.name(), null, next);
1✔
937
        next.handleProtocolNegotiationCompleted(
1✔
938
            protocolNegotiationEvent.getAttributes(), protocolNegotiationEvent.getSecurity());
1✔
939
      } else {
1✔
940
        super.userEventTriggered(ctx, evt);
×
941
      }
942
    }
1✔
943
  }
944

945
  /*
946
   * Common {@link ProtocolNegotiator}s used by gRPC.  Protocol negotiation follows a pattern to
947
   * simplify the pipeline.   The pipeline should look like:
948
   *
949
   * 1.  {@link ProtocolNegotiator#newHandler() PN.H}, created.
950
   * 2.  [Tail], {@link WriteBufferingAndExceptionHandler WBAEH}, [Head]
951
   * 3.  [Tail], WBAEH, PN.H, [Head]
952
   *
953
   * <p>Typically, PN.H with be an instance of {@link InitHandler IH}, which is a trivial handler
954
   * that can return the {@code scheme()} of the negotiation.  IH, and each handler after,
955
   * replaces itself with a "next" handler once its part of negotiation is complete.  This keeps
956
   * the pipeline small, and limits the interaction between handlers.
957
   *
958
   * <p>Additionally, each handler may fire a {@link ProtocolNegotiationEvent PNE} just after
959
   * replacing itself.  Handlers should capture user events of type PNE, and re-trigger the events
960
   * once that handler's part of negotiation is complete.  This can be seen in the
961
   * {@link WaitUntilActiveHandler WUAH}, which waits until the channel is active.  Once active, it
962
   * replaces itself with the next handler, and fires a PNE containing the addresses.  Continuing
963
   * with IH and WUAH:
964
   *
965
   * 3.  [Tail], WBAEH, IH, [Head]
966
   * 4.  [Tail], WBAEH, WUAH, [Head]
967
   * 5.  [Tail], WBAEH, {@link GrpcNegotiationHandler}, [Head]
968
   * 6a. [Tail], WBAEH, {@link GrpcHttp2ConnectionHandler GHCH}, [Head]
969
   * 6b. [Tail], GHCH, [Head]
970
   */
971

972
  /**
973
   * A negotiator that only does plain text.
974
   */
975
  static final class PlaintextProtocolNegotiator implements ProtocolNegotiator {
1✔
976

977
    @Override
978
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
979
      ChannelHandler grpcNegotiationHandler = new GrpcNegotiationHandler(grpcHandler);
1✔
980
      ChannelHandler activeHandler = new WaitUntilActiveHandler(grpcNegotiationHandler,
1✔
981
          grpcHandler.getNegotiationLogger());
1✔
982
      return activeHandler;
1✔
983
    }
984

985
    @Override
986
    public void close() {}
1✔
987

988
    @Override
989
    public AsciiString scheme() {
990
      return Utils.HTTP;
1✔
991
    }
992
  }
993

994
  /**
995
   * Waits for the channel to be active, and then installs the next Handler.  Using this allows
996
   * subsequent handlers to assume the channel is active and ready to send.  Additionally, this a
997
   * {@link ProtocolNegotiationEvent}, with the connection addresses.
998
   */
999
  static final class WaitUntilActiveHandler extends ProtocolNegotiationHandler {
1000

1001
    boolean protocolNegotiationEventReceived;
1002

1003
    WaitUntilActiveHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1004
      super(next, negotiationLogger);
1✔
1005
    }
1✔
1006

1007
    @Override
1008
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
1009
      if (protocolNegotiationEventReceived) {
1✔
1010
        replaceOnActive(ctx);
1✔
1011
        fireProtocolNegotiationEvent(ctx);
1✔
1012
      }
1013
      // Still propagate channelActive to the new handler.
1014
      super.channelActive(ctx);
1✔
1015
    }
1✔
1016

1017
    @Override
1018
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1019
      protocolNegotiationEventReceived = true;
1✔
1020
      if (ctx.channel().isActive()) {
1✔
1021
        replaceOnActive(ctx);
1✔
1022
        fireProtocolNegotiationEvent(ctx);
1✔
1023
      }
1024
    }
1✔
1025

1026
    private void replaceOnActive(ChannelHandlerContext ctx) {
1027
      ProtocolNegotiationEvent existingPne = getProtocolNegotiationEvent();
1✔
1028
      Attributes attrs = existingPne.getAttributes().toBuilder()
1✔
1029
          .set(Grpc.TRANSPORT_ATTR_LOCAL_ADDR, ctx.channel().localAddress())
1✔
1030
          .set(Grpc.TRANSPORT_ATTR_REMOTE_ADDR, ctx.channel().remoteAddress())
1✔
1031
          // Later handlers are expected to overwrite this.
1032
          .set(GrpcAttributes.ATTR_SECURITY_LEVEL, SecurityLevel.NONE)
1✔
1033
          .build();
1✔
1034
      replaceProtocolNegotiationEvent(existingPne.withAttributes(attrs));
1✔
1035
    }
1✔
1036
  }
1037

1038
  /**
1039
   * ProtocolNegotiationHandler is a convenience handler that makes it easy to follow the rules for
1040
   * protocol negotiation.  Handlers should strongly consider extending this handler.
1041
   */
1042
  static class ProtocolNegotiationHandler extends ChannelDuplexHandler {
1043

1044
    private final ChannelHandler next;
1045
    private final String negotiatorName;
1046
    private ProtocolNegotiationEvent pne;
1047
    private final ChannelLogger negotiationLogger;
1048

1049
    protected ProtocolNegotiationHandler(ChannelHandler next, String negotiatorName,
1050
        ChannelLogger negotiationLogger) {
×
1051
      this.next = checkNotNull(next, "next");
×
1052
      this.negotiatorName = negotiatorName;
×
1053
      this.negotiationLogger = checkNotNull(negotiationLogger, "negotiationLogger");
×
1054
    }
×
1055

1056
    protected ProtocolNegotiationHandler(ChannelHandler next, ChannelLogger negotiationLogger) {
1✔
1057
      this.next = checkNotNull(next, "next");
1✔
1058
      this.negotiatorName = getClass().getSimpleName().replace("Handler", "");
1✔
1059
      this.negotiationLogger = checkNotNull(negotiationLogger, "negotiationLogger");
1✔
1060
    }
1✔
1061

1062
    @Override
1063
    public final void handlerAdded(ChannelHandlerContext ctx) throws Exception {
1064
      negotiationLogger.log(ChannelLogLevel.DEBUG, "{0} started", negotiatorName);
1✔
1065
      handlerAdded0(ctx);
1✔
1066
    }
1✔
1067

1068
    @ForOverride
1069
    protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception {
1070
      super.handlerAdded(ctx);
1✔
1071
    }
1✔
1072

1073
    @Override
1074
    public final void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
1075
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
1076
        checkState(pne == null, "pre-existing negotiation: %s < %s", pne, evt);
1✔
1077
        pne = (ProtocolNegotiationEvent) evt;
1✔
1078
        protocolNegotiationEventTriggered(ctx);
1✔
1079
      } else {
1080
        userEventTriggered0(ctx, evt);
1✔
1081
      }
1082
    }
1✔
1083

1084
    protected void userEventTriggered0(ChannelHandlerContext ctx, Object evt) throws Exception {
1085
      super.userEventTriggered(ctx, evt);
1✔
1086
    }
1✔
1087

1088
    @ForOverride
1089
    protected void protocolNegotiationEventTriggered(ChannelHandlerContext ctx) {
1090
      // no-op
1091
    }
1✔
1092

1093
    protected final ProtocolNegotiationEvent getProtocolNegotiationEvent() {
1094
      checkState(pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1095
      return pne;
1✔
1096
    }
1097

1098
    protected final void replaceProtocolNegotiationEvent(ProtocolNegotiationEvent pne) {
1099
      checkState(this.pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1100
      this.pne = checkNotNull(pne);
1✔
1101
    }
1✔
1102

1103
    protected final void fireProtocolNegotiationEvent(ChannelHandlerContext ctx) {
1104
      checkState(pne != null, "previous protocol negotiation event hasn't triggered");
1✔
1105
      negotiationLogger.log(ChannelLogLevel.INFO, "{0} completed", negotiatorName);
1✔
1106
      ctx.pipeline().replace(ctx.name(), /* newName= */ null, next);
1✔
1107
      ctx.fireUserEventTriggered(pne);
1✔
1108
    }
1✔
1109
  }
1110
}
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