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