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