View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.ssl;
17  
18  import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener;
19  import io.netty.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector;
20  import io.netty.util.internal.PlatformDependent;
21  import org.eclipse.jetty.alpn.ALPN;
22  
23  import java.util.LinkedHashSet;
24  import java.util.List;
25  import javax.net.ssl.SSLEngine;
26  import javax.net.ssl.SSLException;
27  
28  import static io.netty.handler.ssl.SslUtils.toSSLHandshakeException;
29  import static io.netty.util.internal.ObjectUtil.checkNotNull;
30  
31  abstract class JettyAlpnSslEngine extends JdkSslEngine {
32      private static final boolean available = initAvailable();
33  
34      static boolean isAvailable() {
35          return available;
36      }
37  
38      private static boolean initAvailable() {
39          if (PlatformDependent.javaVersion() == 8) {
40              try {
41                  // Always use bootstrap class loader.
42                  Class.forName("sun.security.ssl.ALPNExtension", true, null);
43                  return true;
44              } catch (Throwable ignore) {
45                  // alpn-boot was not loaded.
46              }
47          }
48          return false;
49      }
50  
51      static JettyAlpnSslEngine newClientEngine(SSLEngine engine,
52              JdkApplicationProtocolNegotiator applicationNegotiator) {
53          return new ClientEngine(engine, applicationNegotiator);
54      }
55  
56      static JettyAlpnSslEngine newServerEngine(SSLEngine engine,
57              JdkApplicationProtocolNegotiator applicationNegotiator) {
58          return new ServerEngine(engine, applicationNegotiator);
59      }
60  
61      private JettyAlpnSslEngine(SSLEngine engine) {
62          super(engine);
63      }
64  
65      private static final class ClientEngine extends JettyAlpnSslEngine {
66          ClientEngine(SSLEngine engine, final JdkApplicationProtocolNegotiator applicationNegotiator) {
67              super(engine);
68              checkNotNull(applicationNegotiator, "applicationNegotiator");
69              final ProtocolSelectionListener protocolListener = checkNotNull(applicationNegotiator
70                              .protocolListenerFactory().newListener(this, applicationNegotiator.protocols()),
71                      "protocolListener");
72              ALPN.put(engine, new ALPN.ClientProvider() {
73                  @Override
74                  public List<String> protocols() {
75                      return applicationNegotiator.protocols();
76                  }
77  
78                  @Override
79                  public void selected(String protocol) throws SSLException {
80                      try {
81                          protocolListener.selected(protocol);
82                      } catch (Throwable t) {
83                          throw toSSLHandshakeException(t);
84                      }
85                  }
86  
87                  @Override
88                  public void unsupported() {
89                      protocolListener.unsupported();
90                  }
91              });
92          }
93  
94          @Override
95          public void closeInbound() throws SSLException {
96              try {
97                  ALPN.remove(getWrappedEngine());
98              } finally {
99                  super.closeInbound();
100             }
101         }
102 
103         @Override
104         public void closeOutbound() {
105             try {
106                 ALPN.remove(getWrappedEngine());
107             } finally {
108                 super.closeOutbound();
109             }
110         }
111     }
112 
113     private static final class ServerEngine extends JettyAlpnSslEngine {
114         ServerEngine(SSLEngine engine, final JdkApplicationProtocolNegotiator applicationNegotiator) {
115             super(engine);
116             checkNotNull(applicationNegotiator, "applicationNegotiator");
117             final ProtocolSelector protocolSelector = checkNotNull(applicationNegotiator.protocolSelectorFactory()
118                             .newSelector(this, new LinkedHashSet<String>(applicationNegotiator.protocols())),
119                     "protocolSelector");
120             ALPN.put(engine, new ALPN.ServerProvider() {
121                 @Override
122                 public String select(List<String> protocols) throws SSLException {
123                     try {
124                         return protocolSelector.select(protocols);
125                     } catch (Throwable t) {
126                         throw toSSLHandshakeException(t);
127                     }
128                 }
129 
130                 @Override
131                 public void unsupported() {
132                     protocolSelector.unsupported();
133                 }
134             });
135         }
136 
137         @Override
138         public void closeInbound() throws SSLException {
139             try {
140                 ALPN.remove(getWrappedEngine());
141             } finally {
142                 super.closeInbound();
143             }
144         }
145 
146         @Override
147         public void closeOutbound() {
148             try {
149                 ALPN.remove(getWrappedEngine());
150             } finally {
151                 super.closeOutbound();
152             }
153         }
154     }
155 }