View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 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  package org.eclipse.jetty.spdy.server;
20  
21  import java.io.IOException;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.Queue;
25  import java.util.concurrent.ConcurrentLinkedQueue;
26  import java.util.concurrent.atomic.AtomicBoolean;
27  
28  import org.eclipse.jetty.io.Connection;
29  import org.eclipse.jetty.io.EndPoint;
30  import org.eclipse.jetty.server.AbstractConnectionFactory;
31  import org.eclipse.jetty.server.Connector;
32  import org.eclipse.jetty.spdy.CompressionFactory;
33  import org.eclipse.jetty.spdy.FlowControlStrategy;
34  import org.eclipse.jetty.spdy.StandardCompressionFactory;
35  import org.eclipse.jetty.spdy.StandardSession;
36  import org.eclipse.jetty.spdy.api.GoAwayInfo;
37  import org.eclipse.jetty.spdy.api.Session;
38  import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
39  import org.eclipse.jetty.spdy.client.FlowControlStrategyFactory;
40  import org.eclipse.jetty.spdy.client.SPDYConnection;
41  import org.eclipse.jetty.spdy.generator.Generator;
42  import org.eclipse.jetty.spdy.parser.Parser;
43  import org.eclipse.jetty.util.Callback;
44  import org.eclipse.jetty.util.annotation.ManagedAttribute;
45  import org.eclipse.jetty.util.annotation.ManagedObject;
46  
47  @ManagedObject("SPDY Server Connection Factory")
48  public class SPDYServerConnectionFactory extends AbstractConnectionFactory
49  {
50      /**
51       * @deprecated use {@link #checkProtocolNegotiationAvailable()} instead.
52       */
53      @Deprecated
54      public static void checkNPNAvailable()
55      {
56          checkProtocolNegotiationAvailable();
57      }
58  
59      public static void checkProtocolNegotiationAvailable()
60      {
61          if (!isAvailableInBootClassPath("org.eclipse.jetty.alpn.ALPN") &&
62                  !isAvailableInBootClassPath("org.eclipse.jetty.npn.NextProtoNego"))
63              throw new IllegalStateException("No ALPN nor NPN classes available");
64      }
65  
66      private static boolean isAvailableInBootClassPath(String className)
67      {
68          try
69          {
70              Class<?> klass = ClassLoader.getSystemClassLoader().loadClass(className);
71              if (klass.getClassLoader() != null)
72                  throw new IllegalStateException(className + " must be on JVM boot classpath");
73              return true;
74          }
75          catch (ClassNotFoundException x)
76          {
77              return false;
78          }
79      }
80  
81      private final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
82      private final short version;
83      private final ServerSessionFrameListener listener;
84      private int initialWindowSize;
85      private boolean dispatchIO;
86  
87      public SPDYServerConnectionFactory(int version)
88      {
89          this(version, null);
90      }
91  
92      public SPDYServerConnectionFactory(int version, ServerSessionFrameListener listener)
93      {
94          super("spdy/" + version);
95          this.version = (short)version;
96          this.listener = listener;
97          setInitialWindowSize(65536);
98          setDispatchIO(true);
99      }
100 
101     @ManagedAttribute("SPDY version")
102     public short getVersion()
103     {
104         return version;
105     }
106 
107     public ServerSessionFrameListener getServerSessionFrameListener()
108     {
109         return listener;
110     }
111 
112     @Override
113     public Connection newConnection(Connector connector, EndPoint endPoint)
114     {
115         CompressionFactory compressionFactory = new StandardCompressionFactory();
116         Parser parser = new Parser(compressionFactory.newDecompressor());
117         Generator generator = new Generator(connector.getByteBufferPool(), compressionFactory.newCompressor());
118 
119         ServerSessionFrameListener listener = provideServerSessionFrameListener(connector, endPoint);
120         SPDYConnection connection = new ServerSPDYConnection(connector, endPoint, parser, listener,
121                 isDispatchIO(), getInputBufferSize());
122 
123         FlowControlStrategy flowControlStrategy = newFlowControlStrategy(version);
124 
125         StandardSession session = new StandardSession(getVersion(), connector.getByteBufferPool(),
126                 connector.getScheduler(), connection, endPoint, connection, 2, listener,
127                 generator, flowControlStrategy);
128         session.setWindowSize(getInitialWindowSize());
129         parser.addListener(session);
130         connection.setSession(session);
131 
132         sessionOpened(session);
133 
134         return configure(connection, connector, endPoint);
135     }
136 
137     protected FlowControlStrategy newFlowControlStrategy(short version)
138     {
139         return FlowControlStrategyFactory.newFlowControlStrategy(version);
140     }
141 
142     protected ServerSessionFrameListener provideServerSessionFrameListener(Connector connector, EndPoint endPoint)
143     {
144         return listener;
145     }
146 
147     @ManagedAttribute("Initial Window Size")
148     public int getInitialWindowSize()
149     {
150         return initialWindowSize;
151     }
152 
153     public void setInitialWindowSize(int initialWindowSize)
154     {
155         this.initialWindowSize = initialWindowSize;
156     }
157 
158     @ManagedAttribute("Dispatch I/O to a pooled thread")
159     public boolean isDispatchIO()
160     {
161         return dispatchIO;
162     }
163 
164     public void setDispatchIO(boolean dispatchIO)
165     {
166         this.dispatchIO = dispatchIO;
167     }
168 
169     protected boolean sessionOpened(Session session)
170     {
171         // Add sessions only if the connector is not stopping
172         return sessions.offer(session);
173     }
174 
175     protected boolean sessionClosed(Session session)
176     {
177         // Remove sessions only if the connector is not stopping
178         // to avoid concurrent removes during iterations
179         return sessions.remove(session);
180     }
181 
182     void closeSessions()
183     {
184         for (Session session : sessions)
185             session.goAway(new GoAwayInfo(), Callback.Adapter.INSTANCE);
186         sessions.clear();
187     }
188 
189     @Override
190     protected void doStop() throws Exception
191     {
192         closeSessions();
193         super.doStop();
194     }
195 
196     public Collection<Session> getSessions()
197     {
198         return Collections.unmodifiableCollection(sessions);
199     }
200 
201     @Override
202     protected void dumpThis(Appendable out) throws IOException
203     {
204         super.dumpThis(out);
205         dump(out, "", sessions);
206     }
207 
208     private class ServerSPDYConnection extends SPDYConnection implements Runnable
209     {
210         private final ServerSessionFrameListener listener;
211         private final AtomicBoolean connected = new AtomicBoolean();
212 
213         private ServerSPDYConnection(Connector connector, EndPoint endPoint, Parser parser,
214                                      ServerSessionFrameListener listener, boolean dispatchIO, int bufferSize)
215         {
216             super(endPoint, connector.getByteBufferPool(), parser, connector.getExecutor(),
217                     dispatchIO, bufferSize);
218             this.listener = listener;
219         }
220 
221         @Override
222         public void onOpen()
223         {
224             super.onOpen();
225             if (connected.compareAndSet(false, true))
226                 getExecutor().execute(this);
227         }
228 
229         @Override
230         public void onClose()
231         {
232             super.onClose();
233             sessionClosed(getSession());
234         }
235 
236         @Override
237         public void run()
238         {
239             // NPE guard to support tests
240             if (listener != null)
241                 listener.onConnect(getSession());
242         }
243     }
244 
245 }