/******************************************************************************* * Copyright (c) 2011 Intalio, Inc. * ====================================================================== * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. *******************************************************************************/ // ======================================================================== // Copyright (c) 2010 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.websocket; import java.io.IOException; import java.math.BigInteger; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EofException; /* ------------------------------------------------------------ */ /** WebSocketGenerator. * This class generates websocket packets. * It is fully synchronized because it is likely that async * threads will call the addMessage methods while other * threads are flushing the generator. */ public class WebSocketGeneratorD00 implements WebSocketGenerator { final private WebSocketBuffers _buffers; final private EndPoint _endp; private Buffer _buffer; public WebSocketGeneratorD00(WebSocketBuffers buffers, EndPoint endp) { _buffers=buffers; _endp=endp; } public synchronized void addFrame(byte flags, byte opcode,byte[] content, int offset, int length) throws IOException { long blockFor=_endp.getMaxIdleTime(); if (_buffer==null) _buffer=_buffers.getDirectBuffer(); if (_buffer.space() == 0) expelBuffer(blockFor); bufferPut(opcode, blockFor); if (isLengthFrame(opcode)) { // Send a length delimited frame // How many bytes we need for the length ? // We have 7 bits available, so log2(length) / 7 + 1 // For example, 50000 bytes is 2 8-bytes: 11000011 01010000 // but we need to write it in 3 7-bytes 0000011 0000110 1010000 // 65536 == 1 00000000 00000000 => 100 0000000 0000000 int lengthBytes = new BigInteger(String.valueOf(length)).bitLength() / 7 + 1; for (int i = lengthBytes - 1; i > 0; --i) { byte lengthByte = (byte)(0x80 | (0x7F & (length >> 7 * i))); bufferPut(lengthByte, blockFor); } bufferPut((byte)(0x7F & length), blockFor); } int remaining = length; while (remaining > 0) { int chunk = remaining < _buffer.space() ? remaining : _buffer.space(); _buffer.put(content, offset + (length - remaining), chunk); remaining -= chunk; if (_buffer.space() > 0) { if (!isLengthFrame(opcode)) _buffer.put((byte)0xFF); // Gently flush the data, issuing a non-blocking write flushBuffer(); } else { // Forcibly flush the data, issuing a blocking write expelBuffer(blockFor); if (remaining == 0) { if (!isLengthFrame(opcode)) _buffer.put((byte)0xFF); // Gently flush the data, issuing a non-blocking write flushBuffer(); } } } } private synchronized boolean isLengthFrame(byte frame) { return (frame & WebSocketConnectionD00.LENGTH_FRAME) == WebSocketConnectionD00.LENGTH_FRAME; } private synchronized void bufferPut(byte datum, long blockFor) throws IOException { if (_buffer==null) _buffer=_buffers.getDirectBuffer(); _buffer.put(datum); if (_buffer.space() == 0) expelBuffer(blockFor); } public synchronized int flush(int blockFor) throws IOException { return expelBuffer(blockFor); } public synchronized int flush() throws IOException { int flushed = flushBuffer(); if (_buffer!=null && _buffer.length()==0) { _buffers.returnBuffer(_buffer); _buffer=null; } return flushed; } private synchronized int flushBuffer() throws IOException { if (!_endp.isOpen()) throw new EofException(); if (_buffer!=null && _buffer.hasContent()) return _endp.flush(_buffer); return 0; } private synchronized int expelBuffer(long blockFor) throws IOException { if (_buffer==null) return 0; int result = flushBuffer(); _buffer.compact(); if (!_endp.isBlocking()) { while (_buffer.space()==0) { boolean ready = _endp.blockWritable(blockFor); if (!ready) throw new IOException("Write timeout"); result += flushBuffer(); _buffer.compact(); } } return result; } public synchronized boolean isBufferEmpty() { return _buffer==null || _buffer.length()==0; } }