View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.math.BigInteger;
23  
24  import org.eclipse.jetty.io.Buffer;
25  import org.eclipse.jetty.io.EndPoint;
26  import org.eclipse.jetty.io.EofException;
27  
28  
29  /* ------------------------------------------------------------ */
30  /** WebSocketGenerator.
31   * This class generates websocket packets.
32   * It is fully synchronized because it is likely that async
33   * threads will call the addMessage methods while other
34   * threads are flushing the generator.
35   */
36  public class WebSocketGeneratorD00 implements WebSocketGenerator
37  {
38      final private WebSocketBuffers _buffers;
39      final private EndPoint _endp;
40      private Buffer _buffer;
41  
42      public WebSocketGeneratorD00(WebSocketBuffers buffers, EndPoint endp)
43      {
44          _buffers=buffers;
45          _endp=endp;
46      }
47      
48      public synchronized void addFrame(byte flags, byte opcode,byte[] content, int offset, int length) throws IOException
49      {
50          long blockFor=_endp.getMaxIdleTime();
51          
52          if (_buffer==null)
53              _buffer=_buffers.getDirectBuffer();
54  
55          if (_buffer.space() == 0)
56              expelBuffer(blockFor);
57  
58          bufferPut(opcode, blockFor);
59  
60          if (isLengthFrame(opcode))
61          {
62              // Send a length delimited frame
63  
64              // How many bytes we need for the length ?
65              // We have 7 bits available, so log2(length) / 7 + 1
66              // For example, 50000 bytes is 2 8-bytes: 11000011 01010000
67              // but we need to write it in 3 7-bytes 0000011 0000110 1010000
68              // 65536 == 1 00000000 00000000 => 100 0000000 0000000
69              int lengthBytes = new BigInteger(String.valueOf(length)).bitLength() / 7 + 1;
70              for (int i = lengthBytes - 1; i > 0; --i)
71              {
72                  byte lengthByte = (byte)(0x80 | (0x7F & (length >> 7 * i)));
73                  bufferPut(lengthByte, blockFor);
74              }
75              bufferPut((byte)(0x7F & length), blockFor);
76          }
77  
78          int remaining = length;
79          while (remaining > 0)
80          {
81              int chunk = remaining < _buffer.space() ? remaining : _buffer.space();
82              _buffer.put(content, offset + (length - remaining), chunk);
83              remaining -= chunk;
84              if (_buffer.space() > 0)
85              {
86                  if (!isLengthFrame(opcode))
87                      _buffer.put((byte)0xFF);
88                  // Gently flush the data, issuing a non-blocking write
89                  flushBuffer();
90              }
91              else
92              {
93                  // Forcibly flush the data, issuing a blocking write
94                  expelBuffer(blockFor);
95                  if (remaining == 0)
96                  {
97                      if (!isLengthFrame(opcode))
98                          _buffer.put((byte)0xFF);
99                      // Gently flush the data, issuing a non-blocking write
100                     flushBuffer();
101                 }
102             }
103         }
104     }
105 
106     private synchronized boolean isLengthFrame(byte frame)
107     {
108         return (frame & WebSocketConnectionD00.LENGTH_FRAME) == WebSocketConnectionD00.LENGTH_FRAME;
109     }
110 
111     private synchronized void bufferPut(byte datum, long blockFor) throws IOException
112     {
113         if (_buffer==null)
114             _buffer=_buffers.getDirectBuffer();
115         _buffer.put(datum);
116         if (_buffer.space() == 0)
117             expelBuffer(blockFor);
118     }
119 
120     public synchronized int flush(int blockFor) throws IOException
121     {
122         return expelBuffer(blockFor);
123     }
124 
125     public synchronized int flush() throws IOException
126     {
127         int flushed = flushBuffer();
128         if (_buffer!=null && _buffer.length()==0)
129         {
130             _buffers.returnBuffer(_buffer);
131             _buffer=null;
132         }
133         return flushed;
134     }
135 
136     private synchronized int flushBuffer() throws IOException
137     {
138         if (!_endp.isOpen())
139             throw new EofException();
140 
141         if (_buffer!=null && _buffer.hasContent())
142             return _endp.flush(_buffer);
143 
144         return 0;
145     }
146 
147     private synchronized int expelBuffer(long blockFor) throws IOException
148     {
149         if (_buffer==null)
150             return 0;
151         int result = flushBuffer();
152         _buffer.compact();
153         if (!_endp.isBlocking())
154         {
155             while (_buffer.space()==0)
156             {
157                 boolean ready = _endp.blockWritable(blockFor);
158                 if (!ready)
159                     throw new IOException("Write timeout");
160 
161                 result += flushBuffer();
162                 _buffer.compact();
163             }
164         }
165         return result;
166     }
167 
168     public synchronized boolean isBufferEmpty()
169     {
170         return _buffer==null || _buffer.length()==0;
171     }
172     
173 }