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.io.UnsupportedEncodingException;
18 import java.security.MessageDigest;
19 import java.util.Collections;
20 import java.util.List;
21 import javax.servlet.http.HttpServletRequest;
22 import javax.servlet.http.HttpServletResponse;
23
24 import org.eclipse.jetty.io.AbstractConnection;
25 import org.eclipse.jetty.io.AsyncEndPoint;
26 import org.eclipse.jetty.io.Buffer;
27 import org.eclipse.jetty.io.ByteArrayBuffer;
28 import org.eclipse.jetty.io.Connection;
29 import org.eclipse.jetty.io.EndPoint;
30 import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
31 import org.eclipse.jetty.util.B64Code;
32 import org.eclipse.jetty.util.StringUtil;
33 import org.eclipse.jetty.util.TypeUtil;
34 import org.eclipse.jetty.util.Utf8Appendable;
35 import org.eclipse.jetty.util.Utf8StringBuilder;
36 import org.eclipse.jetty.util.log.Log;
37 import org.eclipse.jetty.util.log.Logger;
38 import org.eclipse.jetty.websocket.WebSocket.OnBinaryMessage;
39 import org.eclipse.jetty.websocket.WebSocket.OnControl;
40 import org.eclipse.jetty.websocket.WebSocket.OnFrame;
41 import org.eclipse.jetty.websocket.WebSocket.OnTextMessage;
42
43 public class WebSocketConnectionD13 extends AbstractConnection implements WebSocketConnection
44 {
45 private static final Logger LOG = Log.getLogger(WebSocketConnectionD13.class);
46 private static final boolean STRICT=Boolean.getBoolean("org.eclipse.jetty.websocket.STRICT");
47 private static final boolean BRUTAL=Boolean.getBoolean("org.eclipse.jetty.websocket.BRUTAL");
48
49 final static byte OP_CONTINUATION = 0x00;
50 final static byte OP_TEXT = 0x01;
51 final static byte OP_BINARY = 0x02;
52 final static byte OP_EXT_DATA = 0x03;
53
54 final static byte OP_CONTROL = 0x08;
55 final static byte OP_CLOSE = 0x08;
56 final static byte OP_PING = 0x09;
57 final static byte OP_PONG = 0x0A;
58 final static byte OP_EXT_CTRL = 0x0B;
59
60 final static int CLOSE_NORMAL=1000;
61 final static int CLOSE_SHUTDOWN=1001;
62 final static int CLOSE_PROTOCOL=1002;
63 final static int CLOSE_BAD_DATA=1003;
64 final static int CLOSE_UNDEFINED=1004;
65 final static int CLOSE_NO_CODE=1005;
66 final static int CLOSE_NO_CLOSE=1006;
67 final static int CLOSE_BAD_PAYLOAD=1007;
68 final static int CLOSE_POLICY_VIOLATION=1008;
69 final static int CLOSE_MESSAGE_TOO_LARGE=1009;
70 final static int CLOSE_REQUIRED_EXTENSION=1010;
71
72 final static int FLAG_FIN=0x8;
73
74 final static int VERSION=13;
75
76 static boolean isLastFrame(byte flags)
77 {
78 return (flags&FLAG_FIN)!=0;
79 }
80
81 static boolean isControlFrame(byte opcode)
82 {
83 return (opcode&OP_CONTROL)!=0;
84 }
85
86 private final static byte[] MAGIC;
87 private final IdleCheck _idle;
88 private final List<Extension> _extensions;
89 private final WebSocketParserD13 _parser;
90 private final WebSocketGeneratorD13 _generator;
91 private final WebSocketGenerator _outbound;
92 private final WebSocket _webSocket;
93 private final OnFrame _onFrame;
94 private final OnBinaryMessage _onBinaryMessage;
95 private final OnTextMessage _onTextMessage;
96 private final OnControl _onControl;
97 private final String _protocol;
98 private final int _draft;
99 private final ClassLoader _context;
100 private volatile int _closeCode;
101 private volatile String _closeMessage;
102 private volatile boolean _closedIn;
103 private volatile boolean _closedOut;
104 private int _maxTextMessageSize=-1;
105 private int _maxBinaryMessageSize=-1;
106
107 static
108 {
109 try
110 {
111 MAGIC="258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(StringUtil.__ISO_8859_1);
112 }
113 catch (UnsupportedEncodingException e)
114 {
115 throw new RuntimeException(e);
116 }
117 }
118
119 private final WebSocket.FrameConnection _connection = new WSFrameConnection();
120
121
122
123 public WebSocketConnectionD13(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List<Extension> extensions,int draft)
124 throws IOException
125 {
126 this(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft,null);
127 }
128
129
130 public WebSocketConnectionD13(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List<Extension> extensions,int draft, MaskGen maskgen)
131 throws IOException
132 {
133 super(endpoint,timestamp);
134
135 _context=Thread.currentThread().getContextClassLoader();
136
137 if (endpoint instanceof AsyncEndPoint)
138 ((AsyncEndPoint)endpoint).cancelIdle();
139
140 _draft=draft;
141 _endp.setMaxIdleTime(maxIdleTime);
142
143 _webSocket = websocket;
144 _onFrame=_webSocket instanceof OnFrame ? (OnFrame)_webSocket : null;
145 _onTextMessage=_webSocket instanceof OnTextMessage ? (OnTextMessage)_webSocket : null;
146 _onBinaryMessage=_webSocket instanceof OnBinaryMessage ? (OnBinaryMessage)_webSocket : null;
147 _onControl=_webSocket instanceof OnControl ? (OnControl)_webSocket : null;
148 _generator = new WebSocketGeneratorD13(buffers, _endp,maskgen);
149
150 _extensions=extensions;
151 WebSocketParser.FrameHandler frameHandler = new WSFrameHandler();
152 if (_extensions!=null)
153 {
154 int e=0;
155 for (Extension extension : _extensions)
156 {
157 extension.bind(
158 _connection,
159 e==extensions.size()-1? frameHandler :extensions.get(e+1),
160 e==0?_generator:extensions.get(e-1));
161 e++;
162 }
163 }
164
165 _outbound=(_extensions==null||_extensions.size()==0)?_generator:extensions.get(extensions.size()-1);
166 WebSocketParser.FrameHandler inbound = (_extensions == null || _extensions.size() == 0) ? frameHandler : extensions.get(0);
167
168 _parser = new WebSocketParserD13(buffers, endpoint, inbound,maskgen==null);
169
170 _protocol=protocol;
171
172
173 if (_endp instanceof SelectChannelEndPoint)
174 {
175 final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp;
176 scep.cancelIdle();
177 _idle=new IdleCheck()
178 {
179 public void access(EndPoint endp)
180 {
181 scep.scheduleIdle();
182 }
183 };
184 scep.scheduleIdle();
185 }
186 else
187 {
188 _idle = new IdleCheck()
189 {
190 public void access(EndPoint endp)
191 {}
192 };
193 }
194 }
195
196
197 public WebSocket.Connection getConnection()
198 {
199 return _connection;
200 }
201
202
203 public List<Extension> getExtensions()
204 {
205 if (_extensions==null)
206 return Collections.emptyList();
207
208 return _extensions;
209 }
210
211
212 public Connection handle() throws IOException
213 {
214 Thread current = Thread.currentThread();
215 ClassLoader oldcontext = current.getContextClassLoader();
216 current.setContextClassLoader(_context);
217 try
218 {
219
220 boolean progress=true;
221
222 while (progress)
223 {
224 int flushed=_generator.flushBuffer();
225 int filled=_parser.parseNext();
226
227 progress = flushed>0 || filled>0;
228
229 if (filled<0 || flushed<0)
230 {
231 _endp.close();
232 break;
233 }
234 }
235 }
236 catch(IOException e)
237 {
238 try
239 {
240 _endp.close();
241 }
242 catch(IOException e2)
243 {
244 LOG.ignore(e2);
245 }
246 throw e;
247 }
248 finally
249 {
250 current.setContextClassLoader(oldcontext);
251 _parser.returnBuffer();
252 _generator.returnBuffer();
253 if (_endp.isOpen())
254 {
255 _idle.access(_endp);
256 if (_closedIn && _closedOut && _outbound.isBufferEmpty())
257 _endp.close();
258 else if (_endp.isInputShutdown() && !_closedIn)
259 closeIn(CLOSE_NO_CLOSE,null);
260 else
261 checkWriteable();
262 }
263 }
264 return this;
265 }
266
267
268 public boolean isIdle()
269 {
270 return _parser.isBufferEmpty() && _outbound.isBufferEmpty();
271 }
272
273
274 @Override
275 public void idleExpired()
276 {
277 long idle = System.currentTimeMillis()-((SelectChannelEndPoint)_endp).getIdleTimestamp();
278 closeOut(WebSocketConnectionD13.CLOSE_NORMAL,"Idle for "+idle+"ms > "+_endp.getMaxIdleTime()+"ms");
279 }
280
281
282 public boolean isSuspended()
283 {
284 return false;
285 }
286
287
288 public void closed()
289 {
290 final boolean closed;
291 synchronized (this)
292 {
293 closed=_closeCode==0;
294 if (closed)
295 _closeCode=WebSocketConnectionD13.CLOSE_NO_CLOSE;
296 }
297 if (closed)
298 _webSocket.onClose(WebSocketConnectionD13.CLOSE_NO_CLOSE,"closed");
299 }
300
301
302 public void closeIn(int code,String message)
303 {
304 LOG.debug("ClosedIn {} {}",this,message);
305
306 final boolean close;
307 final boolean tell_app;
308 synchronized (this)
309 {
310 close=_closedOut;
311 _closedIn=true;
312 tell_app=_closeCode==0;
313 if (tell_app)
314 {
315 _closeCode=code;
316 _closeMessage=message;
317 }
318 }
319
320 try
321 {
322 if (tell_app)
323 _webSocket.onClose(code,message);
324 }
325 finally
326 {
327 try
328 {
329 if (close)
330 _endp.close();
331 else
332 closeOut(code,message);
333 }
334 catch(IOException e)
335 {
336 LOG.ignore(e);
337 }
338 }
339 }
340
341
342 public void closeOut(int code,String message)
343 {
344 LOG.debug("ClosedOut {} {}",this,message);
345
346 final boolean close;
347 final boolean tell_app;
348 final boolean send_close;
349 synchronized (this)
350 {
351 close=_closedIn;
352 send_close=!_closedOut;
353 _closedOut=true;
354 tell_app=_closeCode==0;
355 if (tell_app)
356 {
357 _closeCode=code;
358 _closeMessage=message;
359 }
360 }
361
362 try
363 {
364 if (tell_app)
365 _webSocket.onClose(code,message);
366 }
367 finally
368 {
369 try
370 {
371 if (send_close)
372 {
373 if (code<=0)
374 code=WebSocketConnectionD13.CLOSE_NORMAL;
375 byte[] bytes = ("xx"+(message==null?"":message)).getBytes(StringUtil.__ISO_8859_1);
376 bytes[0]=(byte)(code/0x100);
377 bytes[1]=(byte)(code%0x100);
378 _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_CLOSE,bytes,0,bytes.length);
379 _outbound.flush();
380 if (close)
381 _endp.shutdownOutput();
382 }
383 else if (close)
384 _endp.close();
385
386 }
387 catch(IOException e)
388 {
389 LOG.ignore(e);
390 }
391 }
392 }
393
394
395 public void fillBuffersFrom(Buffer buffer)
396 {
397 _parser.fill(buffer);
398 }
399
400
401 private void checkWriteable()
402 {
403 if (!_outbound.isBufferEmpty() && _endp instanceof AsyncEndPoint)
404 {
405 ((AsyncEndPoint)_endp).scheduleWrite();
406 }
407 }
408
409
410
411
412 private class WSFrameConnection implements WebSocket.FrameConnection
413 {
414 volatile boolean _disconnecting;
415
416
417 public void sendMessage(String content) throws IOException
418 {
419 if (_closedOut)
420 throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
421 byte[] data = content.getBytes(StringUtil.__UTF8);
422 _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_TEXT,data,0,data.length);
423 checkWriteable();
424 _idle.access(_endp);
425 }
426
427
428 public void sendMessage(byte[] content, int offset, int length) throws IOException
429 {
430 if (_closedOut)
431 throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
432 _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_BINARY,content,offset,length);
433 checkWriteable();
434 _idle.access(_endp);
435 }
436
437
438 public void sendFrame(byte flags,byte opcode, byte[] content, int offset, int length) throws IOException
439 {
440 if (_closedOut)
441 throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
442 _outbound.addFrame(flags,opcode,content,offset,length);
443 checkWriteable();
444 _idle.access(_endp);
445 }
446
447
448 public void sendControl(byte ctrl, byte[] data, int offset, int length) throws IOException
449 {
450 if (_closedOut)
451 throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
452 _outbound.addFrame((byte)FLAG_FIN,ctrl,data,offset,length);
453 checkWriteable();
454 _idle.access(_endp);
455 }
456
457
458 public boolean isMessageComplete(byte flags)
459 {
460 return isLastFrame(flags);
461 }
462
463
464 public boolean isOpen()
465 {
466 return _endp!=null&&_endp.isOpen();
467 }
468
469
470 public void close(int code, String message)
471 {
472 if (_disconnecting)
473 return;
474 _disconnecting=true;
475 WebSocketConnectionD13.this.closeOut(code,message);
476 }
477
478
479 public void setMaxIdleTime(int ms)
480 {
481 try
482 {
483 _endp.setMaxIdleTime(ms);
484 }
485 catch(IOException e)
486 {
487 LOG.warn(e);
488 }
489 }
490
491
492 public void setMaxTextMessageSize(int size)
493 {
494 _maxTextMessageSize=size;
495 }
496
497
498 public void setMaxBinaryMessageSize(int size)
499 {
500 _maxBinaryMessageSize=size;
501 }
502
503
504 public int getMaxIdleTime()
505 {
506 return _endp.getMaxIdleTime();
507 }
508
509
510 public int getMaxTextMessageSize()
511 {
512 return _maxTextMessageSize;
513 }
514
515
516 public int getMaxBinaryMessageSize()
517 {
518 return _maxBinaryMessageSize;
519 }
520
521
522 public String getProtocol()
523 {
524 return _protocol;
525 }
526
527
528 public byte binaryOpcode()
529 {
530 return OP_BINARY;
531 }
532
533
534 public byte textOpcode()
535 {
536 return OP_TEXT;
537 }
538
539
540 public byte continuationOpcode()
541 {
542 return OP_CONTINUATION;
543 }
544
545
546 public byte finMask()
547 {
548 return FLAG_FIN;
549 }
550
551
552 public boolean isControl(byte opcode)
553 {
554 return isControlFrame(opcode);
555 }
556
557
558 public boolean isText(byte opcode)
559 {
560 return opcode==OP_TEXT;
561 }
562
563
564 public boolean isBinary(byte opcode)
565 {
566 return opcode==OP_BINARY;
567 }
568
569
570 public boolean isContinuation(byte opcode)
571 {
572 return opcode==OP_CONTINUATION;
573 }
574
575
576 public boolean isClose(byte opcode)
577 {
578 return opcode==OP_CLOSE;
579 }
580
581
582 public boolean isPing(byte opcode)
583 {
584 return opcode==OP_PING;
585 }
586
587
588 public boolean isPong(byte opcode)
589 {
590 return opcode==OP_PONG;
591 }
592
593
594 public void disconnect()
595 {
596 close(CLOSE_NORMAL,null);
597 }
598
599
600 public void setAllowFrameFragmentation(boolean allowFragmentation)
601 {
602 _parser.setFakeFragments(allowFragmentation);
603 }
604
605
606 public boolean isAllowFrameFragmentation()
607 {
608 return _parser.isFakeFragments();
609 }
610
611
612 @Override
613 public String toString()
614 {
615 return this.getClass().getSimpleName()+"D13@"+_endp.getLocalAddr()+":"+_endp.getLocalPort()+"<->"+_endp.getRemoteAddr()+":"+_endp.getRemotePort();
616 }
617 }
618
619
620
621
622 private class WSFrameHandler implements WebSocketParser.FrameHandler
623 {
624 private final Utf8StringBuilder _utf8 = new Utf8StringBuilder(512);
625 private ByteArrayBuffer _aggregate;
626 private byte _opcode=-1;
627
628 public void onFrame(final byte flags, final byte opcode, final Buffer buffer)
629 {
630 boolean lastFrame = isLastFrame(flags);
631
632 synchronized(WebSocketConnectionD13.this)
633 {
634
635 if (_closedIn)
636 return;
637 }
638 try
639 {
640 byte[] array=buffer.array();
641
642 if (STRICT)
643 {
644 if (isControlFrame(opcode) && buffer.length()>125)
645 {
646 errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Control frame too large");
647 return;
648 }
649
650 if ((flags&0x7)!=0)
651 {
652 errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"RSV bits set 0x"+Integer.toHexString(flags));
653 return;
654 }
655
656
657 if (_closeCode!=0 && _closeCode!=CLOSE_NORMAL && opcode!=OP_CLOSE)
658 return;
659 }
660
661
662 if (_onFrame!=null)
663 {
664 if (_onFrame.onFrame(flags,opcode,array,buffer.getIndex(),buffer.length()))
665 return;
666 }
667
668 if (_onControl!=null && isControlFrame(opcode))
669 {
670 if (_onControl.onControl(opcode,array,buffer.getIndex(),buffer.length()))
671 return;
672 }
673
674 switch(opcode)
675 {
676 case WebSocketConnectionD13.OP_CONTINUATION:
677 {
678 if (_opcode==-1)
679 {
680 errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad Continuation");
681 return;
682 }
683
684
685 if (_onTextMessage!=null && _opcode==WebSocketConnectionD13.OP_TEXT)
686 {
687 if (_utf8.append(buffer.array(),buffer.getIndex(),buffer.length(),_connection.getMaxTextMessageSize()))
688 {
689
690 if (lastFrame)
691 {
692 _opcode=-1;
693 String msg =_utf8.toString();
694 _utf8.reset();
695 _onTextMessage.onMessage(msg);
696 }
697 }
698 else
699 textMessageTooLarge();
700 }
701
702 if (_opcode>=0 && _connection.getMaxBinaryMessageSize()>=0)
703 {
704 if (_aggregate!=null && checkBinaryMessageSize(_aggregate.length(),buffer.length()))
705 {
706 _aggregate.put(buffer);
707
708
709 if (lastFrame && _onBinaryMessage!=null)
710 {
711 try
712 {
713 _onBinaryMessage.onMessage(_aggregate.array(),_aggregate.getIndex(),_aggregate.length());
714 }
715 finally
716 {
717 _opcode=-1;
718 _aggregate.clear();
719 }
720 }
721 }
722 }
723 break;
724 }
725 case WebSocketConnectionD13.OP_PING:
726 {
727 LOG.debug("PING {}",this);
728 if (!_closedOut)
729 _connection.sendControl(WebSocketConnectionD13.OP_PONG,buffer.array(),buffer.getIndex(),buffer.length());
730 break;
731 }
732
733 case WebSocketConnectionD13.OP_PONG:
734 {
735 LOG.debug("PONG {}",this);
736 break;
737 }
738
739 case WebSocketConnectionD13.OP_CLOSE:
740 {
741 int code=WebSocketConnectionD13.CLOSE_NO_CODE;
742 String message=null;
743 if (buffer.length()>=2)
744 {
745 code=(0xff&buffer.array()[buffer.getIndex()])*0x100+(0xff&buffer.array()[buffer.getIndex()+1]);
746 if (buffer.length()>2)
747 message=new String(buffer.array(),buffer.getIndex()+2,buffer.length()-2,StringUtil.__UTF8);
748 }
749 closeIn(code,message);
750 break;
751 }
752
753 case WebSocketConnectionD13.OP_TEXT:
754 {
755 if (STRICT && _opcode!=-1)
756 {
757 errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Expected Continuation"+Integer.toHexString(opcode));
758 return;
759 }
760
761 if(_onTextMessage!=null)
762 {
763 if (_connection.getMaxTextMessageSize()<=0)
764 {
765
766 if (lastFrame)
767 _onTextMessage.onMessage(buffer.toString(StringUtil.__UTF8));
768 else
769 {
770 LOG.warn("Frame discarded. Text aggregation disabled for {}",_endp);
771 errorClose(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"Text frame aggregation disabled");
772 }
773 }
774
775 else if (_utf8.append(buffer.array(),buffer.getIndex(),buffer.length(),_connection.getMaxTextMessageSize()))
776 {
777 if (lastFrame)
778 {
779 String msg =_utf8.toString();
780 _utf8.reset();
781 _onTextMessage.onMessage(msg);
782 }
783 else
784 {
785 _opcode=WebSocketConnectionD13.OP_TEXT;
786 }
787 }
788 else
789 textMessageTooLarge();
790 }
791 break;
792 }
793
794 case WebSocketConnectionD13.OP_BINARY:
795 {
796 if (STRICT && _opcode!=-1)
797 {
798 errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Expected Continuation"+Integer.toHexString(opcode));
799 return;
800 }
801
802 if (_onBinaryMessage!=null && checkBinaryMessageSize(0,buffer.length()))
803 {
804 if (lastFrame)
805 {
806 _onBinaryMessage.onMessage(array,buffer.getIndex(),buffer.length());
807 }
808 else if (_connection.getMaxBinaryMessageSize()>=0)
809 {
810 _opcode=opcode;
811
812 if (_aggregate==null)
813 _aggregate=new ByteArrayBuffer(_connection.getMaxBinaryMessageSize());
814 _aggregate.put(buffer);
815 }
816 else
817 {
818 LOG.warn("Frame discarded. Binary aggregation disabed for {}",_endp);
819 errorClose(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"Binary frame aggregation disabled");
820 }
821 }
822 break;
823 }
824
825 default:
826 if (STRICT)
827 errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad opcode 0x"+Integer.toHexString(opcode));
828 return;
829 }
830 }
831 catch(ThreadDeath th)
832 {
833 throw th;
834 }
835 catch(Utf8Appendable.NotUtf8Exception notUtf8)
836 {
837 LOG.warn("{} for {}",notUtf8,_endp);
838 LOG.debug(notUtf8);
839 errorClose(WebSocketConnectionD13.CLOSE_BAD_PAYLOAD,"Invalid UTF-8");
840 }
841 catch(Throwable probablyNotUtf8)
842 {
843 LOG.warn("{} for {}",probablyNotUtf8,_endp);
844 LOG.debug(probablyNotUtf8);
845 errorClose(WebSocketConnectionD13.CLOSE_BAD_PAYLOAD,"Invalid Payload: "+probablyNotUtf8);
846 }
847 }
848
849 private void errorClose(int code, String message)
850 {
851 _connection.close(code,message);
852 if (BRUTAL)
853 {
854 try
855 {
856 _endp.close();
857 }
858 catch (IOException e)
859 {
860 LOG.warn(e.toString());
861 LOG.debug(e);
862 }
863 }
864 }
865
866 private boolean checkBinaryMessageSize(int bufferLen, int length)
867 {
868 int max = _connection.getMaxBinaryMessageSize();
869 if (max>0 && (bufferLen+length)>max)
870 {
871 LOG.warn("Binary message too large > {}B for {}",_connection.getMaxBinaryMessageSize(),_endp);
872 _connection.close(WebSocketConnectionD13.CLOSE_MESSAGE_TOO_LARGE,"Message size > "+_connection.getMaxBinaryMessageSize());
873 _opcode=-1;
874 if (_aggregate!=null)
875 _aggregate.clear();
876 return false;
877 }
878 return true;
879 }
880
881 private void textMessageTooLarge()
882 {
883 LOG.warn("Text message too large > {} chars for {}",_connection.getMaxTextMessageSize(),_endp);
884 _connection.close(WebSocketConnectionD13.CLOSE_MESSAGE_TOO_LARGE,"Text message size > "+_connection.getMaxTextMessageSize()+" chars");
885
886 _opcode=-1;
887 _utf8.reset();
888 }
889
890 public void close(int code,String message)
891 {
892 if (code!=CLOSE_NORMAL)
893 LOG.warn("Close: "+code+" "+message);
894 _connection.close(code,message);
895 }
896
897 @Override
898 public String toString()
899 {
900 return WebSocketConnectionD13.this.toString()+"FH";
901 }
902 }
903
904
905 private interface IdleCheck
906 {
907 void access(EndPoint endp);
908 }
909
910
911 public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
912 {
913 String key = request.getHeader("Sec-WebSocket-Key");
914
915 response.setHeader("Upgrade","WebSocket");
916 response.addHeader("Connection","Upgrade");
917 response.addHeader("Sec-WebSocket-Accept",hashKey(key));
918 if (subprotocol!=null)
919 response.addHeader("Sec-WebSocket-Protocol",subprotocol);
920
921 for(Extension ext : _extensions)
922 response.addHeader("Sec-WebSocket-Extensions",ext.getParameterizedName());
923
924 response.sendError(101);
925
926 if (_onFrame!=null)
927 _onFrame.onHandshake(_connection);
928 _webSocket.onOpen(_connection);
929 }
930
931
932 public static String hashKey(String key)
933 {
934 try
935 {
936 MessageDigest md = MessageDigest.getInstance("SHA1");
937 md.update(key.getBytes("UTF-8"));
938 md.update(MAGIC);
939 return new String(B64Code.encode(md.digest()));
940 }
941 catch (Exception e)
942 {
943 throw new RuntimeException(e);
944 }
945 }
946
947
948 @Override
949 public String toString()
950 {
951 return "WS/D"+_draft+"-"+_endp;
952 }
953 }