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 java.lang.reflect.Method;
20 import java.security.AccessController;
21 import java.security.PrivilegedExceptionAction;
22 import javax.net.ssl.SSLContext;
23 import javax.net.ssl.SSLEngine;
24 
25 /**
26  * Utility class for determining support for Jetty TLS ALPN/NPN.
27  */
28 final class JettyTlsUtil {
JettyTlsUtil()29   private JettyTlsUtil() {
30   }
31 
32   private static Throwable jettyAlpnUnavailabilityCause;
33   private static Throwable jettyNpnUnavailabilityCause;
34 
35   private static class Java9AlpnUnavailabilityCauseHolder {
36 
37     static final Throwable cause = checkAlpnAvailability();
38 
checkAlpnAvailability()39     static Throwable checkAlpnAvailability() {
40       try {
41         SSLContext context = SSLContext.getInstance("TLS");
42         context.init(null, null, null);
43         SSLEngine engine = context.createSSLEngine();
44         Method getApplicationProtocol =
45             AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
46               @Override
47               public Method run() throws Exception {
48                 return SSLEngine.class.getMethod("getApplicationProtocol");
49               }
50             });
51         getApplicationProtocol.invoke(engine);
52         return null;
53       } catch (Throwable t) {
54         return t;
55       }
56     }
57   }
58 
59   /**
60    * Indicates whether or not the Jetty ALPN jar is installed in the boot classloader.
61    */
isJettyAlpnConfigured()62   static synchronized boolean isJettyAlpnConfigured() {
63     try {
64       Class.forName("org.eclipse.jetty.alpn.ALPN", true, null);
65       return true;
66     } catch (ClassNotFoundException e) {
67       jettyAlpnUnavailabilityCause = e;
68       return false;
69     }
70   }
71 
getJettyAlpnUnavailabilityCause()72   static synchronized Throwable getJettyAlpnUnavailabilityCause() {
73     // This case should be unlikely
74     if (jettyAlpnUnavailabilityCause == null) {
75       boolean discard = isJettyAlpnConfigured();
76     }
77     return jettyAlpnUnavailabilityCause;
78   }
79 
80   /**
81    * Indicates whether or not the Jetty NPN jar is installed in the boot classloader.
82    */
isJettyNpnConfigured()83   static synchronized boolean isJettyNpnConfigured() {
84     try {
85       Class.forName("org.eclipse.jetty.npn.NextProtoNego", true, null);
86       return true;
87     } catch (ClassNotFoundException e) {
88       jettyNpnUnavailabilityCause = e;
89       return false;
90     }
91   }
92 
getJettyNpnUnavailabilityCause()93   static synchronized Throwable getJettyNpnUnavailabilityCause() {
94     // This case should be unlikely
95     if (jettyNpnUnavailabilityCause == null) {
96       boolean discard = isJettyNpnConfigured();
97     }
98     return jettyNpnUnavailabilityCause;
99   }
100 
101   /**
102    * Indicates whether Java 9 ALPN is available.
103    */
isJava9AlpnAvailable()104   static boolean isJava9AlpnAvailable() {
105     return getJava9AlpnUnavailabilityCause() == null;
106   }
107 
getJava9AlpnUnavailabilityCause()108   static Throwable getJava9AlpnUnavailabilityCause() {
109     return Java9AlpnUnavailabilityCauseHolder.cause;
110   }
111 }
112