View Javadoc

1   package org.eclipse.jetty.websocket;
2   
3   import java.io.IOException;
4   
5   import org.eclipse.jetty.io.Buffer;
6   import org.eclipse.jetty.io.EndPoint;
7   
8   
9   /* ------------------------------------------------------------ */
10  /** WebSocketGenerator.
11   * This class generates websocket packets.
12   * It is fully synchronized because it is likely that async 
13   * threads will call the addMessage methods while other 
14   * threads are flushing the generator.
15   */
16  public class WebSocketGenerator
17  {
18      final private WebSocketBuffers _buffers;
19      final private EndPoint _endp;
20      private Buffer _buffer;
21      
22      public WebSocketGenerator(WebSocketBuffers buffers, EndPoint endp)
23      {
24          _buffers=buffers;
25          _endp=endp;
26      }
27  
28      synchronized public void addFrame(byte frame,byte[] content, int blockFor) throws IOException
29      {
30          addFrame(frame,content,0,content.length,blockFor);
31      }
32      
33      synchronized public void addFrame(byte frame,byte[] content, int offset, int length, int blockFor) throws IOException
34      {
35          if (_buffer==null)
36              _buffer=_buffers.getDirectBuffer();
37          
38          if ((frame&0x80)==0x80)
39          {
40              // Send in a length delimited frame
41              
42              // maximum of 3 byte length == 21 bits
43              if (length>2097152)
44                  throw new IllegalArgumentException("too big");
45              int length_bytes=(length>16384)?3:(length>128)?2:1;
46              int needed=length+1+length_bytes;
47              checkSpace(needed,blockFor);
48              
49              _buffer.put(frame);
50  
51              switch (length_bytes)
52              {
53                  case 3:
54                      _buffer.put((byte)(0x80|(length>>14)));
55                  case 2:
56                      _buffer.put((byte)(0x80|(0x7f&(length>>7))));
57                  case 1:
58                      _buffer.put((byte)(0x7f&length));
59              }
60  
61              _buffer.put(content,offset,length);
62          }
63          else
64          {
65              // send in a sentinel frame
66              int needed=length+2;
67              checkSpace(needed,blockFor);
68  
69              _buffer.put(frame);
70              _buffer.put(content,offset,length);
71              _buffer.put((byte)0xFF);
72          }
73      }
74      
75      synchronized public void addFrame(byte frame, String content, int blockFor) throws IOException
76      {
77          Buffer byte_buffer=_buffers.getBuffer();
78          try
79          {
80              byte[] array=byte_buffer.array();
81  
82              int chars = content.length();
83              int bytes = 0;
84              final int limit=array.length-6;
85  
86              for (int i = 0; i < chars; i++)
87              {
88                  int code = content.charAt(i);
89  
90                  if (bytes>=limit)
91                      throw new IllegalArgumentException("frame too large");
92  
93                  if ((code & 0xffffff80) == 0) 
94                  {
95                      array[bytes++]=(byte)(code);
96                  }
97                  else if((code&0xfffff800)==0)
98                  {
99                      array[bytes++]=(byte)(0xc0|(code>>6));
100                     array[bytes++]=(byte)(0x80|(code&0x3f));
101                 }
102                 else if((code&0xffff0000)==0)
103                 {
104                     array[bytes++]=(byte)(0xe0|(code>>12));
105                     array[bytes++]=(byte)(0x80|((code>>6)&0x3f));
106                     array[bytes++]=(byte)(0x80|(code&0x3f));
107                 }
108                 else if((code&0xff200000)==0)
109                 {
110                     array[bytes++]=(byte)(0xf0|(code>>18));
111                     array[bytes++]=(byte)(0x80|((code>>12)&0x3f));
112                     array[bytes++]=(byte)(0x80|((code>>6)&0x3f));
113                     array[bytes++]=(byte)(0x80|(code&0x3f));
114                 }
115                 else if((code&0xf4000000)==0)
116                 {
117                     array[bytes++]=(byte)(0xf8|(code>>24));
118                     array[bytes++]=(byte)(0x80|((code>>18)&0x3f));
119                     array[bytes++]=(byte)(0x80|((code>>12)&0x3f));
120                     array[bytes++]=(byte)(0x80|((code>>6)&0x3f));
121                     array[bytes++]=(byte)(0x80|(code&0x3f));
122                 }
123                 else if((code&0x80000000)==0)
124                 {
125                     array[bytes++]=(byte)(0xfc|(code>>30));
126                     array[bytes++]=(byte)(0x80|((code>>24)&0x3f));
127                     array[bytes++]=(byte)(0x80|((code>>18)&0x3f));
128                     array[bytes++]=(byte)(0x80|((code>>12)&0x3f));
129                     array[bytes++]=(byte)(0x80|((code>>6)&0x3f));
130                     array[bytes++]=(byte)(0x80|(code&0x3f));
131                 }
132                 else
133                 {
134                     array[bytes++]=(byte)('?');
135                 }
136             }
137             addFrame(frame,array,0,bytes,blockFor);
138         }
139         finally
140         {
141             _buffers.returnBuffer(byte_buffer);
142         }
143     }
144     
145     private void checkSpace(int needed, long blockFor)
146         throws IOException
147     {
148         int space=_buffer.space();
149         
150         if (space<needed)
151         {
152             if (_endp.isBlocking())
153             {
154                 try
155                 {
156                     flushBuffer();
157                     _buffer.compact();
158                     space=_buffer.space();
159                 }
160                 catch(IOException e)
161                 {
162                     throw e;
163                 }
164             }
165             else
166             {
167                 flushBuffer();
168                 _buffer.compact();
169                 space=_buffer.space();
170 
171                 if (space<needed && _buffer.length()>0 && _endp.blockWritable(blockFor))
172                 {
173                     flushBuffer();
174                     _buffer.compact();
175                     space=_buffer.space();
176                 }
177             }
178             
179             if (space<needed)
180             {
181                 _endp.close();
182                 throw new IOException("Full Timeout");
183             }
184         }
185     }
186 
187     synchronized public int flush(long blockFor)
188     {
189         return 0;
190     }
191 
192     synchronized public int flush() throws IOException
193     {
194         int flushed = flushBuffer();
195         if (_buffer!=null && _buffer.length()==0)
196         {
197             _buffers.returnBuffer(_buffer);
198             _buffer=null;
199         }
200         return flushed;
201     }
202     
203     private int flushBuffer() throws IOException
204     {
205         if (!_endp.isOpen())
206             return -1;
207         
208         if (_buffer!=null)
209         {
210             int flushed =_endp.flush(_buffer);
211             if (flushed>0)
212                 _buffer.skip(flushed);
213             return flushed;
214         }
215         return 0;
216     }
217 
218     synchronized public boolean isBufferEmpty()
219     {
220         return _buffer==null || _buffer.length()==0;
221     }
222 }