View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
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      // Order is important on server side, so we use a LinkedHashMap
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         // Add sessions only if the connector is not stopping
277         return isRunning() && sessions.offer(session);
278     }
279 
280     protected boolean sessionClosed(Session session)
281     {
282         // Remove sessions only if the connector is not stopping
283         // to avoid concurrent removes during iterations
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 }