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

grpc / grpc-java / #19618

04 Jan 2025 12:01AM UTC coverage: 88.555% (+0.005%) from 88.55%
#19618

push

github

ejona86
xds: Avoid depending on io.grpc.xds.Internal* classes

Internal* classes should generally be accessors that are used outside of
the package/project. Only one attribute was used outside of xds, so
leave only that one attribute in InternalXdsAttributes. One attribute
was used by the internal.security package, so move the definition to the
same package to reduce the circular dependencies.

33620 of 37965 relevant lines covered (88.56%)

0.89 hits per line

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

89.23
/../xds/src/main/java/io/grpc/xds/internal/security/SecurityProtocolNegotiators.java
1
/*
2
 * Copyright 2019 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.xds.internal.security;
18

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

21
import com.google.common.annotations.VisibleForTesting;
22
import io.grpc.Attributes;
23
import io.grpc.Grpc;
24
import io.grpc.internal.GrpcUtil;
25
import io.grpc.internal.ObjectPool;
26
import io.grpc.netty.GrpcHttp2ConnectionHandler;
27
import io.grpc.netty.InternalProtocolNegotiationEvent;
28
import io.grpc.netty.InternalProtocolNegotiator;
29
import io.grpc.netty.InternalProtocolNegotiator.ProtocolNegotiator;
30
import io.grpc.netty.InternalProtocolNegotiators;
31
import io.grpc.netty.ProtocolNegotiationEvent;
32
import io.netty.channel.ChannelHandler;
33
import io.netty.channel.ChannelHandlerAdapter;
34
import io.netty.channel.ChannelHandlerContext;
35
import io.netty.channel.ChannelInboundHandlerAdapter;
36
import io.netty.handler.ssl.SslContext;
37
import io.netty.util.AsciiString;
38
import java.security.cert.CertStoreException;
39
import java.util.ArrayList;
40
import java.util.List;
41
import java.util.concurrent.Executor;
42
import java.util.logging.Level;
43
import java.util.logging.Logger;
44
import javax.annotation.Nullable;
45

46
/**
47
 * Provides client and server side gRPC {@link ProtocolNegotiator}s to provide the SSL
48
 * context.
49
 */
50
@VisibleForTesting
51
public final class SecurityProtocolNegotiators {
52

53
  // Prevent instantiation.
54
  private SecurityProtocolNegotiators() {
55
  }
56

57
  private static final Logger logger
1✔
58
      = Logger.getLogger(SecurityProtocolNegotiators.class.getName());
1✔
59

60
  private static final AsciiString SCHEME = AsciiString.of("http");
1✔
61

62
  public static final Attributes.Key<SslContextProviderSupplier>
63
          ATTR_SERVER_SSL_CONTEXT_PROVIDER_SUPPLIER =
1✔
64
          Attributes.Key.create("io.grpc.xds.internal.security.server.sslContextProviderSupplier");
1✔
65

66
  /** Attribute key for SslContextProviderSupplier (used from client) for a subchannel. */
67
  @Grpc.TransportAttr
68
  public static final Attributes.Key<SslContextProviderSupplier>
69
      ATTR_SSL_CONTEXT_PROVIDER_SUPPLIER =
1✔
70
          Attributes.Key.create("io.grpc.xds.internal.security.SslContextProviderSupplier");
1✔
71

72
  /**
73
   * Returns a {@link InternalProtocolNegotiator.ClientFactory}.
74
   *
75
   * @param fallbackNegotiator protocol negotiator to use as fallback.
76
   */
77
  public static InternalProtocolNegotiator.ClientFactory clientProtocolNegotiatorFactory(
78
      @Nullable InternalProtocolNegotiator.ClientFactory fallbackNegotiator) {
79
    return new ClientFactory(fallbackNegotiator);
1✔
80
  }
81

82
  public static InternalProtocolNegotiator.ServerFactory serverProtocolNegotiatorFactory(
83
      @Nullable InternalProtocolNegotiator.ServerFactory fallbackNegotiator) {
84
    return new ServerFactory(fallbackNegotiator);
1✔
85
  }
86

87
  private static final class ServerFactory implements InternalProtocolNegotiator.ServerFactory {
88

89
    private final InternalProtocolNegotiator.ServerFactory fallbackProtocolNegotiator;
90

91
    private ServerFactory(InternalProtocolNegotiator.ServerFactory fallbackNegotiator) {
1✔
92
      this.fallbackProtocolNegotiator = fallbackNegotiator;
1✔
93
    }
1✔
94

95
    @Override
96
    public ProtocolNegotiator newNegotiator(ObjectPool<? extends Executor> offloadExecutorPool) {
97
      return new ServerSecurityProtocolNegotiator(
1✔
98
          fallbackProtocolNegotiator.newNegotiator(offloadExecutorPool));
1✔
99
    }
100
  }
101

102
  private static final class ClientFactory implements InternalProtocolNegotiator.ClientFactory {
103

104
    private final InternalProtocolNegotiator.ClientFactory fallbackProtocolNegotiator;
105

106
    private ClientFactory(InternalProtocolNegotiator.ClientFactory fallbackNegotiator) {
1✔
107
      this.fallbackProtocolNegotiator = fallbackNegotiator;
1✔
108
    }
1✔
109

110
    @Override
111
    public ProtocolNegotiator newNegotiator() {
112
      return new ClientSecurityProtocolNegotiator(fallbackProtocolNegotiator.newNegotiator());
1✔
113
    }
114

115
    @Override
116
    public int getDefaultPort() {
117
      return GrpcUtil.DEFAULT_PORT_SSL;
1✔
118
    }
119
  }
120

121
  @VisibleForTesting
122
  static final class ClientSecurityProtocolNegotiator implements ProtocolNegotiator {
123

124
    @Nullable private final ProtocolNegotiator fallbackProtocolNegotiator;
125

126
    ClientSecurityProtocolNegotiator(@Nullable ProtocolNegotiator fallbackProtocolNegotiator) {
1✔
127
      this.fallbackProtocolNegotiator = fallbackProtocolNegotiator;
1✔
128
    }
1✔
129

130
    @Override
131
    public AsciiString scheme() {
132
      return SCHEME;
1✔
133
    }
134

135
    @Override
136
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
137
      // check if SslContextProviderSupplier was passed via attributes
138
      SslContextProviderSupplier localSslContextProviderSupplier =
1✔
139
          grpcHandler.getEagAttributes().get(ATTR_SSL_CONTEXT_PROVIDER_SUPPLIER);
1✔
140
      if (localSslContextProviderSupplier == null) {
1✔
141
        checkNotNull(
1✔
142
            fallbackProtocolNegotiator, "No TLS config and no fallbackProtocolNegotiator!");
143
        return fallbackProtocolNegotiator.newHandler(grpcHandler);
1✔
144
      }
145
      return new ClientSecurityHandler(grpcHandler, localSslContextProviderSupplier);
1✔
146
    }
147

148
    @Override
149
    public void close() {}
1✔
150
  }
151

152
  private static class BufferReadsHandler extends ChannelInboundHandlerAdapter {
1✔
153
    private final List<Object> reads = new ArrayList<>();
1✔
154
    private boolean readComplete;
155

156
    @Override
157
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
158
      reads.add(msg);
1✔
159
    }
1✔
160

161
    @Override
162
    public void channelReadComplete(ChannelHandlerContext ctx) {
163
      readComplete = true;
1✔
164
    }
1✔
165

166
    @Override
167
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
168
      for (Object msg : reads) {
1✔
169
        super.channelRead(ctx, msg);
1✔
170
      }
1✔
171
      if (readComplete) {
1✔
172
        super.channelReadComplete(ctx);
1✔
173
      }
174
    }
1✔
175

176
    @Override
177
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
178
      logger.log(Level.SEVERE, "exceptionCaught", cause);
×
179
      ctx.fireExceptionCaught(cause);
×
180
    }
×
181
  }
182

183
  @VisibleForTesting
184
  static final class ClientSecurityHandler
185
      extends InternalProtocolNegotiators.ProtocolNegotiationHandler {
186
    private final GrpcHttp2ConnectionHandler grpcHandler;
187
    private final SslContextProviderSupplier sslContextProviderSupplier;
188

189
    ClientSecurityHandler(
190
        GrpcHttp2ConnectionHandler grpcHandler,
191
        SslContextProviderSupplier sslContextProviderSupplier) {
192
      super(
1✔
193
          // superclass (InternalProtocolNegotiators.ProtocolNegotiationHandler) expects 'next'
194
          // handler but we don't have a next handler _yet_. So we "disable" superclass's behavior
195
          // here and then manually add 'next' when we call fireProtocolNegotiationEvent()
196
          new ChannelHandlerAdapter() {
1✔
197
            @Override
198
            public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
199
              ctx.pipeline().remove(this);
1✔
200
            }
1✔
201
          }, grpcHandler.getNegotiationLogger());
1✔
202
      checkNotNull(grpcHandler, "grpcHandler");
1✔
203
      this.grpcHandler = grpcHandler;
1✔
204
      this.sslContextProviderSupplier = sslContextProviderSupplier;
1✔
205
    }
1✔
206

207
    @Override
208
    protected void handlerAdded0(final ChannelHandlerContext ctx) {
209
      final BufferReadsHandler bufferReads = new BufferReadsHandler();
1✔
210
      ctx.pipeline().addBefore(ctx.name(), null, bufferReads);
1✔
211

212
      sslContextProviderSupplier.updateSslContext(
1✔
213
          new SslContextProvider.Callback(ctx.executor()) {
1✔
214

215
            @Override
216
            public void updateSslContext(SslContext sslContext) {
217
              if (ctx.isRemoved()) {
1✔
218
                return;
1✔
219
              }
220
              logger.log(
1✔
221
                  Level.FINEST,
222
                  "ClientSecurityHandler.updateSslContext authority={0}, ctx.name={1}",
223
                  new Object[]{grpcHandler.getAuthority(), ctx.name()});
1✔
224
              ChannelHandler handler =
1✔
225
                  InternalProtocolNegotiators.tls(sslContext).newHandler(grpcHandler);
1✔
226

227
              // Delegate rest of handshake to TLS handler
228
              ctx.pipeline().addAfter(ctx.name(), null, handler);
1✔
229
              fireProtocolNegotiationEvent(ctx);
1✔
230
              ctx.pipeline().remove(bufferReads);
1✔
231
            }
1✔
232

233
            @Override
234
            public void onException(Throwable throwable) {
235
              ctx.fireExceptionCaught(throwable);
×
236
            }
×
237
          }
238
      );
239
    }
1✔
240

241
    @Override
242
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
243
        throws Exception {
244
      logger.log(Level.SEVERE, "exceptionCaught", cause);
×
245
      ctx.fireExceptionCaught(cause);
×
246
    }
×
247
  }
248

249
  private static final class ServerSecurityProtocolNegotiator implements ProtocolNegotiator {
250

251
    @Nullable private final ProtocolNegotiator fallbackProtocolNegotiator;
252

253
    /** Constructor. */
254
    @VisibleForTesting
255
    public ServerSecurityProtocolNegotiator(
256
        @Nullable ProtocolNegotiator fallbackProtocolNegotiator) {
1✔
257
      this.fallbackProtocolNegotiator = fallbackProtocolNegotiator;
1✔
258
    }
1✔
259

260
    @Override
261
    public AsciiString scheme() {
262
      return SCHEME;
×
263
    }
264

265
    @Override
266
    public ChannelHandler newHandler(GrpcHttp2ConnectionHandler grpcHandler) {
267
      return new HandlerPickerHandler(grpcHandler, fallbackProtocolNegotiator);
1✔
268
    }
269

270
    @Override
271
    public void close() {}
×
272
  }
273

274
  @VisibleForTesting
275
  static final class HandlerPickerHandler
276
      extends ChannelInboundHandlerAdapter {
277
    private final GrpcHttp2ConnectionHandler grpcHandler;
278
    @Nullable private final ProtocolNegotiator fallbackProtocolNegotiator;
279

280
    HandlerPickerHandler(
281
        GrpcHttp2ConnectionHandler grpcHandler,
282
        @Nullable ProtocolNegotiator fallbackProtocolNegotiator) {
1✔
283
      this.grpcHandler = checkNotNull(grpcHandler, "grpcHandler");
1✔
284
      this.fallbackProtocolNegotiator = fallbackProtocolNegotiator;
1✔
285
    }
1✔
286

287
    @Override
288
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
289
      if (evt instanceof ProtocolNegotiationEvent) {
1✔
290
        ProtocolNegotiationEvent pne = (ProtocolNegotiationEvent)evt;
1✔
291
        SslContextProviderSupplier sslContextProviderSupplier = InternalProtocolNegotiationEvent
1✔
292
                .getAttributes(pne).get(ATTR_SERVER_SSL_CONTEXT_PROVIDER_SUPPLIER);
1✔
293
        if (sslContextProviderSupplier == null) {
1✔
294
          logger.log(Level.FINE, "No sslContextProviderSupplier found in filterChainMatch "
1✔
295
              + "for connection from {0} to {1}",
296
              new Object[]{ctx.channel().remoteAddress(), ctx.channel().localAddress()});
1✔
297
          if (fallbackProtocolNegotiator == null) {
1✔
298
            ctx.fireExceptionCaught(new CertStoreException("No certificate source found!"));
1✔
299
            return;
1✔
300
          }
301
          logger.log(Level.FINE, "Using fallback credentials for connection from {0} to {1}",
1✔
302
              new Object[]{ctx.channel().remoteAddress(), ctx.channel().localAddress()});
1✔
303
          ctx.pipeline()
1✔
304
              .replace(
1✔
305
                  this,
306
                  null,
307
                  fallbackProtocolNegotiator.newHandler(grpcHandler));
1✔
308
          ctx.fireUserEventTriggered(pne);
1✔
309
          return;
1✔
310
        } else {
311
          ctx.pipeline()
1✔
312
              .replace(
1✔
313
                  this,
314
                  null,
315
                  new ServerSecurityHandler(
316
                      grpcHandler, sslContextProviderSupplier));
317
          ctx.fireUserEventTriggered(pne);
1✔
318
          return;
1✔
319
        }
320
      } else {
321
        super.userEventTriggered(ctx, evt);
×
322
      }
323
    }
×
324
  }
325

326
  @VisibleForTesting
327
  static final class ServerSecurityHandler
328
          extends InternalProtocolNegotiators.ProtocolNegotiationHandler {
329
    private final GrpcHttp2ConnectionHandler grpcHandler;
330
    private final SslContextProviderSupplier sslContextProviderSupplier;
331

332
    ServerSecurityHandler(
333
            GrpcHttp2ConnectionHandler grpcHandler,
334
            SslContextProviderSupplier sslContextProviderSupplier) {
335
      super(
1✔
336
          // superclass (InternalProtocolNegotiators.ProtocolNegotiationHandler) expects 'next'
337
          // handler but we don't have a next handler _yet_. So we "disable" superclass's behavior
338
          // here and then manually add 'next' when we call fireProtocolNegotiationEvent()
339
          new ChannelHandlerAdapter() {
1✔
340
            @Override
341
            public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
342
              ctx.pipeline().remove(this);
1✔
343
            }
1✔
344
          }, grpcHandler.getNegotiationLogger());
1✔
345
      checkNotNull(grpcHandler, "grpcHandler");
1✔
346
      this.grpcHandler = grpcHandler;
1✔
347
      this.sslContextProviderSupplier = sslContextProviderSupplier;
1✔
348
    }
1✔
349

350
    @Override
351
    protected void handlerAdded0(final ChannelHandlerContext ctx) {
352
      final BufferReadsHandler bufferReads = new BufferReadsHandler();
1✔
353
      ctx.pipeline().addBefore(ctx.name(), null, bufferReads);
1✔
354

355
      sslContextProviderSupplier.updateSslContext(
1✔
356
          new SslContextProvider.Callback(ctx.executor()) {
1✔
357

358
            @Override
359
            public void updateSslContext(SslContext sslContext) {
360
              ChannelHandler handler =
1✔
361
                  InternalProtocolNegotiators.serverTls(sslContext).newHandler(grpcHandler);
1✔
362

363
              // Delegate rest of handshake to TLS handler
364
              if (!ctx.isRemoved()) {
1✔
365
                ctx.pipeline().addAfter(ctx.name(), null, handler);
1✔
366
                fireProtocolNegotiationEvent(ctx);
1✔
367
                ctx.pipeline().remove(bufferReads);
1✔
368
              }
369
            }
1✔
370

371
            @Override
372
            public void onException(Throwable throwable) {
373
              ctx.fireExceptionCaught(throwable);
×
374
            }
×
375
          }
376
      );
377
    }
1✔
378
  }
379
}
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