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.security.MessageDigest;
18 import java.security.NoSuchAlgorithmException;
19
20 import javax.servlet.http.HttpServletRequest;
21 import javax.servlet.http.HttpServletResponse;
22
23 import org.eclipse.jetty.io.AbstractConnection;
24 import org.eclipse.jetty.io.AsyncEndPoint;
25 import org.eclipse.jetty.io.Buffer;
26 import org.eclipse.jetty.io.ByteArrayBuffer;
27 import org.eclipse.jetty.io.Connection;
28 import org.eclipse.jetty.io.EndPoint;
29 import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
30 import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
31 import org.eclipse.jetty.util.log.Log;
32
33 public class WebSocketConnectionD00 extends AbstractConnection implements WebSocketConnection, WebSocket.Outbound
34 {
35 final IdleCheck _idle;
36 final WebSocketParser _parser;
37 final WebSocketGenerator _generator;
38 final WebSocket _websocket;
39 String _key1;
40 String _key2;
41 ByteArrayBuffer _hixieBytes;
42
43 public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint,int draft)
44 throws IOException
45 {
46 this(websocket,endpoint,new WebSocketBuffers(8192),System.currentTimeMillis(),300000,draft);
47 }
48
49 public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, int draft)
50 throws IOException
51 {
52 super(endpoint,timestamp);
53
54 if (endpoint instanceof AsyncEndPoint)
55 ((AsyncEndPoint)endpoint).cancelIdle();
56
57 _endp.setMaxIdleTime(maxIdleTime);
58
59 _websocket = websocket;
60
61
62 switch(draft)
63 {
64 case 1:
65 _generator = new WebSocketGeneratorD01(buffers, _endp);
66 _parser = new WebSocketParserD01(buffers, endpoint, new FrameHandlerD1(this,_websocket));
67 break;
68 default:
69 _generator = new WebSocketGeneratorD00(buffers, _endp);
70 _parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD0(_websocket));
71 }
72
73
74 if (_endp instanceof SelectChannelEndPoint)
75 {
76 final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp;
77 scep.cancelIdle();
78 _idle=new IdleCheck()
79 {
80 public void access(EndPoint endp)
81 {
82 scep.scheduleIdle();
83 }
84 };
85 scep.scheduleIdle();
86 }
87 else
88 {
89 _idle = new IdleCheck()
90 {
91 public void access(EndPoint endp)
92 {}
93 };
94 }
95 }
96
97 public void setHixieKeys(String key1,String key2)
98 {
99 _key1=key1;
100 _key2=key2;
101 _hixieBytes=new IndirectNIOBuffer(16);
102 }
103
104 public Connection handle() throws IOException
105 {
106 try
107 {
108
109 if (_hixieBytes!=null)
110 {
111
112
113 Buffer buffer=_parser.getBuffer();
114 if (buffer!=null && buffer.length()>0)
115 {
116 int l=buffer.length();
117 if (l>(8-_hixieBytes.length()))
118 l=8-_hixieBytes.length();
119 _hixieBytes.put(buffer.peek(buffer.getIndex(),l));
120 buffer.skip(l);
121 }
122
123
124 while(_endp.isOpen())
125 {
126
127 if (_hixieBytes.length()==8)
128 {
129
130
131 doTheHixieHixieShake();
132 _endp.flush(_hixieBytes);
133 _hixieBytes=null;
134 _endp.flush();
135 break;
136 }
137
138
139 int filled=_endp.fill(_hixieBytes);
140 if (filled<0)
141 {
142 _endp.close();
143 break;
144 }
145 }
146
147 _websocket.onConnect(this);
148 return this;
149 }
150
151
152 boolean progress=true;
153
154 while (progress)
155 {
156 int flushed=_generator.flush();
157 int filled=_parser.parseNext();
158
159 progress = flushed>0 || filled>0;
160
161 if (filled<0 || flushed<0)
162 {
163 _endp.close();
164 break;
165 }
166 }
167 }
168 catch(IOException e)
169 {
170 try
171 {
172 _endp.close();
173 }
174 catch(IOException e2)
175 {
176 Log.ignore(e2);
177 }
178 throw e;
179 }
180 finally
181 {
182 if (_endp.isOpen())
183 {
184 _idle.access(_endp);
185 checkWriteable();
186 }
187 }
188 return this;
189 }
190
191 private void doTheHixieHixieShake()
192 {
193 byte[] result=WebSocketConnectionD00.doTheHixieHixieShake(
194 WebSocketConnectionD00.hixieCrypt(_key1),
195 WebSocketConnectionD00.hixieCrypt(_key2),
196 _hixieBytes.asArray());
197 _hixieBytes.clear();
198 _hixieBytes.put(result);
199 }
200
201 public boolean isOpen()
202 {
203 return _endp!=null&&_endp.isOpen();
204 }
205
206 public boolean isIdle()
207 {
208 return _parser.isBufferEmpty() && _generator.isBufferEmpty();
209 }
210
211 public boolean isSuspended()
212 {
213 return false;
214 }
215
216 public void closed()
217 {
218 _websocket.onDisconnect();
219 }
220
221
222
223
224
225 public void sendMessage(String content) throws IOException
226 {
227 sendMessage(WebSocket.SENTINEL_FRAME,content);
228 }
229
230
231
232
233
234 public void sendMessage(byte frame, String content) throws IOException
235 {
236 _generator.addFrame(frame,content,_endp.getMaxIdleTime());
237 _generator.flush();
238 checkWriteable();
239 _idle.access(_endp);
240 }
241
242
243
244
245
246 public void sendMessage(byte opcode, byte[] content, int offset, int length) throws IOException
247 {
248 _generator.addFrame(opcode,content,offset,length,_endp.getMaxIdleTime());
249 _generator.flush();
250 checkWriteable();
251 _idle.access(_endp);
252 }
253
254
255
256
257
258 public void sendFragment(boolean more,byte opcode, byte[] content, int offset, int length) throws IOException
259 {
260 _generator.addFragment(!more,opcode,content,offset,length,_endp.getMaxIdleTime());
261 _generator.flush();
262 checkWriteable();
263 _idle.access(_endp);
264 }
265
266 public void disconnect(int code, String message)
267 {
268 throw new UnsupportedOperationException();
269 }
270
271 public void disconnect()
272 {
273 try
274 {
275 _generator.flush(_endp.getMaxIdleTime());
276 _endp.close();
277 }
278 catch(IOException e)
279 {
280 Log.ignore(e);
281 }
282 }
283
284 public void fillBuffersFrom(Buffer buffer)
285 {
286 _parser.fill(buffer);
287 }
288
289
290 private void checkWriteable()
291 {
292 if (!_generator.isBufferEmpty() && _endp instanceof AsyncEndPoint)
293 ((AsyncEndPoint)_endp).scheduleWrite();
294 }
295
296
297 static long hixieCrypt(String key)
298 {
299
300
301
302 long number=0;
303 int spaces=0;
304 for (char c : key.toCharArray())
305 {
306 if (Character.isDigit(c))
307 number=number*10+(c-'0');
308 else if (c==' ')
309 spaces++;
310 }
311 return number/spaces;
312 }
313
314 public static byte[] doTheHixieHixieShake(long key1,long key2,byte[] key3)
315 {
316 try
317 {
318 MessageDigest md = MessageDigest.getInstance("MD5");
319 byte [] fodder = new byte[16];
320
321 fodder[0]=(byte)(0xff&(key1>>24));
322 fodder[1]=(byte)(0xff&(key1>>16));
323 fodder[2]=(byte)(0xff&(key1>>8));
324 fodder[3]=(byte)(0xff&key1);
325 fodder[4]=(byte)(0xff&(key2>>24));
326 fodder[5]=(byte)(0xff&(key2>>16));
327 fodder[6]=(byte)(0xff&(key2>>8));
328 fodder[7]=(byte)(0xff&key2);
329 for (int i=0;i<8;i++)
330 fodder[8+i]=key3[i];
331 md.update(fodder);
332 byte[] result=md.digest();
333 return result;
334 }
335 catch (NoSuchAlgorithmException e)
336 {
337 throw new IllegalStateException(e);
338 }
339 }
340
341 private interface IdleCheck
342 {
343 void access(EndPoint endp);
344 }
345
346 public void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException
347 {
348 String uri=request.getRequestURI();
349 String query=request.getQueryString();
350 if (query!=null && query.length()>0)
351 uri+="?"+query;
352 String host=request.getHeader("Host");
353
354 String key1 = request.getHeader("Sec-WebSocket-Key1");
355 if (key1!=null)
356 {
357 String key2 = request.getHeader("Sec-WebSocket-Key2");
358 setHixieKeys(key1,key2);
359
360 response.setHeader("Upgrade","WebSocket");
361 response.addHeader("Connection","Upgrade");
362 response.addHeader("Sec-WebSocket-Origin",origin);
363 response.addHeader("Sec-WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
364 if (subprotocol!=null)
365 response.addHeader("Sec-WebSocket-Protocol",subprotocol);
366 response.sendError(101,"WebSocket Protocol Handshake");
367 }
368 else
369 {
370 response.setHeader("Upgrade","WebSocket");
371 response.addHeader("Connection","Upgrade");
372 response.addHeader("WebSocket-Origin",origin);
373 response.addHeader("WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
374 if (subprotocol!=null)
375 response.addHeader("WebSocket-Protocol",subprotocol);
376 response.sendError(101,"Web Socket Protocol Handshake");
377 response.flushBuffer();
378 _websocket.onConnect(this);
379 }
380 }
381
382 }