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