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.close();
133 break;
134 }
135 }
136
137 if (_websocket instanceof OnFrame)
138 ((OnFrame)_websocket).onHandshake(this);
139 _websocket.onOpen(this);
140 return this;
141 }
142
143
144 boolean progress=true;
145
146 while (progress)
147 {
148 int flushed=_generator.flush();
149 int filled=_parser.parseNext();
150
151 progress = flushed>0 || filled>0;
152
153 _endp.flush();
154
155 if (_endp instanceof AsyncEndPoint && ((AsyncEndPoint)_endp).hasProgressed())
156 progress=true;
157 }
158 }
159 catch(IOException e)
160 {
161 LOG.debug(e);
162 try
163 {
164 if (_endp.isOpen())
165 _endp.close();
166 }
167 catch(IOException e2)
168 {
169 LOG.ignore(e2);
170 }
171 throw e;
172 }
173 finally
174 {
175 if (_endp.isOpen())
176 {
177 if (_endp.isInputShutdown() && _generator.isBufferEmpty())
178 _endp.close();
179 else
180 checkWriteable();
181
182 checkWriteable();
183 }
184 }
185 return this;
186 }
187
188
189 public void onInputShutdown() throws IOException
190 {
191
192 }
193
194
195 private void doTheHixieHixieShake()
196 {
197 byte[] result=WebSocketConnectionD00.doTheHixieHixieShake(
198 WebSocketConnectionD00.hixieCrypt(_key1),
199 WebSocketConnectionD00.hixieCrypt(_key2),
200 _hixieBytes.asArray());
201 _hixieBytes.clear();
202 _hixieBytes.put(result);
203 }
204
205
206 public boolean isOpen()
207 {
208 return _endp!=null&&_endp.isOpen();
209 }
210
211
212 public boolean isIdle()
213 {
214 return _parser.isBufferEmpty() && _generator.isBufferEmpty();
215 }
216
217
218 public boolean isSuspended()
219 {
220 return false;
221 }
222
223
224 public void onClose()
225 {
226 _websocket.onClose(WebSocketConnectionD06.CLOSE_NORMAL,"");
227 }
228
229
230
231
232 public void sendMessage(String content) throws IOException
233 {
234 byte[] data = content.getBytes(StringUtil.__UTF8);
235 _generator.addFrame((byte)0,SENTINEL_FRAME,data,0,data.length);
236 _generator.flush();
237 checkWriteable();
238 }
239
240
241 public void sendMessage(byte[] data, int offset, int length) throws IOException
242 {
243 _generator.addFrame((byte)0,LENGTH_FRAME,data,offset,length);
244 _generator.flush();
245 checkWriteable();
246 }
247
248
249 public boolean isMore(byte flags)
250 {
251 return (flags&0x8) != 0;
252 }
253
254
255
256
257
258 public void sendControl(byte code, byte[] content, int offset, int length) throws IOException
259 {
260 }
261
262
263 public void sendFrame(byte flags,byte opcode, byte[] content, int offset, int length) throws IOException
264 {
265 _generator.addFrame((byte)0,opcode,content,offset,length);
266 _generator.flush();
267 checkWriteable();
268 }
269
270
271 public void close(int code, String message)
272 {
273 throw new UnsupportedOperationException();
274 }
275
276
277 public void disconnect()
278 {
279 close();
280 }
281
282
283 public void close()
284 {
285 try
286 {
287 _generator.flush();
288 _endp.close();
289 }
290 catch(IOException e)
291 {
292 LOG.ignore(e);
293 }
294 }
295
296 public void shutdown()
297 {
298 close();
299 }
300
301
302 public void fillBuffersFrom(Buffer buffer)
303 {
304 _parser.fill(buffer);
305 }
306
307
308
309 private void checkWriteable()
310 {
311 if (!_generator.isBufferEmpty() && _endp instanceof AsyncEndPoint)
312 ((AsyncEndPoint)_endp).scheduleWrite();
313 }
314
315
316 static long hixieCrypt(String key)
317 {
318
319
320
321 long number=0;
322 int spaces=0;
323 for (char c : key.toCharArray())
324 {
325 if (Character.isDigit(c))
326 number=number*10+(c-'0');
327 else if (c==' ')
328 spaces++;
329 }
330 return number/spaces;
331 }
332
333 public static byte[] doTheHixieHixieShake(long key1,long key2,byte[] key3)
334 {
335 try
336 {
337 MessageDigest md = MessageDigest.getInstance("MD5");
338 byte [] fodder = new byte[16];
339
340 fodder[0]=(byte)(0xff&(key1>>24));
341 fodder[1]=(byte)(0xff&(key1>>16));
342 fodder[2]=(byte)(0xff&(key1>>8));
343 fodder[3]=(byte)(0xff&key1);
344 fodder[4]=(byte)(0xff&(key2>>24));
345 fodder[5]=(byte)(0xff&(key2>>16));
346 fodder[6]=(byte)(0xff&(key2>>8));
347 fodder[7]=(byte)(0xff&key2);
348 System.arraycopy(key3, 0, fodder, 8, 8);
349 md.update(fodder);
350 return md.digest();
351 }
352 catch (NoSuchAlgorithmException e)
353 {
354 throw new IllegalStateException(e);
355 }
356 }
357
358 public void setMaxTextMessageSize(int size)
359 {
360 }
361
362 public void setMaxIdleTime(int ms)
363 {
364 try
365 {
366 _endp.setMaxIdleTime(ms);
367 }
368 catch(IOException e)
369 {
370 LOG.warn(e);
371 }
372 }
373
374 public void setMaxBinaryMessageSize(int size)
375 {
376 }
377
378 public int getMaxTextMessageSize()
379 {
380 return -1;
381 }
382
383 public int getMaxIdleTime()
384 {
385 return _endp.getMaxIdleTime();
386 }
387
388 public int getMaxBinaryMessageSize()
389 {
390 return -1;
391 }
392
393 public String getProtocol()
394 {
395 return _protocol;
396 }
397
398 protected void onFrameHandshake()
399 {
400 if (_websocket instanceof OnFrame)
401 {
402 ((OnFrame)_websocket).onHandshake(this);
403 }
404 }
405
406 protected void onWebsocketOpen()
407 {
408 _websocket.onOpen(this);
409 }
410
411 static class FrameHandlerD00 implements WebSocketParser.FrameHandler
412 {
413 final WebSocket _websocket;
414
415 FrameHandlerD00(WebSocket websocket)
416 {
417 _websocket=websocket;
418 }
419
420 public void onFrame(byte flags, byte opcode, Buffer buffer)
421 {
422 try
423 {
424 byte[] array=buffer.array();
425
426 if (opcode==0)
427 {
428 if (_websocket instanceof WebSocket.OnTextMessage)
429 ((WebSocket.OnTextMessage)_websocket).onMessage(buffer.toString(StringUtil.__UTF8));
430 }
431 else
432 {
433 if (_websocket instanceof WebSocket.OnBinaryMessage)
434 ((WebSocket.OnBinaryMessage)_websocket).onMessage(array,buffer.getIndex(),buffer.length());
435 }
436 }
437 catch(Throwable th)
438 {
439 LOG.warn(th);
440 }
441 }
442
443 public void close(int code,String message)
444 {
445 }
446 }
447
448 public boolean isMessageComplete(byte flags)
449 {
450 return true;
451 }
452
453 public byte binaryOpcode()
454 {
455 return LENGTH_FRAME;
456 }
457
458 public byte textOpcode()
459 {
460 return SENTINEL_FRAME;
461 }
462
463 public boolean isControl(byte opcode)
464 {
465 return false;
466 }
467
468 public boolean isText(byte opcode)
469 {
470 return (opcode&LENGTH_FRAME)==0;
471 }
472
473 public boolean isBinary(byte opcode)
474 {
475 return (opcode&LENGTH_FRAME)!=0;
476 }
477
478 public boolean isContinuation(byte opcode)
479 {
480 return false;
481 }
482
483 public boolean isClose(byte opcode)
484 {
485 return false;
486 }
487
488 public boolean isPing(byte opcode)
489 {
490 return false;
491 }
492
493 public boolean isPong(byte opcode)
494 {
495 return false;
496 }
497
498 public List<Extension> getExtensions()
499 {
500 return Collections.emptyList();
501 }
502
503 public byte continuationOpcode()
504 {
505 return 0;
506 }
507
508 public byte finMask()
509 {
510 return 0;
511 }
512
513 public void setAllowFrameFragmentation(boolean allowFragmentation)
514 {
515 }
516
517 public boolean isAllowFrameFragmentation()
518 {
519 return false;
520 }
521 }