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.netty5.handler.ssl;
17  
18  import io.netty5.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelectionListener;
19  import io.netty5.handler.ssl.JdkApplicationProtocolNegotiator.ProtocolSelector;
20  import io.netty5.util.internal.PlatformDependent;
21  import org.eclipse.jetty.npn.NextProtoNego;
22  import org.eclipse.jetty.npn.NextProtoNego.ClientProvider;
23  import org.eclipse.jetty.npn.NextProtoNego.ServerProvider;
24  
25  import javax.net.ssl.SSLEngine;
26  import javax.net.ssl.SSLException;
27  import java.util.LinkedHashSet;
28  import java.util.List;
29  
30  import static java.util.Objects.requireNonNull;
31  
32  final class JettyNpnSslEngine extends JdkSslEngine {
33      private static boolean available;
34  
35      static boolean isAvailable() {
36          updateAvailability();
37          return available;
38      }
39  
40      private static void updateAvailability() {
41          if (available) {
42              return;
43          }
44          try {
45              // Always use bootstrap class loader.
46              Class.forName("sun.security.ssl.NextProtoNegoExtension", true, null);
47              available = true;
48          } catch (Exception ignore) {
49              // npn-boot was not loaded.
50          }
51      }
52  
53      JettyNpnSslEngine(SSLEngine engine, final JdkApplicationProtocolNegotiator applicationNegotiator, boolean server) {
54          super(engine);
55          requireNonNull(applicationNegotiator, "applicationNegotiator");
56  
57          if (server) {
58              final ProtocolSelectionListener protocolListener = requireNonNull(applicationNegotiator
59                      .protocolListenerFactory().newListener(this, applicationNegotiator.protocols()),
60                      "protocolListener");
61              NextProtoNego.put(engine, new ServerProvider() {
62                  @Override
63                  public void unsupported() {
64                      protocolListener.unsupported();
65                  }
66  
67                  @Override
68                  public List<String> protocols() {
69                      return applicationNegotiator.protocols();
70                  }
71  
72                  @Override
73                  public void protocolSelected(String protocol) {
74                      try {
75                          protocolListener.selected(protocol);
76                      } catch (Throwable t) {
77                          PlatformDependent.throwException(t);
78                      }
79                  }
80              });
81          } else {
82              final ProtocolSelector protocolSelector = requireNonNull(applicationNegotiator.protocolSelectorFactory()
83                      .newSelector(this, new LinkedHashSet<>(applicationNegotiator.protocols())),
84                      "protocolSelector");
85              NextProtoNego.put(engine, new ClientProvider() {
86                  @Override
87                  public boolean supports() {
88                      return true;
89                  }
90  
91                  @Override
92                  public void unsupported() {
93                      protocolSelector.unsupported();
94                  }
95  
96                  @Override
97                  public String selectProtocol(List<String> protocols) {
98                      try {
99                          return protocolSelector.select(protocols);
100                     } catch (Throwable t) {
101                         PlatformDependent.throwException(t);
102                         return null;
103                     }
104                 }
105             });
106         }
107     }
108 
109     @Override
110     public void closeInbound() throws SSLException {
111         NextProtoNego.remove(getWrappedEngine());
112         super.closeInbound();
113     }
114 
115     @Override
116     public void closeOutbound() {
117         NextProtoNego.remove(getWrappedEngine());
118         super.closeOutbound();
119     }
120 }