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.StringUtil;
32 import org.eclipse.jetty.util.Utf8StringBuilder;
33 import org.eclipse.jetty.util.log.Log;
34
35 public class WebSocketConnectionD00 extends AbstractConnection implements WebSocketConnection, WebSocket.Connection
36 {
37 public final static byte LENGTH_FRAME=(byte)0x80;
38 public final static byte SENTINEL_FRAME=(byte)0x00;
39
40
41 final IdleCheck _idle;
42 final WebSocketParser _parser;
43 final WebSocketGenerator _generator;
44 final WebSocket _websocket;
45 final String _protocol;
46 String _key1;
47 String _key2;
48 ByteArrayBuffer _hixieBytes;
49
50 public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, int draft)
51 throws IOException
52 {
53 super(endpoint,timestamp);
54
55 if (endpoint instanceof AsyncEndPoint)
56 ((AsyncEndPoint)endpoint).cancelIdle();
57
58 _endp.setMaxIdleTime(maxIdleTime);
59
60 _websocket = websocket;
61 _protocol=protocol;
62
63
64 switch(draft)
65 {
66 case 1:
67 _generator = new WebSocketGeneratorD01(buffers, _endp);
68 _parser = new WebSocketParserD01(buffers, endpoint, new FrameHandlerD1(_websocket));
69 break;
70 default:
71 _generator = new WebSocketGeneratorD00(buffers, _endp);
72 _parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD0(_websocket));
73 }
74
75
76 if (_endp instanceof SelectChannelEndPoint)
77 {
78 final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp;
79 scep.cancelIdle();
80 _idle=new IdleCheck()
81 {
82 public void access(EndPoint endp)
83 {
84 scep.scheduleIdle();
85 }
86 };
87 scep.scheduleIdle();
88 }
89 else
90 {
91 _idle = new IdleCheck()
92 {
93 public void access(EndPoint endp)
94 {}
95 };
96 }
97 }
98
99 public void setHixieKeys(String key1,String key2)
100 {
101 _key1=key1;
102 _key2=key2;
103 _hixieBytes=new IndirectNIOBuffer(16);
104 }
105
106 public Connection handle() throws IOException
107 {
108 try
109 {
110
111 if (_hixieBytes!=null)
112 {
113
114
115 Buffer buffer=_parser.getBuffer();
116 if (buffer!=null && buffer.length()>0)
117 {
118 int l=buffer.length();
119 if (l>(8-_hixieBytes.length()))
120 l=8-_hixieBytes.length();
121 _hixieBytes.put(buffer.peek(buffer.getIndex(),l));
122 buffer.skip(l);
123 }
124
125
126 while(_endp.isOpen())
127 {
128
129 if (_hixieBytes.length()==8)
130 {
131
132
133 doTheHixieHixieShake();
134 _endp.flush(_hixieBytes);
135 _hixieBytes=null;
136 _endp.flush();
137 break;
138 }
139
140
141 int filled=_endp.fill(_hixieBytes);
142 if (filled<0)
143 {
144 _endp.close();
145 break;
146 }
147 }
148
149 _websocket.onConnect(this);
150 return this;
151 }
152
153
154 boolean progress=true;
155
156 while (progress)
157 {
158 int flushed=_generator.flush();
159 int filled=_parser.parseNext();
160
161 progress = flushed>0 || filled>0;
162
163 if (filled<0 || flushed<0)
164 {
165 _endp.close();
166 break;
167 }
168 }
169 }
170 catch(IOException e)
171 {
172 try
173 {
174 _endp.close();
175 }
176 catch(IOException e2)
177 {
178 Log.ignore(e2);
179 }
180 throw e;
181 }
182 finally
183 {
184 if (_endp.isOpen())
185 {
186 _idle.access(_endp);
187 checkWriteable();
188 }
189 }
190 return this;
191 }
192
193 private void doTheHixieHixieShake()
194 {
195 byte[] result=WebSocketConnectionD00.doTheHixieHixieShake(
196 WebSocketConnectionD00.hixieCrypt(_key1),
197 WebSocketConnectionD00.hixieCrypt(_key2),
198 _hixieBytes.asArray());
199 _hixieBytes.clear();
200 _hixieBytes.put(result);
201 }
202
203 public boolean isOpen()
204 {
205 return _endp!=null&&_endp.isOpen();
206 }
207
208 public boolean isIdle()
209 {
210 return _parser.isBufferEmpty() && _generator.isBufferEmpty();
211 }
212
213 public boolean isSuspended()
214 {
215 return false;
216 }
217
218 public void closed()
219 {
220 _websocket.onDisconnect(0,"");
221 }
222
223
224
225
226 public void sendMessage(String content) throws IOException
227 {
228 byte[] data = content.getBytes(StringUtil.__UTF8);
229 _generator.addFrame((byte)0,SENTINEL_FRAME,data,0,data.length,_endp.getMaxIdleTime());
230 _generator.flush();
231 checkWriteable();
232 _idle.access(_endp);
233 }
234
235
236 public void sendMessage(byte[] data, int offset, int length) throws IOException
237 {
238 _generator.addFrame((byte)0,LENGTH_FRAME,data,offset,length,_endp.getMaxIdleTime());
239 _generator.flush();
240 checkWriteable();
241 _idle.access(_endp);
242 }
243
244
245 public boolean isMore(byte flags)
246 {
247 return (flags&0x8) != 0;
248 }
249
250
251
252
253
254 public void sendControl(byte code, byte[] content, int offset, int length) throws IOException
255 {
256 }
257
258
259 public void sendFrame(byte flags,byte opcode, byte[] content, int offset, int length) throws IOException
260 {
261 _generator.addFrame((byte)0,opcode,content,offset,length,_endp.getMaxIdleTime());
262 _generator.flush();
263 checkWriteable();
264 _idle.access(_endp);
265 }
266
267
268 public void disconnect(int code, String message)
269 {
270 throw new UnsupportedOperationException();
271 }
272
273
274 public void disconnect()
275 {
276 try
277 {
278 _generator.flush(_endp.getMaxIdleTime());
279 _endp.close();
280 }
281 catch(IOException e)
282 {
283 Log.ignore(e);
284 }
285 }
286
287 public void fillBuffersFrom(Buffer buffer)
288 {
289 _parser.fill(buffer);
290 }
291
292
293 private void checkWriteable()
294 {
295 if (!_generator.isBufferEmpty() && _endp instanceof AsyncEndPoint)
296 ((AsyncEndPoint)_endp).scheduleWrite();
297 }
298
299
300 static long hixieCrypt(String key)
301 {
302
303
304
305 long number=0;
306 int spaces=0;
307 for (char c : key.toCharArray())
308 {
309 if (Character.isDigit(c))
310 number=number*10+(c-'0');
311 else if (c==' ')
312 spaces++;
313 }
314 return number/spaces;
315 }
316
317 public static byte[] doTheHixieHixieShake(long key1,long key2,byte[] key3)
318 {
319 try
320 {
321 MessageDigest md = MessageDigest.getInstance("MD5");
322 byte [] fodder = new byte[16];
323
324 fodder[0]=(byte)(0xff&(key1>>24));
325 fodder[1]=(byte)(0xff&(key1>>16));
326 fodder[2]=(byte)(0xff&(key1>>8));
327 fodder[3]=(byte)(0xff&key1);
328 fodder[4]=(byte)(0xff&(key2>>24));
329 fodder[5]=(byte)(0xff&(key2>>16));
330 fodder[6]=(byte)(0xff&(key2>>8));
331 fodder[7]=(byte)(0xff&key2);
332 for (int i=0;i<8;i++)
333 fodder[8+i]=key3[i];
334 md.update(fodder);
335 byte[] result=md.digest();
336 return result;
337 }
338 catch (NoSuchAlgorithmException e)
339 {
340 throw new IllegalStateException(e);
341 }
342 }
343
344 private interface IdleCheck
345 {
346 void access(EndPoint endp);
347 }
348
349 public void handshake(HttpServletRequest request, HttpServletResponse response, String origin, String subprotocol) throws IOException
350 {
351 String uri=request.getRequestURI();
352 String query=request.getQueryString();
353 if (query!=null && query.length()>0)
354 uri+="?"+query;
355 String host=request.getHeader("Host");
356
357 String key1 = request.getHeader("Sec-WebSocket-Key1");
358 if (key1!=null)
359 {
360 String key2 = request.getHeader("Sec-WebSocket-Key2");
361 setHixieKeys(key1,key2);
362
363 response.setHeader("Upgrade","WebSocket");
364 response.addHeader("Connection","Upgrade");
365 response.addHeader("Sec-WebSocket-Origin",origin);
366 response.addHeader("Sec-WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
367 if (subprotocol!=null)
368 response.addHeader("Sec-WebSocket-Protocol",subprotocol);
369 response.sendError(101,"WebSocket Protocol Handshake");
370 }
371 else
372 {
373 response.setHeader("Upgrade","WebSocket");
374 response.addHeader("Connection","Upgrade");
375 response.addHeader("WebSocket-Origin",origin);
376 response.addHeader("WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
377 if (subprotocol!=null)
378 response.addHeader("WebSocket-Protocol",subprotocol);
379 response.sendError(101,"Web Socket Protocol Handshake");
380 response.flushBuffer();
381 _websocket.onConnect(this);
382 }
383 }
384
385 public void setMaxTextMessageSize(int size)
386 {
387 }
388
389 public void setMaxBinaryMessageSize(int size)
390 {
391 }
392
393 public int getMaxTextMessageSize()
394 {
395 return -1;
396 }
397
398 public int getMaxBinaryMessageSize()
399 {
400 return -1;
401 }
402
403 public String getProtocol()
404 {
405 return _protocol;
406 }
407
408 class FrameHandlerD0 implements WebSocketParser.FrameHandler
409 {
410 final WebSocket _websocket;
411 final Utf8StringBuilder _utf8 = new Utf8StringBuilder();
412
413 FrameHandlerD0(WebSocket websocket)
414 {
415 _websocket=websocket;
416 }
417
418 public void onFrame(byte flags, byte opcode, Buffer buffer)
419 {
420 try
421 {
422 byte[] array=buffer.array();
423
424 if (opcode==0)
425 {
426 if (_websocket instanceof WebSocket.OnTextMessage)
427 ((WebSocket.OnTextMessage)_websocket).onMessage(buffer.toString(StringUtil.__UTF8));
428 }
429 else
430 {
431 if (_websocket instanceof WebSocket.OnBinaryMessage)
432 ((WebSocket.OnBinaryMessage)_websocket).onMessage(array,buffer.getIndex(),buffer.length());
433 }
434 }
435 catch(ThreadDeath th)
436 {
437 throw th;
438 }
439 catch(Throwable th)
440 {
441 Log.warn(th);
442 }
443 }
444
445 public void close(int code,String message)
446 {
447 disconnect(code,message);
448 }
449 }
450
451 class FrameHandlerD1 implements WebSocketParser.FrameHandler
452 {
453 public final static byte PING=1;
454 public final static byte PONG=1;
455
456 final WebSocket _websocket;
457 final Utf8StringBuilder _utf8 = new Utf8StringBuilder();
458 boolean _fragmented=false;
459
460 FrameHandlerD1(WebSocket websocket)
461 {
462 _websocket=websocket;
463 }
464
465 public void onFrame(byte flags, byte opcode, Buffer buffer)
466 {
467 try
468 {
469 byte[] array=buffer.array();
470
471 if (opcode==0)
472 {
473 if (isMore(flags))
474 {
475 _utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
476 _fragmented=true;
477 }
478 else if (_fragmented)
479 {
480 _utf8.append(buffer.array(),buffer.getIndex(),buffer.length());
481 if (_websocket instanceof WebSocket.OnTextMessage)
482 ((WebSocket.OnTextMessage)_websocket).onMessage(_utf8.toString());
483 _utf8.reset();
484 _fragmented=false;
485 }
486 else
487 {
488 if (_websocket instanceof WebSocket.OnTextMessage)
489 ((WebSocket.OnTextMessage)_websocket).onMessage(buffer.toString(StringUtil.__UTF8));
490 }
491 }
492 else if (opcode==PING)
493 {
494 sendFrame(flags,PONG,buffer.array(),buffer.getIndex(),buffer.length());
495 }
496 else if (opcode==PONG)
497 {
498
499 }
500 else
501 {
502 if (isMore(flags))
503 {
504 if (_websocket instanceof WebSocket.OnFrame)
505 ((WebSocket.OnFrame)_websocket).onFrame(flags,opcode,array,buffer.getIndex(),buffer.length());
506 }
507 else if (_fragmented)
508 {
509 if (_websocket instanceof WebSocket.OnFrame)
510 ((WebSocket.OnFrame)_websocket).onFrame(flags,opcode,array,buffer.getIndex(),buffer.length());
511 }
512 else
513 {
514 if (_websocket instanceof WebSocket.OnBinaryMessage)
515 ((WebSocket.OnBinaryMessage)_websocket).onMessage(array,buffer.getIndex(),buffer.length());
516 }
517 }
518 }
519 catch(ThreadDeath th)
520 {
521 throw th;
522 }
523 catch(Throwable th)
524 {
525 Log.warn(th);
526 }
527 }
528
529 public void close(int code,String message)
530 {
531 disconnect(code,message);
532 }
533 }
534
535 }