View Javadoc

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