1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.websocket;
15
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.Enumeration;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23 import javax.servlet.http.HttpServletRequest;
24 import javax.servlet.http.HttpServletResponse;
25
26 import org.eclipse.jetty.http.HttpException;
27 import org.eclipse.jetty.http.HttpParser;
28 import org.eclipse.jetty.io.ConnectedEndPoint;
29 import org.eclipse.jetty.server.HttpConnection;
30 import org.eclipse.jetty.util.QuotedStringTokenizer;
31 import org.eclipse.jetty.util.log.Log;
32 import org.eclipse.jetty.util.log.Logger;
33
34
35
36
37 public class WebSocketFactory
38 {
39 private static final Logger LOG = Log.getLogger(WebSocketFactory.class);
40
41 public interface Acceptor
42 {
43
44
45
46
47
48
49
50
51 WebSocket doWebSocketConnect(HttpServletRequest request, String protocol);
52
53
54
55
56
57
58
59
60 boolean checkOrigin(HttpServletRequest request, String origin);
61 }
62
63 private final Map<String,Class<? extends Extension>> _extensionClasses = new HashMap<String, Class<? extends Extension>>();
64 {
65 _extensionClasses.put("identity",IdentityExtension.class);
66 _extensionClasses.put("fragment",FragmentExtension.class);
67 _extensionClasses.put("x-deflate-frame",DeflateFrameExtension.class);
68 }
69
70 private final Acceptor _acceptor;
71 private WebSocketBuffers _buffers;
72 private int _maxIdleTime = 300000;
73 private int _maxTextMessageSize = 16*1024;
74 private int _maxBinaryMessageSize = -1;
75
76 public WebSocketFactory(Acceptor acceptor)
77 {
78 this(acceptor, 64 * 1024);
79 }
80
81 public WebSocketFactory(Acceptor acceptor, int bufferSize)
82 {
83 _buffers = new WebSocketBuffers(bufferSize);
84 _acceptor = acceptor;
85 }
86
87
88
89
90
91 public Map<String,Class<? extends Extension>> getExtensionClassesMap()
92 {
93 return _extensionClasses;
94 }
95
96
97
98
99
100
101 public long getMaxIdleTime()
102 {
103 return _maxIdleTime;
104 }
105
106
107
108
109
110
111 public void setMaxIdleTime(int maxIdleTime)
112 {
113 _maxIdleTime = maxIdleTime;
114 }
115
116
117
118
119
120
121 public int getBufferSize()
122 {
123 return _buffers.getBufferSize();
124 }
125
126
127
128
129
130
131 public void setBufferSize(int bufferSize)
132 {
133 if (bufferSize != getBufferSize())
134 _buffers = new WebSocketBuffers(bufferSize);
135 }
136
137
138
139
140 public int getMaxTextMessageSize()
141 {
142 return _maxTextMessageSize;
143 }
144
145
146
147
148
149
150 public void setMaxTextMessageSize(int maxTextMessageSize)
151 {
152 _maxTextMessageSize = maxTextMessageSize;
153 }
154
155
156
157
158 public int getMaxBinaryMessageSize()
159 {
160 return _maxBinaryMessageSize;
161 }
162
163
164
165
166
167
168 public void setMaxBinaryMessageSize(int maxBinaryMessageSize)
169 {
170 _maxBinaryMessageSize = maxBinaryMessageSize;
171 }
172
173
174
175
176
177
178
179
180
181
182
183
184
185 public void upgrade(HttpServletRequest request, HttpServletResponse response, WebSocket websocket, String protocol)
186 throws IOException
187 {
188 if (!"websocket".equalsIgnoreCase(request.getHeader("Upgrade")))
189 throw new IllegalStateException("!Upgrade:websocket");
190 if (!"HTTP/1.1".equals(request.getProtocol()))
191 throw new IllegalStateException("!HTTP/1.1");
192
193 int draft = request.getIntHeader("Sec-WebSocket-Version");
194 if (draft < 0)
195 draft = request.getIntHeader("Sec-WebSocket-Draft");
196 HttpConnection http = HttpConnection.getCurrentConnection();
197 ConnectedEndPoint endp = (ConnectedEndPoint)http.getEndPoint();
198
199 List<String> extensions_requested = new ArrayList<String>();
200 for (Enumeration e=request.getHeaders("Sec-WebSocket-Extensions");e.hasMoreElements();)
201 {
202 QuotedStringTokenizer tok = new QuotedStringTokenizer((String)e.nextElement(),",");
203 while (tok.hasMoreTokens())
204 extensions_requested.add(tok.nextToken());
205 }
206
207 final WebSocketConnection connection;
208 final List<Extension> extensions;
209 switch (draft)
210 {
211 case -1:
212 case 0:
213 extensions=Collections.emptyList();
214 connection = new WebSocketConnectionD00(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
215 break;
216 case 1:
217 case 2:
218 case 3:
219 case 4:
220 case 5:
221 case 6:
222 extensions=Collections.emptyList();
223 connection = new WebSocketConnectionD06(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
224 break;
225 case 7:
226 case 8:
227 extensions= initExtensions(extensions_requested,8-WebSocketConnectionD08.OP_EXT_DATA, 16-WebSocketConnectionD08.OP_EXT_CTRL,3);
228 connection = new WebSocketConnectionD08(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
229 break;
230 case 13:
231 extensions= initExtensions(extensions_requested,8-WebSocketConnectionD13.OP_EXT_DATA, 16-WebSocketConnectionD13.OP_EXT_CTRL,3);
232 connection = new WebSocketConnectionD13(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
233 break;
234 default:
235 LOG.warn("Unsupported Websocket version: "+draft);
236 response.setHeader("Sec-WebSocket-Version","0,6,8,13");
237 throw new HttpException(400, "Unsupported draft specification: " + draft);
238 }
239
240
241 connection.getConnection().setMaxBinaryMessageSize(_maxBinaryMessageSize);
242 connection.getConnection().setMaxTextMessageSize(_maxTextMessageSize);
243
244
245 connection.handshake(request, response, protocol);
246 response.flushBuffer();
247
248
249 connection.fillBuffersFrom(((HttpParser)http.getParser()).getHeaderBuffer());
250 connection.fillBuffersFrom(((HttpParser)http.getParser()).getBodyBuffer());
251
252
253 request.setAttribute("org.eclipse.jetty.io.Connection", connection);
254 }
255
256
257
258 protected String[] parseProtocols(String protocol)
259 {
260 if (protocol == null)
261 return new String[]{null};
262 protocol = protocol.trim();
263 if (protocol == null || protocol.length() == 0)
264 return new String[]{null};
265 String[] passed = protocol.split("\\s*,\\s*");
266 String[] protocols = new String[passed.length + 1];
267 System.arraycopy(passed, 0, protocols, 0, passed.length);
268 return protocols;
269 }
270
271
272
273 public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response)
274 throws IOException
275 {
276 if ("websocket".equalsIgnoreCase(request.getHeader("Upgrade")))
277 {
278 String origin = request.getHeader("Origin");
279 if (origin==null)
280 origin = request.getHeader("Sec-WebSocket-Origin");
281 if (!_acceptor.checkOrigin(request,origin))
282 {
283 response.sendError(HttpServletResponse.SC_FORBIDDEN);
284 return false;
285 }
286
287
288 WebSocket websocket = null;
289
290 Enumeration<String> protocols = request.getHeaders("Sec-WebSocket-Protocol");
291 String protocol=null;
292 while (protocol==null && protocols!=null && protocols.hasMoreElements())
293 {
294 String candidate = protocols.nextElement();
295 for (String p : parseProtocols(candidate))
296 {
297 websocket = _acceptor.doWebSocketConnect(request, p);
298 if (websocket != null)
299 {
300 protocol = p;
301 break;
302 }
303 }
304 }
305
306
307 if (websocket == null)
308 {
309
310 websocket = _acceptor.doWebSocketConnect(request, null);
311
312 if (websocket==null)
313 {
314 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
315 return false;
316 }
317 }
318
319
320 upgrade(request, response, websocket, protocol);
321 return true;
322 }
323
324 return false;
325 }
326
327
328
329 public List<Extension> initExtensions(List<String> requested,int maxDataOpcodes,int maxControlOpcodes,int maxReservedBits)
330 {
331 List<Extension> extensions = new ArrayList<Extension>();
332 for (String rExt : requested)
333 {
334 QuotedStringTokenizer tok = new QuotedStringTokenizer(rExt,";");
335 String extName=tok.nextToken().trim();
336 Map<String,String> parameters = new HashMap<String,String>();
337 while (tok.hasMoreTokens())
338 {
339 QuotedStringTokenizer nv = new QuotedStringTokenizer(tok.nextToken().trim(),"=");
340 String name=nv.nextToken().trim();
341 String value=nv.hasMoreTokens()?nv.nextToken().trim():null;
342 parameters.put(name,value);
343 }
344
345 Extension extension = newExtension(extName);
346
347 if (extension==null)
348 continue;
349
350 if (extension.init(parameters))
351 {
352 LOG.debug("add {} {}",extName,parameters);
353 extensions.add(extension);
354 }
355 }
356 LOG.debug("extensions={}",extensions);
357 return extensions;
358 }
359
360
361
362 private Extension newExtension(String name)
363 {
364 try
365 {
366 Class<? extends Extension> extClass = _extensionClasses.get(name);
367 if (extClass!=null)
368 return extClass.newInstance();
369 }
370 catch (Exception e)
371 {
372 LOG.warn(e);
373 }
374
375 return null;
376 }
377 }