1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.websocket.server;
20
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Queue;
29 import java.util.concurrent.ConcurrentLinkedQueue;
30 import java.util.concurrent.Executor;
31
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34
35 import org.eclipse.jetty.http.HttpStatus;
36 import org.eclipse.jetty.io.ByteBufferPool;
37 import org.eclipse.jetty.io.EndPoint;
38 import org.eclipse.jetty.io.MappedByteBufferPool;
39 import org.eclipse.jetty.server.HttpConnection;
40 import org.eclipse.jetty.util.component.ContainerLifeCycle;
41 import org.eclipse.jetty.util.log.Log;
42 import org.eclipse.jetty.util.log.Logger;
43 import org.eclipse.jetty.util.thread.Scheduler;
44 import org.eclipse.jetty.util.thread.TimerScheduler;
45 import org.eclipse.jetty.websocket.core.annotations.WebSocket;
46 import org.eclipse.jetty.websocket.core.api.Extension;
47 import org.eclipse.jetty.websocket.core.api.ExtensionRegistry;
48 import org.eclipse.jetty.websocket.core.api.UpgradeRequest;
49 import org.eclipse.jetty.websocket.core.api.UpgradeResponse;
50 import org.eclipse.jetty.websocket.core.api.WebSocketException;
51 import org.eclipse.jetty.websocket.core.api.WebSocketPolicy;
52 import org.eclipse.jetty.websocket.core.extensions.WebSocketExtensionRegistry;
53 import org.eclipse.jetty.websocket.core.io.IncomingFrames;
54 import org.eclipse.jetty.websocket.core.io.OutgoingFrames;
55 import org.eclipse.jetty.websocket.core.io.WebSocketSession;
56 import org.eclipse.jetty.websocket.core.io.event.EventDriver;
57 import org.eclipse.jetty.websocket.core.io.event.EventDriverFactory;
58 import org.eclipse.jetty.websocket.core.protocol.ExtensionConfig;
59 import org.eclipse.jetty.websocket.server.handshake.HandshakeRFC6455;
60
61
62
63
64 public class WebSocketServerFactory extends ContainerLifeCycle implements WebSocketCreator
65 {
66 private static final Logger LOG = Log.getLogger(WebSocketServerFactory.class);
67
68 private final Map<Integer, WebSocketHandshake> handshakes = new HashMap<>();
69 {
70 handshakes.put(HandshakeRFC6455.VERSION,new HandshakeRFC6455());
71 }
72
73 private final Queue<WebSocketSession> sessions = new ConcurrentLinkedQueue<>();
74
75
76
77 private final Scheduler scheduler = new TimerScheduler();
78 private final String supportedVersions;
79 private final WebSocketPolicy basePolicy;
80 private final EventDriverFactory eventDriverFactory;
81 private final WebSocketExtensionRegistry extensionRegistry;
82 private WebSocketCreator creator;
83 private List<Class<?>> registeredSocketClasses;
84
85 public WebSocketServerFactory(WebSocketPolicy policy)
86 {
87 this(policy,new MappedByteBufferPool());
88 }
89
90 public WebSocketServerFactory(WebSocketPolicy policy, ByteBufferPool bufferPool)
91 {
92 addBean(scheduler);
93 addBean(bufferPool);
94
95 this.registeredSocketClasses = new ArrayList<>();
96
97 this.basePolicy = policy;
98 this.eventDriverFactory = new EventDriverFactory(basePolicy);
99 this.extensionRegistry = new WebSocketExtensionRegistry(basePolicy,bufferPool);
100 this.creator = this;
101
102
103 List<Integer> versions = new ArrayList<>();
104 for (int v : handshakes.keySet())
105 {
106 versions.add(v);
107 }
108 Collections.sort(versions,Collections.reverseOrder());
109 StringBuilder rv = new StringBuilder();
110 for (int v : versions)
111 {
112 if (rv.length() > 0)
113 {
114 rv.append(", ");
115 }
116 rv.append(v);
117 }
118 supportedVersions = rv.toString();
119 }
120
121 public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response) throws IOException
122 {
123 ServletWebSocketRequest sockreq = new ServletWebSocketRequest(request);
124 ServletWebSocketResponse sockresp = new ServletWebSocketResponse(response);
125
126 WebSocketCreator creator = getCreator();
127
128 Object websocketPojo = creator.createWebSocket(sockreq,sockresp);
129
130
131 if (sockresp.isCommitted())
132 {
133 return false;
134 }
135
136 if (websocketPojo == null)
137 {
138
139 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
140 return false;
141 }
142
143
144 EventDriver driver = eventDriverFactory.wrap(websocketPojo);
145 return upgrade(sockreq,sockresp,driver);
146 }
147
148 protected void closeConnections()
149 {
150 for (WebSocketSession session : sessions)
151 {
152 session.close();
153 }
154 sessions.clear();
155 }
156
157
158
159
160 @Override
161 public Object createWebSocket(UpgradeRequest req, UpgradeResponse resp)
162 {
163 if (registeredSocketClasses.size() < 1)
164 {
165 throw new WebSocketException("No WebSockets have been registered with the factory. Cannot use default implementation of WebSocketCreator.");
166 }
167
168 if (registeredSocketClasses.size() > 1)
169 {
170 LOG.warn("You have registered more than 1 websocket object, and are using the default WebSocketCreator! Using first registered websocket.");
171 }
172
173 Class<?> firstClass = registeredSocketClasses.get(0);
174 try
175 {
176 return firstClass.newInstance();
177 }
178 catch (InstantiationException | IllegalAccessException e)
179 {
180 throw new WebSocketException("Unable to create instance of " + firstClass,e);
181 }
182 }
183
184 @Override
185 protected void doStop() throws Exception
186 {
187 closeConnections();
188 super.doStop();
189 }
190
191 public WebSocketCreator getCreator()
192 {
193 return this.creator;
194 }
195
196 public ExtensionRegistry getExtensionRegistry()
197 {
198 return extensionRegistry;
199 }
200
201
202
203
204
205
206
207
208 public WebSocketPolicy getPolicy()
209 {
210 return basePolicy;
211 }
212
213 public List<Extension> initExtensions(List<ExtensionConfig> requested)
214 {
215 List<Extension> extensions = new ArrayList<Extension>();
216
217 for (ExtensionConfig cfg : requested)
218 {
219 Extension extension = extensionRegistry.newInstance(cfg);
220
221 if (extension == null)
222 {
223 continue;
224 }
225
226 LOG.debug("added {}",extension);
227 extensions.add(extension);
228 }
229 LOG.debug("extensions={}",extensions);
230 return extensions;
231 }
232
233 public boolean isUpgradeRequest(HttpServletRequest request, HttpServletResponse response)
234 {
235 String upgrade = request.getHeader("Upgrade");
236 if (upgrade == null)
237 {
238
239 return false;
240 }
241
242 if (!"websocket".equalsIgnoreCase(upgrade))
243 {
244 LOG.warn("Not a 'Upgrade: WebSocket' (was [Upgrade: " + upgrade + "])");
245 return false;
246 }
247
248 if (!"HTTP/1.1".equals(request.getProtocol()))
249 {
250 LOG.warn("Not a 'HTTP/1.1' request (was [" + request.getProtocol() + "])");
251 return false;
252 }
253
254 return true;
255 }
256
257 protected String[] parseProtocols(String protocol)
258 {
259 if (protocol == null)
260 {
261 return new String[]
262 { null };
263 }
264 protocol = protocol.trim();
265 if ((protocol == null) || (protocol.length() == 0))
266 {
267 return new String[]
268 { null };
269 }
270 String[] passed = protocol.split("\\s*,\\s*");
271 String[] protocols = new String[passed.length + 1];
272 System.arraycopy(passed,0,protocols,0,passed.length);
273 return protocols;
274 }
275
276
277
278
279
280
281
282
283
284 public void register(Class<?> websocketPojo)
285 {
286 registeredSocketClasses.add(websocketPojo);
287 }
288
289 public boolean sessionClosed(WebSocketSession session)
290 {
291 return isRunning() && sessions.remove(session);
292 }
293
294 public boolean sessionOpened(WebSocketSession session)
295 {
296 if (LOG.isDebugEnabled())
297 {
298 LOG.debug("Session Opened: {}",session);
299 }
300 if (!isRunning())
301 {
302 LOG.warn("Factory is not running");
303 return false;
304 }
305 boolean ret = sessions.offer(session);
306 session.onConnect();
307 return ret;
308 }
309
310 public void setCreator(WebSocketCreator creator)
311 {
312 this.creator = creator;
313 }
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330 public boolean upgrade(ServletWebSocketRequest request, ServletWebSocketResponse response, EventDriver driver) throws IOException
331 {
332 if (!"websocket".equalsIgnoreCase(request.getHeader("Upgrade")))
333 {
334 throw new IllegalStateException("Not a 'WebSocket: Upgrade' request");
335 }
336 if (!"HTTP/1.1".equals(request.getProtocol()))
337 {
338 throw new IllegalStateException("Not a 'HTTP/1.1' request");
339 }
340
341 int version = request.getIntHeader("Sec-WebSocket-Version");
342 if (version < 0)
343 {
344
345 version = request.getIntHeader("Sec-WebSocket-Draft");
346 }
347
348 WebSocketHandshake handshaker = handshakes.get(version);
349 if (handshaker == null)
350 {
351 LOG.warn("Unsupported Websocket version: " + version);
352
353
354 response.setHeader("Sec-WebSocket-Version",supportedVersions);
355 response.sendError(HttpStatus.BAD_REQUEST_400,"Unsupported websocket version specification");
356 return false;
357 }
358
359
360 HttpConnection http = HttpConnection.getCurrentConnection();
361 EndPoint endp = http.getEndPoint();
362 Executor executor = http.getConnector().getExecutor();
363 ByteBufferPool bufferPool = http.getConnector().getByteBufferPool();
364 WebSocketServerConnection connection = new WebSocketServerConnection(endp,executor,scheduler,driver.getPolicy(),bufferPool,this);
365
366 LOG.debug("HttpConnection: {}",http);
367 LOG.debug("AsyncWebSocketConnection: {}",connection);
368
369
370 WebSocketSession session = new WebSocketSession(driver,connection,getPolicy(),response.getAcceptedSubProtocol());
371 connection.setSession(session);
372 List<Extension> extensions = initExtensions(request.getExtensions());
373 request.setValidExtensions(extensions);
374
375
376 IncomingFrames incoming = session;
377 OutgoingFrames outgoing = connection;
378
379
380 if (extensions != null)
381 {
382 connection.getParser().configureFromExtensions(extensions);
383 connection.getGenerator().configureFromExtensions(extensions);
384
385 Iterator<Extension> extIter;
386
387 extIter = extensions.iterator();
388 while (extIter.hasNext())
389 {
390 Extension ext = extIter.next();
391 ext.setNextOutgoingFrames(outgoing);
392 outgoing = ext;
393 }
394
395
396 Collections.reverse(extensions);
397 extIter = extensions.iterator();
398 while (extIter.hasNext())
399 {
400 Extension ext = extIter.next();
401 ext.setNextIncomingFrames(incoming);
402 incoming = ext;
403 }
404 }
405
406
407 session.setOutgoing(outgoing);
408
409 connection.getParser().setIncomingFramesHandler(incoming);
410
411
412 request.setAttribute(HttpConnection.UPGRADE_CONNECTION_ATTRIBUTE,connection);
413
414
415 LOG.debug("Handshake Response: {}",handshaker);
416 handshaker.doHandshakeResponse(request,response);
417
418 LOG.debug("Websocket upgrade {} {} {} {}",request.getRequestURI(),version,response.getAcceptedSubProtocol(),connection);
419 return true;
420 }
421 }