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