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 19package org.eclipse.jetty.websocket; 20 21import java.io.IOException; 22import java.math.BigInteger; 23 24import org.eclipse.jetty.io.Buffer; 25import org.eclipse.jetty.io.EndPoint; 26import 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 */ 36public 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} 174