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