1 /*
2 * Copyright (c) 2012 the original author or 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 org.eclipse.jetty.npn;
18
19 import java.util.Collections;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.WeakHashMap;
23 import javax.net.ssl.SSLEngine;
24 import javax.net.ssl.SSLSocket;
25
26 /**
27 * <p>{@link NextProtoNego} provides an API to applications that want to make use of the
28 * <a href="http://technotes.googlecode.com/git/nextprotoneg.html">Next Protocol Negotiation</a>.</p>
29 * <p>The NPN extension is only available when using the TLS protocol, therefore applications must
30 * ensure that the TLS protocol is used:</p>
31 * <pre>
32 * SSLContext context = SSLContext.getInstance("TLSv1");
33 * </pre>
34 * <p>Refer to the
35 * <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext">list
36 * of standard SSLContext protocol names</a> for further information on TLS protocol versions supported.</p>
37 * <p>Applications must register instances of either {@link SSLSocket} or {@link SSLEngine} with a
38 * {@link ClientProvider} or with a {@link ServerProvider}, depending whether they are on client or
39 * server side.</p>
40 * <p>The NPN implementation will invoke the provider callbacks to allow applications to interact
41 * with the negotiation of the next protocol.</p>
42 * <p>Client side typical usage:</p>
43 * <pre>
44 * SSLSocket sslSocket = ...;
45 * NextProtoNego.put(sslSocket, new NextProtoNego.ClientProvider()
46 * {
47 * @Override
48 * public boolean supports()
49 * {
50 * return true;
51 * }
52 *
53 * @Override
54 * public void unsupported()
55 * {
56 * }
57 *
58 * @Override
59 * public String selectProtocol(List<String> protocols)
60 * {
61 * return protocols.get(0);
62 * }
63 * });
64 * </pre>
65 * <p>Server side typical usage:</p>
66 * <pre>
67 * SSLSocket sslSocket = ...;
68 * NextProtoNego.put(sslSocket, new NextProtoNego.ServerProvider()
69 * {
70 * @Override
71 * public void unsupported()
72 * {
73 * }
74 *
75 * @Override
76 * public List<String> protocols()
77 * {
78 * return Arrays.asList("http/1.1");
79 * }
80 *
81 * @Override
82 * public void protocolSelected(String protocol)
83 * {
84 * System.out.println("Protocol Selected is: " + protocol);
85 * }
86 * });
87 * </pre>
88 * <p>There is no need to unregister {@link SSLSocket} or {@link SSLEngine} instances, as they
89 * are kept in a {@link WeakHashMap} and will be garbage collected when the application does not
90 * hard reference them anymore.</p>
91 * <p>In order to help application development, you can set the {@link NextProtoNego#debug} field
92 * to {@code true} to have debug code printed to {@link System#err}.</p>
93 */
94 public class NextProtoNego
95 {
96 /**
97 * <p>Enables debug logging on {@link System#err}.</p>
98 */
99 public static boolean debug = false;
100
101 private static Map<Object, Provider> objects = Collections.synchronizedMap(new WeakHashMap<Object, Provider>());
102
103 private NextProtoNego()
104 {
105 }
106
107 /**
108 * <p>Registers a SSLSocket with a provider.</p>
109 *
110 * @param socket the socket to register with the provider
111 * @param provider the provider to register with the socket
112 */
113 public static void put(SSLSocket socket, Provider provider)
114 {
115 objects.put(socket, provider);
116 }
117
118 /**
119 * @param socket a socket registered with {@link #put(SSLSocket, Provider)}
120 * @return the provider registered with the given socket
121 */
122 public static Provider get(SSLSocket socket)
123 {
124 return objects.get(socket);
125 }
126
127 /**
128 * <p>Registers a SSLEngine with a provider.</p>
129 *
130 * @param engine the engine to register with the provider
131 * @param provider the provider to register with the engine
132 */
133 public static void put(SSLEngine engine, Provider provider)
134 {
135 objects.put(engine, provider);
136 }
137
138 /**
139 *
140 * @param engine an engine registered with {@link #put(SSLEngine, Provider)}
141 * @return the provider registered with the given engine
142 */
143 public static Provider get(SSLEngine engine)
144 {
145 return objects.get(engine);
146 }
147
148 /**
149 * <p>Base, empty, interface for providers.</p>
150 */
151 public interface Provider
152 {
153 }
154
155 /**
156 * <p>The client-side provider interface that applications must implement to interact
157 * with the negotiation of the next protocol.</p>
158 */
159 public interface ClientProvider extends Provider
160 {
161 /**
162 * <p>Callback invoked to let the implementation know whether an
163 * empty NPN extension should be added to a ClientHello SSL message.</p>
164 *
165 * @return true to add the NPN extension, false otherwise
166 */
167 public boolean supports();
168
169 /**
170 * <p>Callback invoked to let the application know that the server does
171 * not support NPN.</p>
172 */
173 public void unsupported();
174
175 /**
176 * <p>Callback invoked to let the application select a protocol
177 * among the ones sent by the server.</p>
178 *
179 * @param protocols the protocols sent by the server
180 * @return the protocol selected by the application, or null if the
181 * NextProtocol SSL message should not be sent to the server
182 */
183 public String selectProtocol(List<String> protocols);
184 }
185
186 /**
187 * <p>The server-side provider interface that applications must implement to interact
188 * with the negotiation of the next protocol.</p>
189 */
190 public interface ServerProvider extends Provider
191 {
192 /**
193 * <p>Callback invoked to let the application know that the client does not
194 * support NPN.</p>
195 */
196 public void unsupported();
197
198 /**
199 * <p>Callback invoked to let the implementation know the list
200 * of protocols that should be added to an NPN extension in a
201 * ServerHello SSL message.</p>
202 * <p>This callback is invoked only if the client sent a NPN extension.</p>
203 *
204 * @return the list of protocols, or null if no NPN extension
205 * should be sent to the client
206 */
207 public List<String> protocols();
208
209 /**
210 * <p>Callback invoked to let the application know the protocol selected
211 * by the client.</p>
212 * <p>This callback is invoked only if the client sent a NextProtocol SSL message.</p>
213 *
214 * @param protocol the selected protocol
215 */
216 public void protocolSelected(String protocol);
217 }
218 }