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