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 6:
217 extensions=Collections.emptyList();
218 connection = new WebSocketConnectionD06(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
219 break;
220 case 7:
221 case 8:
222 case 9:
223 case 10:
224 case 11:
225 case 12:
226 extensions= initExtensions(extensions_requested,8-WebSocketConnectionD12.OP_EXT_DATA, 16-WebSocketConnectionD13.OP_EXT_CTRL,3);
227 connection = new WebSocketConnectionD12(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
228 break;
229 case 13:
230 extensions= initExtensions(extensions_requested,8-WebSocketConnectionD13.OP_EXT_DATA, 16-WebSocketConnectionD13.OP_EXT_CTRL,3);
231 connection = new WebSocketConnectionD13(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
232 break;
233 default:
234 LOG.warn("Unsupported Websocket version: "+draft);
235 throw new HttpException(400, "Unsupported draft specification: " + draft);
236 }
237
238
239 connection.getConnection().setMaxBinaryMessageSize(_maxBinaryMessageSize);
240 connection.getConnection().setMaxTextMessageSize(_maxTextMessageSize);
241
242
243 connection.handshake(request, response, protocol);
244 response.flushBuffer();
245
246
247 connection.fillBuffersFrom(((HttpParser)http.getParser()).getHeaderBuffer());
248 connection.fillBuffersFrom(((HttpParser)http.getParser()).getBodyBuffer());
249
250
251 request.setAttribute("org.eclipse.jetty.io.Connection", connection);
252 }
253
254 protected String[] parseProtocols(String protocol)
255 {
256 if (protocol == null)
257 return new String[]{null};
258 protocol = protocol.trim();
259 if (protocol == null || protocol.length() == 0)
260 return new String[]{null};
261 String[] passed = protocol.split("\\s*,\\s*");
262 String[] protocols = new String[passed.length + 1];
263 System.arraycopy(passed, 0, protocols, 0, passed.length);
264 return protocols;
265 }
266
267 public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response)
268 throws IOException
269 {
270 if ("websocket".equalsIgnoreCase(request.getHeader("Upgrade")))
271 {
272 String origin = request.getHeader("Origin");
273 if (origin==null)
274 origin = request.getHeader("Sec-WebSocket-Origin");
275 if (!_acceptor.checkOrigin(request,origin))
276 {
277 response.sendError(HttpServletResponse.SC_FORBIDDEN);
278 return false;
279 }
280
281
282 WebSocket websocket = null;
283 String protocol = request.getHeader("Sec-WebSocket-Protocol");
284 if (protocol == null)
285 protocol = request.getHeader("WebSocket-Protocol");
286 for (String p : parseProtocols(protocol))
287 {
288 websocket = _acceptor.doWebSocketConnect(request, p);
289 if (websocket != null)
290 {
291 protocol = p;
292 break;
293 }
294 }
295
296
297 if (websocket == null)
298 {
299 response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
300 return false;
301 }
302
303
304 upgrade(request, response, websocket, protocol);
305 return true;
306 }
307
308 return false;
309 }
310
311 public List<Extension> initExtensions(List<String> requested,int maxDataOpcodes,int maxControlOpcodes,int maxReservedBits)
312 {
313 List<Extension> extensions = new ArrayList<Extension>();
314 for (String rExt : requested)
315 {
316 QuotedStringTokenizer tok = new QuotedStringTokenizer(rExt,";");
317 String extName=tok.nextToken().trim();
318 Map<String,String> parameters = new HashMap<String,String>();
319 while (tok.hasMoreTokens())
320 {
321 QuotedStringTokenizer nv = new QuotedStringTokenizer(tok.nextToken().trim(),"=");
322 String name=nv.nextToken().trim();
323 String value=nv.hasMoreTokens()?nv.nextToken().trim():null;
324 parameters.put(name,value);
325 }
326
327 Extension extension = newExtension(extName);
328
329 if (extension==null)
330 continue;
331
332 if (extension.init(parameters))
333 {
334 LOG.debug("add {} {}",extName,parameters);
335 extensions.add(extension);
336 }
337 }
338 LOG.debug("extensions={}",extensions);
339 return extensions;
340 }
341
342 private Extension newExtension(String name)
343 {
344 try
345 {
346 Class<? extends Extension> extClass = _extensionClasses.get(name);
347 if (extClass!=null)
348 return extClass.newInstance();
349 }
350 catch (Exception e)
351 {
352 LOG.warn(e);
353 }
354
355 return null;
356 }
357
358
359 }