1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.eclipse.jetty.spdy;
21
22 import java.io.IOException;
23 import java.nio.channels.SocketChannel;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.LinkedHashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Queue;
31 import java.util.concurrent.ConcurrentLinkedQueue;
32 import java.util.concurrent.Executor;
33 import java.util.concurrent.Executors;
34 import java.util.concurrent.RejectedExecutionException;
35 import java.util.concurrent.ScheduledExecutorService;
36 import java.util.concurrent.TimeUnit;
37 import javax.net.ssl.SSLEngine;
38 import javax.net.ssl.SSLException;
39
40 import org.eclipse.jetty.io.AsyncEndPoint;
41 import org.eclipse.jetty.io.nio.AsyncConnection;
42 import org.eclipse.jetty.io.nio.SslConnection;
43 import org.eclipse.jetty.npn.NextProtoNego;
44 import org.eclipse.jetty.server.nio.SelectChannelConnector;
45 import org.eclipse.jetty.spdy.api.SPDY;
46 import org.eclipse.jetty.spdy.api.Session;
47 import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
48 import org.eclipse.jetty.util.component.AggregateLifeCycle;
49 import org.eclipse.jetty.util.log.Log;
50 import org.eclipse.jetty.util.log.Logger;
51 import org.eclipse.jetty.util.ssl.SslContextFactory;
52 import org.eclipse.jetty.util.thread.ThreadPool;
53
54 public class SPDYServerConnector extends SelectChannelConnector
55 {
56 private static final Logger logger = Log.getLogger(SPDYServerConnector.class);
57
58
59 private final Map<String, AsyncConnectionFactory> factories = new LinkedHashMap<>();
60 private final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
61 private final ByteBufferPool bufferPool = new StandardByteBufferPool();
62 private final Executor executor = new LazyExecutor();
63 private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
64 private final ServerSessionFrameListener listener;
65 private final SslContextFactory sslContextFactory;
66 private volatile AsyncConnectionFactory defaultConnectionFactory;
67 private volatile int initialWindowSize = 65536;
68
69 public SPDYServerConnector(ServerSessionFrameListener listener)
70 {
71 this(listener, null);
72 }
73
74 public SPDYServerConnector(ServerSessionFrameListener listener, SslContextFactory sslContextFactory)
75 {
76 this.listener = listener;
77 this.sslContextFactory = sslContextFactory;
78 if (sslContextFactory != null)
79 addBean(sslContextFactory);
80 putAsyncConnectionFactory("spdy/3", new ServerSPDYAsyncConnectionFactory(SPDY.V3, bufferPool, executor, scheduler, listener));
81 putAsyncConnectionFactory("spdy/2", new ServerSPDYAsyncConnectionFactory(SPDY.V2, bufferPool, executor, scheduler, listener));
82 setDefaultAsyncConnectionFactory(getAsyncConnectionFactory("spdy/2"));
83 }
84
85 public ByteBufferPool getByteBufferPool()
86 {
87 return bufferPool;
88 }
89
90 public Executor getExecutor()
91 {
92 return executor;
93 }
94
95 public ScheduledExecutorService getScheduler()
96 {
97 return scheduler;
98 }
99
100 public ServerSessionFrameListener getServerSessionFrameListener()
101 {
102 return listener;
103 }
104
105 public SslContextFactory getSslContextFactory()
106 {
107 return sslContextFactory;
108 }
109
110 @Override
111 protected void doStart() throws Exception
112 {
113 super.doStart();
114 logger.info("SPDY support is experimental. Please report feedback at jetty-dev@eclipse.org");
115 }
116
117 @Override
118 protected void doStop() throws Exception
119 {
120 closeSessions();
121 scheduler.shutdown();
122 super.doStop();
123 }
124
125 @Override
126 public void join() throws InterruptedException
127 {
128 scheduler.awaitTermination(0, TimeUnit.MILLISECONDS);
129 super.join();
130 }
131
132 public AsyncConnectionFactory getAsyncConnectionFactory(String protocol)
133 {
134 synchronized (factories)
135 {
136 return factories.get(protocol);
137 }
138 }
139
140 public AsyncConnectionFactory putAsyncConnectionFactory(String protocol, AsyncConnectionFactory factory)
141 {
142 synchronized (factories)
143 {
144 return factories.put(protocol, factory);
145 }
146 }
147
148 public AsyncConnectionFactory removeAsyncConnectionFactory(String protocol)
149 {
150 synchronized (factories)
151 {
152 return factories.remove(protocol);
153 }
154 }
155
156 public Map<String, AsyncConnectionFactory> getAsyncConnectionFactories()
157 {
158 synchronized (factories)
159 {
160 return new LinkedHashMap<>(factories);
161 }
162 }
163
164 public void clearAsyncConnectionFactories()
165 {
166 synchronized (factories)
167 {
168 factories.clear();
169 }
170 }
171
172 protected List<String> provideProtocols()
173 {
174 synchronized (factories)
175 {
176 return new ArrayList<>(factories.keySet());
177 }
178 }
179
180 public AsyncConnectionFactory getDefaultAsyncConnectionFactory()
181 {
182 return defaultConnectionFactory;
183 }
184
185 public void setDefaultAsyncConnectionFactory(AsyncConnectionFactory defaultConnectionFactory)
186 {
187 this.defaultConnectionFactory = defaultConnectionFactory;
188 }
189
190 @Override
191 protected AsyncConnection newConnection(final SocketChannel channel, AsyncEndPoint endPoint)
192 {
193 if (sslContextFactory != null)
194 {
195 final SSLEngine engine = newSSLEngine(sslContextFactory, channel);
196 SslConnection sslConnection = new SslConnection(engine, endPoint)
197 {
198 @Override
199 public void onClose()
200 {
201 NextProtoNego.remove(engine);
202 super.onClose();
203 }
204 };
205 endPoint.setConnection(sslConnection);
206 final AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint();
207 NextProtoNego.put(engine, new NextProtoNego.ServerProvider()
208 {
209 @Override
210 public void unsupported()
211 {
212 AsyncConnectionFactory connectionFactory = getDefaultAsyncConnectionFactory();
213 AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this);
214 sslEndPoint.setConnection(connection);
215 }
216
217 @Override
218 public List<String> protocols()
219 {
220 return provideProtocols();
221 }
222
223 @Override
224 public void protocolSelected(String protocol)
225 {
226 AsyncConnectionFactory connectionFactory = getAsyncConnectionFactory(protocol);
227 AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, SPDYServerConnector.this);
228 sslEndPoint.setConnection(connection);
229 }
230 });
231
232 AsyncConnection connection = new EmptyAsyncConnection(sslEndPoint);
233 sslEndPoint.setConnection(connection);
234
235 startHandshake(engine);
236
237 return sslConnection;
238 }
239 else
240 {
241 AsyncConnectionFactory connectionFactory = getDefaultAsyncConnectionFactory();
242 AsyncConnection connection = connectionFactory.newAsyncConnection(channel, endPoint, this);
243 endPoint.setConnection(connection);
244 return connection;
245 }
246 }
247
248 protected FlowControlStrategy newFlowControlStrategy(short version)
249 {
250 return FlowControlStrategyFactory.newFlowControlStrategy(version);
251 }
252
253 protected SSLEngine newSSLEngine(SslContextFactory sslContextFactory, SocketChannel channel)
254 {
255 String peerHost = channel.socket().getInetAddress().getHostAddress();
256 int peerPort = channel.socket().getPort();
257 SSLEngine engine = sslContextFactory.newSslEngine(peerHost, peerPort);
258 engine.setUseClientMode(false);
259 return engine;
260 }
261
262 private void startHandshake(SSLEngine engine)
263 {
264 try
265 {
266 engine.beginHandshake();
267 }
268 catch (SSLException x)
269 {
270 throw new RuntimeException(x);
271 }
272 }
273
274 protected boolean sessionOpened(Session session)
275 {
276
277 return isRunning() && sessions.offer(session);
278 }
279
280 protected boolean sessionClosed(Session session)
281 {
282
283
284 return isRunning() && sessions.remove(session);
285 }
286
287 private void closeSessions()
288 {
289 for (Session session : sessions)
290 session.goAway();
291 sessions.clear();
292 }
293
294 protected Collection<Session> getSessions()
295 {
296 return Collections.unmodifiableCollection(sessions);
297 }
298
299 public int getInitialWindowSize()
300 {
301 return initialWindowSize;
302 }
303
304 public void setInitialWindowSize(int initialWindowSize)
305 {
306 this.initialWindowSize = initialWindowSize;
307 }
308
309 private class LazyExecutor implements Executor
310 {
311 @Override
312 public void execute(Runnable command)
313 {
314 ThreadPool threadPool = getThreadPool();
315 if (threadPool == null)
316 throw new RejectedExecutionException();
317 threadPool.dispatch(command);
318 }
319 }
320
321
322 @Override
323 public void dump(Appendable out, String indent) throws IOException
324 {
325 super.dump(out,indent);
326 AggregateLifeCycle.dump(out, indent, new ArrayList<Session>(sessions));
327 }
328
329
330 }