1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
50
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
156 return sessions.offer(session);
157 }
158
159 protected boolean sessionClosed(Session session)
160 {
161
162
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
217 if (listener != null)
218 listener.onConnect(getSession());
219 }
220 }
221
222 }