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