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 final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
73  
74      public SPDYServerConnectionFactory(int version)
75      {
76          this(version, null);
77      }
78  
79      public SPDYServerConnectionFactory(int version, ServerSessionFrameListener listener)
80      {
81          super("spdy/" + version);
82          this.version = (short)version;
83          this.listener = listener;
84          setInitialWindowSize(65536);
85      }
86  
87      @ManagedAttribute("SPDY version")
88      public short getVersion()
89      {
90          return version;
91      }
92  
93      public ServerSessionFrameListener getServerSessionFrameListener()
94      {
95          return listener;
96      }
97  
98      @Override
99      public Connection newConnection(Connector connector, EndPoint endPoint)
100     {
101         CompressionFactory compressionFactory = new StandardCompressionFactory();
102         Parser parser = new Parser(compressionFactory.newDecompressor());
103         Generator generator = new Generator(connector.getByteBufferPool(), compressionFactory.newCompressor());
104 
105         ServerSessionFrameListener listener = provideServerSessionFrameListener(connector, endPoint);
106         SPDYConnection connection = new ServerSPDYConnection(connector, endPoint, parser, listener, getInputBufferSize());
107 
108         FlowControlStrategy flowControlStrategy = newFlowControlStrategy(version);
109 
110         StandardSession session = new StandardSession(getVersion(), connector.getByteBufferPool(),
111                 connector.getExecutor(), connector.getScheduler(), connection, endPoint, connection, 2, listener,
112                 generator, flowControlStrategy);
113         session.setWindowSize(getInitialWindowSize());
114         parser.addListener(session);
115         connection.setSession(session);
116 
117         sessionOpened(session);
118 
119         return configure(connection, connector, endPoint);
120     }
121 
122     protected FlowControlStrategy newFlowControlStrategy(short version)
123     {
124         return FlowControlStrategyFactory.newFlowControlStrategy(version);
125     }
126 
127     protected ServerSessionFrameListener provideServerSessionFrameListener(Connector connector, EndPoint endPoint)
128     {
129         return listener;
130     }
131 
132     @ManagedAttribute("Initial Window Size")
133     public int getInitialWindowSize()
134     {
135         return initialWindowSize;
136     }
137 
138     public void setInitialWindowSize(int initialWindowSize)
139     {
140         this.initialWindowSize = initialWindowSize;
141     }
142 
143     protected boolean sessionOpened(Session session)
144     {
145         // Add sessions only if the connector is not stopping
146         return sessions.offer(session);
147     }
148 
149     protected boolean sessionClosed(Session session)
150     {
151         // Remove sessions only if the connector is not stopping
152         // to avoid concurrent removes during iterations
153         return sessions.remove(session);
154     }
155 
156     void closeSessions()
157     {
158         for (Session session : sessions)
159             session.goAway(new GoAwayInfo(), new Callback.Adapter());
160         sessions.clear();
161     }
162 
163     @Override
164     protected void doStop() throws Exception
165     {
166         closeSessions();
167         super.doStop();
168     }
169 
170     public Collection<Session> getSessions()
171     {
172         return Collections.unmodifiableCollection(sessions);
173     }
174 
175     private class ServerSPDYConnection extends SPDYConnection implements Runnable
176     {
177         private final ServerSessionFrameListener listener;
178         private final AtomicBoolean connected = new AtomicBoolean();
179 
180         private ServerSPDYConnection(Connector connector, EndPoint endPoint, Parser parser, ServerSessionFrameListener listener, int bufferSize)
181         {
182             super(endPoint, connector.getByteBufferPool(), parser, connector.getExecutor(), bufferSize);
183             this.listener = listener;
184         }
185 
186         @Override
187         public void onOpen()
188         {
189             super.onOpen();
190             if (connected.compareAndSet(false, true))
191                 getExecutor().execute(this);
192         }
193 
194         @Override
195         public void onClose()
196         {
197             super.onClose();
198             sessionClosed(getSession());
199         }
200 
201         @Override
202         public void run()
203         {
204             // NPE guard to support tests
205             if (listener != null)
206                 listener.onConnect(getSession());
207         }
208     }
209 
210 }