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 import java.util.Collections;
20 import java.util.List;
21
22 import javax.servlet.http.HttpServletRequest;
23 import javax.servlet.http.HttpServletResponse;
24
25 import org.eclipse.jetty.io.AbstractConnection;
26 import org.eclipse.jetty.io.AsyncEndPoint;
27 import org.eclipse.jetty.io.Buffer;
28 import org.eclipse.jetty.io.ByteArrayBuffer;
29 import org.eclipse.jetty.io.Connection;
30 import org.eclipse.jetty.io.EndPoint;
31 import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
32 import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
33 import org.eclipse.jetty.util.StringUtil;
34 import org.eclipse.jetty.util.Utf8StringBuilder;
35 import org.eclipse.jetty.util.log.Log;
36 import org.eclipse.jetty.websocket.WebSocket.OnFrame;
37
38 public class WebSocketConnectionD00 extends AbstractConnection implements WebSocketConnection, WebSocket.FrameConnection
39 {
40 public final static byte LENGTH_FRAME=(byte)0x80;
41 public final static byte SENTINEL_FRAME=(byte)0x00;
42
43 final IdleCheck _idle;
44 final WebSocketParser _parser;
45 final WebSocketGenerator _generator;
46 final WebSocket _websocket;
47 final String _protocol;
48 String _key1;
49 String _key2;
50 ByteArrayBuffer _hixieBytes;
51
52 public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
53 throws IOException
54 {
55 super(endpoint,timestamp);
56 if (endpoint instanceof AsyncEndPoint)
57 ((AsyncEndPoint)endpoint).cancelIdle();
58
59 _endp.setMaxIdleTime(maxIdleTime);
60
61 _websocket = websocket;
62 _protocol=protocol;
63
64 _generator = new WebSocketGeneratorD00(buffers, _endp);
65 _parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD0(_websocket));
66
67 if (_endp instanceof SelectChannelEndPoint)
68 {
69 final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp;
70 scep.cancelIdle();
71 _idle=new IdleCheck()
72 {
73 public void access(EndPoint endp)
74 {
75 scep.scheduleIdle();
76 }
77 };
78 scep.scheduleIdle();
79 }
80 else
81 {
82 _idle = new IdleCheck()
83 {
84 public void access(EndPoint endp)
85 {}
86 };
87 }
88 }
89
90
91 public org.eclipse.jetty.websocket.WebSocket.Connection getConnection()
92 {
93 return this;
94 }
95
96
97
98 public void setHixieKeys(String key1,String key2)
99 {
100 _key1=key1;
101 _key2=key2;
102 _hixieBytes=new IndirectNIOBuffer(16);
103 }
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 if (_websocket instanceof OnFrame)
150 ((OnFrame)_websocket).onHandshake(this);
151 _websocket.onOpen(this);
152 return this;
153 }
154
155
156 boolean progress=true;
157
158 while (progress)
159 {
160 int flushed=_generator.flush();
161 int filled=_parser.parseNext();
162
163 progress = flushed>0 || filled>0;
164
165 if (filled<0 || flushed<0)
166 {
167 _endp.close();
168 break;
169 }
170 }
171 }
172 catch(IOException e)
173 {
174 Log.debug(e);
175 try
176 {
177 _endp.close();
178 }
179 catch(IOException e2)
180 {
181 Log.ignore(e2);
182 }
183 throw e;
184 }
185 finally
186 {
187 if (_endp.isOpen())
188 {
189 _idle.access(_endp);
190
191 if (_endp.isInputShutdown() && _generator.isBufferEmpty())
192 _endp.close();
193 else
194 checkWriteable();
195
196 checkWriteable();
197 }
198 }
199 return this;
200 }
201
202
203 private void doTheHixieHixieShake()
204 {
205 byte[] result=WebSocketConnectionD00.doTheHixieHixieShake(
206 WebSocketConnectionD00.hixieCrypt(_key1),
207 WebSocketConnectionD00.hixieCrypt(_key2),
208 _hixieBytes.asArray());
209 _hixieBytes.clear();
210 _hixieBytes.put(result);
211 }
212
213
214 public boolean isOpen()
215 {
216 return _endp!=null&&_endp.isOpen();
217 }
218
219
220 public boolean isIdle()
221 {
222 return _parser.isBufferEmpty() && _generator.isBufferEmpty();
223 }
224
225
226 public boolean isSuspended()
227 {
228 return false;
229 }
230
231
232 public void closed()
233 {
234 _websocket.onClose(WebSocketConnectionD06.CLOSE_NORMAL,"");
235 }
236
237
238
239
240 public void sendMessage(String content) throws IOException
241 {
242 byte[] data = content.getBytes(StringUtil.__UTF8);
243 _generator.addFrame((byte)0,SENTINEL_FRAME,data,0,data.length);
244 _generator.flush();
245 checkWriteable();
246 _idle.access(_endp);
247 }
248
249
250 public void sendMessage(byte[] data, int offset, int length) throws IOException
251 {
252 _generator.addFrame((byte)0,LENGTH_FRAME,data,offset,length);
253 _generator.flush();
254 checkWriteable();
255 _idle.access(_endp);
256 }
257
258
259 public boolean isMore(byte flags)
260 {
261 return (flags&0x8) != 0;
262 }
263
264
265
266
267
268 public void sendControl(byte code, byte[] content, int offset, int length) throws IOException
269 {
270 }
271
272
273 public void sendFrame(byte flags,byte opcode, byte[] content, int offset, int length) throws IOException
274 {
275 _generator.addFrame((byte)0,opcode,content,offset,length);
276 _generator.flush();
277 checkWriteable();
278 _idle.access(_endp);
279 }
280
281
282 public void close(int code, String message)
283 {
284 throw new UnsupportedOperationException();
285 }
286
287
288 public void disconnect()
289 {
290 try
291 {
292 _generator.flush();
293 _endp.close();
294 }
295 catch(IOException e)
296 {
297 Log.ignore(e);
298 }
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 for (int i=0;i<8;i++)
349 fodder[8+i]=key3[i];
350 md.update(fodder);
351 byte[] result=md.digest();
352 return result;
353 }
354 catch (NoSuchAlgorithmException e)
355 {
356 throw new IllegalStateException(e);
357 }
358 }
359
360 private interface IdleCheck
361 {
362 void access(EndPoint endp);
363 }
364
365 public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
366 {
367 String uri=request.getRequestURI();
368 String query=request.getQueryString();
369 if (query!=null && query.length()>0)
370 uri+="?"+query;
371 String host=request.getHeader("Host");
372
373 String origin=request.getHeader("Host");
374 String key1 = request.getHeader("Sec-WebSocket-Key1");
375
376 if (key1!=null)
377 {
378 String key2 = request.getHeader("Sec-WebSocket-Key2");
379 setHixieKeys(key1,key2);
380
381 response.setHeader("Upgrade","WebSocket");
382 response.addHeader("Connection","Upgrade");
383 response.addHeader("Sec-WebSocket-Origin",origin);
384 response.addHeader("Sec-WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
385 if (subprotocol!=null)
386 response.addHeader("Sec-WebSocket-Protocol",subprotocol);
387 response.sendError(101,"WebSocket Protocol Handshake");
388 }
389 else
390 {
391 response.setHeader("Upgrade","WebSocket");
392 response.addHeader("Connection","Upgrade");
393 response.addHeader("WebSocket-Origin",origin);
394 response.addHeader("WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
395 if (subprotocol!=null)
396 response.addHeader("WebSocket-Protocol",subprotocol);
397 response.sendError(101,"Web Socket Protocol Handshake");
398 response.flushBuffer();
399 if (_websocket instanceof OnFrame)
400 ((OnFrame)_websocket).onHandshake(this);
401 _websocket.onOpen(this);
402 }
403 }
404
405 public void setMaxTextMessageSize(int size)
406 {
407 }
408
409 public void setMaxBinaryMessageSize(int size)
410 {
411 }
412
413 public int getMaxTextMessageSize()
414 {
415 return -1;
416 }
417
418 public int getMaxBinaryMessageSize()
419 {
420 return -1;
421 }
422
423 public String getProtocol()
424 {
425 return _protocol;
426 }
427
428 class FrameHandlerD0 implements WebSocketParser.FrameHandler
429 {
430 final WebSocket _websocket;
431 final Utf8StringBuilder _utf8 = new Utf8StringBuilder();
432
433 FrameHandlerD0(WebSocket websocket)
434 {
435 _websocket=websocket;
436 }
437
438 public void onFrame(byte flags, byte opcode, Buffer buffer)
439 {
440 try
441 {
442 byte[] array=buffer.array();
443
444 if (opcode==0)
445 {
446 if (_websocket instanceof WebSocket.OnTextMessage)
447 ((WebSocket.OnTextMessage)_websocket).onMessage(buffer.toString(StringUtil.__UTF8));
448 }
449 else
450 {
451 if (_websocket instanceof WebSocket.OnBinaryMessage)
452 ((WebSocket.OnBinaryMessage)_websocket).onMessage(array,buffer.getIndex(),buffer.length());
453 }
454 }
455 catch(ThreadDeath th)
456 {
457 throw th;
458 }
459 catch(Throwable th)
460 {
461 Log.warn(th);
462 }
463 }
464
465 public void close(int code,String message)
466 {
467 close(code,message);
468 }
469 }
470
471 public boolean isMessageComplete(byte flags)
472 {
473 return true;
474 }
475
476 public byte binaryOpcode()
477 {
478 return LENGTH_FRAME;
479 }
480
481 public byte textOpcode()
482 {
483 return SENTINEL_FRAME;
484 }
485
486 public boolean isControl(byte opcode)
487 {
488 return false;
489 }
490
491 public boolean isText(byte opcode)
492 {
493 return (opcode&LENGTH_FRAME)==0;
494 }
495
496 public boolean isBinary(byte opcode)
497 {
498 return (opcode&LENGTH_FRAME)!=0;
499 }
500
501 public boolean isContinuation(byte opcode)
502 {
503 return false;
504 }
505
506 public boolean isClose(byte opcode)
507 {
508 return false;
509 }
510
511 public boolean isPing(byte opcode)
512 {
513 return false;
514 }
515
516 public boolean isPong(byte opcode)
517 {
518 return false;
519 }
520
521 public List<Extension> getExtensions()
522 {
523 return Collections.emptyList();
524 }
525
526 public byte continuationOpcode()
527 {
528 return 0;
529 }
530
531 public byte finMask()
532 {
533 return 0;
534 }
535
536 public void setFakeFragments(boolean fake)
537 {
538
539
540 }
541
542 public boolean isFakeFragments()
543 {
544
545 return false;
546 }
547 }