// Copyright (c) 2014 Tom Zhou<iwebpp@gmail.com>
package com.iwebpp.wspp;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.LinkedList;
import java.util.List;
import com.iwebpp.SimpleDebug;
import com.iwebpp.node.NodeContext;
import com.iwebpp.node.Util;
public abstract class Receiver extends SimpleDebug {
private static final String TAG = "Receiver";
private class State {
public State(int activeFragmentedOperation, boolean lastFragment,
boolean masked, int opcode, boolean fragmentedOperation) {
super();
this.activeFragmentedOperation = activeFragmentedOperation;
this.lastFragment = lastFragment;
this.masked = masked;
this.opcode = opcode;
this.fragmentedOperation = fragmentedOperation;
}
private int activeFragmentedOperation = -1;
private boolean lastFragment = false;
private boolean masked = false;
private int opcode = 0;
private boolean fragmentedOperation = false;
}
private ByteBuffer expectBuffer;
private BufferPool fragmentedBufferPool;
private BufferPool unfragmentedBufferPool;
private State state;
private List<ByteBuffer> overflow;
private ByteBuffer headerBuffer;
private int expectOffset;
private PacketHandler expectHandler;
private LinkedList<Object> currentMessage;
private boolean dead;
private final OpcHandler _handler_text;
private final OpcHandler _handler_binary;
private final OpcHandler _handler_close;
private final OpcHandler _handler_ping;
private final OpcHandler _handler_pong;
private CharsetDecoder _utf8_decoder;
private NodeContext context;
public static class opcOptions {
public boolean masked;
public ByteBuffer buffer;
public boolean binary;
public opcOptions(boolean masked, ByteBuffer buffer, boolean binary) {
this.masked = masked;
this.buffer = buffer;
this.binary = binary;
}
}
protected Receiver() throws Exception {
final Receiver self = this;
this._utf8_decoder = Charset.forName("utf8").newDecoder();
// memory pool for fragmented messages
/*int fragmentedPoolPrevUsed = -1;
this.fragmentedBufferPool = new BufferPool(1024, function(db, length) {
return db.used + length;
}, function(db) {
return fragmentedPoolPrevUsed = fragmentedPoolPrevUsed >= 0 ?
(fragmentedPoolPrevUsed + db.used) / 2 :
db.used;
});*/
///int fragmentedPoolPrevUsed = -1;
this.fragmentedBufferPool = new BufferPool(1024, new BufferPool.Strategy() {
@Override
public int _shrinkStrategy(BufferPool db) {
return db.PoolPrevUsed = db.PoolPrevUsed >= 0 ?
(db.PoolPrevUsed + db.used()) / 2 :
db.used();
}
@Override
public int _growStrategy(BufferPool db, int length) {
return db.used() + length;
}
});
// memory pool for unfragmented messages
/*var unfragmentedPoolPrevUsed = -1;
this.unfragmentedBufferPool = new BufferPool(1024, function(db, length) {
return db.used + length;
}, function(db) {
return unfragmentedPoolPrevUsed = unfragmentedPoolPrevUsed >= 0 ?
(unfragmentedPoolPrevUsed + db.used) / 2 :
db.used;
});*/
///int unfragmentedPoolPrevUsed = -1;
this.unfragmentedBufferPool = new BufferPool(1024, new BufferPool.Strategy() {
@Override
public int _shrinkStrategy(BufferPool db) {
return db.PoolPrevUsed = db.PoolPrevUsed >= 0 ?
(db.PoolPrevUsed + db.used()) / 2 :
db.used();
}
@Override
public int _growStrategy(BufferPool db, int length) {
return db.used() + length;
}
});
/*
this.state = {
activeFragmentedOperation: null,
lastFragment: false,
masked: false,
opcode: 0,
fragmentedOperation: false
};*/
this.state = new State(-1, false, false, 0, false);
this.overflow = new LinkedList<ByteBuffer>();///[];
this.headerBuffer = ByteBuffer.allocate(10);///new Buffer(10);
this.expectOffset = 0;
this.expectBuffer = null;
this.expectHandler = null;
this.currentMessage = new LinkedList<Object>();///[];
this.expectHeader(2, new processPacket());
this.dead = false;
/*this.onerror = function() {};
this.ontext = function() {};
this.onbinary = function() {};
this.onclose = function() {};
this.onping = function() {};
this.onpong = function() {};*/
// Opc handlers: text, binary, close, ping, pong
this._handler_text = new OpcHandler(){
@Override
public void start(ByteBuffer data) throws Exception {
if (data != null) {
String dstr = "";
for (int i = 0; i < data.capacity(); i ++)
dstr += " "+data.get(i);
debug(TAG, dstr);
}
///var self = this;
// decode length
int firstLength = data.get(1) & 0x7f;
if (firstLength < 126) {
///opcodes['1'].getData.call(self, firstLength);
getData(firstLength);
}
else if (firstLength == 126) {
/*self.expectHeader(2, function(data) {
opcodes['1'].getData.call(self, readUInt16BE.call(data, 0));
});*/
self.expectHeader(2, new PacketHandler(){
public void onPacket(ByteBuffer data) throws Exception {
getData(data.order(ByteOrder.BIG_ENDIAN).getShort(0) & 0xffff);
}
});
}
else if (firstLength == 127) {
/*
self.expectHeader(8, function(data) {
if (readUInt32BE.call(data, 0) != 0) {
self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
return;
}
opcodes['1'].getData.call(self, readUInt32BE.call(data, 4));
});*/
self.expectHeader(8, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
data.order(ByteOrder.BIG_ENDIAN);
if (data.getInt(0) != 0) {
self.error("packets with length spanning more than 32 bit is currently not supported", 1008);
return;
}
getData(data.getInt(4));
}
});
}
}
@Override
public void getData(final int length) throws Exception {
///var self = this;
if (self.state.masked) {
/*self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['1'].finish.call(self, mask, data);
});
});*/
self.expectHeader(4, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
final byte[] mask = new byte[4]; ///data.array();
mask[0] = data.get(0);
mask[1] = data.get(1);
mask[2] = data.get(2);
mask[3] = data.get(3);
debug(TAG, "mask: "+mask[0]+" "+mask[1]+" "+mask[2]+" "+mask[3]);
self.expectData(length, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data2) throws Exception {
finish(mask, data2);
}
});
}
});
}
else {
/*self.expectData(length, function(data) {
opcodes['1'].finish.call(self, null, data);
});*/
self.expectData(length, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
finish(null, data);
}
});
}
}
@Override
public void finish(byte[] mask, ByteBuffer data) throws Exception {
debug(TAG, "ontext, mask:"+mask+",data:"+data);
Object packet = self.unmask(mask, data, true);
debug(TAG, "ontext, mask:"+mask+",packet:"+packet);
if (packet != null) self.currentMessage.add(packet);
if (self.state.lastFragment) {
ByteBuffer messageBuffer = self.concatBuffers(self.currentMessage);
if (!Validation.isValidUTF8(messageBuffer)) {
error("invalid utf8 sequence", 1007);
return;
}
/// TBD...
///self.ontext(messageBuffer.toString("utf8"), {masked: this.state.masked, buffer: messageBuffer});
self.ontext(self._utf8_decoder.decode(
messageBuffer).toString(),
new opcOptions(self.state.masked, messageBuffer, false));
///self.currentMessage = [];
self.currentMessage.clear();
}
self.endPacket();
}
};
this._handler_binary = new OpcHandler(){
@Override
public void start(ByteBuffer data) throws Exception {
if (data != null) {
String dstr = "";
for (int i = 0; i < data.capacity(); i ++)
dstr += " "+data.get(i);
debug(TAG, dstr);
}
///var self = this;
// decode length
int firstLength = data.get(1) & 0x7f;
if (firstLength < 126) {
///opcodes['2'].getData.call(self, firstLength);
getData(firstLength);
}
else if (firstLength == 126) {
/*self.expectHeader(2, function(data) {
opcodes['2'].getData.call(self, readUInt16BE.call(data, 0));
});*/
self.expectHeader(2, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
getData(data.order(ByteOrder.BIG_ENDIAN).getShort(0) & 0xffff);
}
});
}
else if (firstLength == 127) {
/*self.expectHeader(8, function(data) {
if (readUInt32BE.call(data, 0) != 0) {
self.error('packets with length spanning more than 32 bit is currently not supported', 1008);
return;
}
opcodes['2'].getData.call(self, readUInt32BE.call(data, 4, true));
});*/
self.expectHeader(8, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
data.order(ByteOrder.BIG_ENDIAN);
if (data.getInt(0) != 0) {
self.error("packets with length spanning more than 32 bit is currently not supported", 1008);
return;
}
getData(data.getInt(4));
}
});
}
}
@Override
public void getData(final int length) throws Exception {
///var self = this;
if (self.state.masked) {
/*self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['2'].finish.call(self, mask, data);
});
});*/
self.expectHeader(4, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
final byte[] mask = new byte[4]; ///data.array();
mask[0] = data.get(0);
mask[1] = data.get(1);
mask[2] = data.get(2);
mask[3] = data.get(3);
self.expectData(length, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data2)
throws Exception {
finish(mask, data2);
}
});
}
});
}
else {
/*
self.expectData(length, function(data) {
opcodes['2'].finish.call(self, null, data);
});*/
self.expectData(length, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
finish(null, data);
}
});
}
}
@Override
public void finish(byte[] mask, ByteBuffer data) throws Exception {
Object packet = self.unmask(mask, data, true);
debug(TAG, "onbinary, mask:"+mask+",packet:"+packet);
if (packet != null) self.currentMessage.add(packet);
if (self.state.lastFragment) {
ByteBuffer messageBuffer = self.concatBuffers(self.currentMessage);
self.onbinary(messageBuffer, new opcOptions(self.state.masked, messageBuffer, true));
self.currentMessage.clear();/// = [];
}
self.endPacket();
}
};
this._handler_close = new OpcHandler(){
@Override
public void start(ByteBuffer data) throws Exception {
if (data != null) {
String dstr = "";
for (int i = 0; i < data.capacity(); i ++)
dstr += " "+data.get(i);
debug(TAG, dstr);
}
///var self = this;
if (self.state.lastFragment == false) {
self.error("fragmented close is not supported", 1002);
return;
}
// decode length
int firstLength = data.get(1) & 0x7f;
if (firstLength < 126) {
///opcodes['8'].getData.call(self, firstLength);
getData(firstLength);
}
else {
self.error("control frames cannot have more than 125 bytes of data", 1002);
}
}
@Override
public void getData(final int length) throws Exception {
///var self = this;
if (self.state.masked) {
/*
self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['8'].finish.call(self, mask, data);
});
});*/
self.expectHeader(4, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
final byte[] mask = new byte[4]; ///data.array();
mask[0] = data.get(0);
mask[1] = data.get(1);
mask[2] = data.get(2);
mask[3] = data.get(3);
self.expectData(length, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data2)
throws Exception {
finish(mask, data2);
}
});
}
});
}
else {
/*self.expectData(length, function(data) {
opcodes['8'].finish.call(self, null, data);
});*/
self.expectData(length, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
finish(null, data);
}
});
}
}
@Override
public void finish(byte[] mask, ByteBuffer data) throws Exception {
///var self = this;
data = (ByteBuffer) self.unmask(mask, data, true);
debug(TAG, "onclose, mask:"+mask+",data:"+data);
if (data!=null && data.capacity() == 1) {
self.error("close packets with data must be at least two bytes long", 1002);
return;
}
int code = data!=null && data.capacity() > 1 ? data.order(ByteOrder.BIG_ENDIAN).getShort(0) & 0xffff : 1000 ; ///readUInt16BE.call(data, 0) : 1000;
if (!ErrorCodes.isValidErrorCode(code)) {
self.error("invalid error code", 1002);
return;
}
String message = "";
if (data!=null && data.capacity() > 2) {
ByteBuffer messageBuffer = (ByteBuffer) Util.chunkSlice(data, 2, data.capacity());///data.slice(2);
if (!Validation.isValidUTF8(messageBuffer)) {
self.error("invalid utf8 sequence", 1007);
return;
}
///message = messageBuffer.toString('utf8');
message = self._utf8_decoder.decode(messageBuffer).toString();
}
self.onclose(code, message, new opcOptions(self.state.masked, null, false));//{masked: self.state.masked});
self.reset();
}
};
this._handler_ping = new OpcHandler(){
@Override
public void start(ByteBuffer data) throws Exception {
if (data != null) {
String dstr = "";
for (int i = 0; i < data.capacity(); i ++)
dstr += " "+data.get(i);
debug(TAG, dstr);
}
///var self = this;
if (self.state.lastFragment == false) {
self.error("fragmented ping is not supported", 1002);
return;
}
// decode length
int firstLength = data.get(1) & 0x7f;
if (firstLength < 126) {
///opcodes['9'].getData.call(self, firstLength);
getData(firstLength);
}
else {
self.error("control frames cannot have more than 125 bytes of data", 1002);
}
}
@Override
public void getData(final int length) throws Exception {
///var self = this;
if (self.state.masked) {
/*
self.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['9'].finish.call(self, mask, data);
});
});*/
self.expectHeader(4, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
final byte[] mask = new byte[4]; ///data.array();
mask[0] = data.get(0);
mask[1] = data.get(1);
mask[2] = data.get(2);
mask[3] = data.get(3);
self.expectData(length, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data2)
throws Exception {
finish(mask, data2);
}
});
}
});
}
else {
/*
self.expectData(length, function(data) {
opcodes['9'].finish.call(self, null, data);
});*/
self.expectData(length, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
finish(null, data);
}
});
}
}
@Override
public void finish(byte[] mask, ByteBuffer data) throws Exception {
self.onping((ByteBuffer) self.unmask(mask, data, true), new opcOptions(self.state.masked, null, true));///{masked: this.state.masked, binary: true});
self.endPacket();
}
};
this._handler_pong = new OpcHandler(){
@Override
public void start(ByteBuffer data) throws Exception {
///var self = this;
if (self.state.lastFragment == false) {
self.error("fragmented pong is not supported", 1002);
return;
}
// decode length
int firstLength = data.get(1) & 0x7f;
if (firstLength < 126) {
///opcodes['10'].getData.call(self, firstLength);
getData(firstLength);
}
else {
self.error("control frames cannot have more than 125 bytes of data", 1002);
}
}
@Override
public void getData(final int length) throws Exception {
///var self = this;
if (self.state.masked) {
/*
this.expectHeader(4, function(data) {
var mask = data;
self.expectData(length, function(data) {
opcodes['10'].finish.call(self, mask, data);
});
});*/
self.expectHeader(4, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
final byte[] mask = new byte[4]; ///data.array();
mask[0] = data.get(0);
mask[1] = data.get(1);
mask[2] = data.get(2);
mask[3] = data.get(3);
self.expectData(length, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data2) throws Exception {
finish(mask, data2);
}
});
}
});
}
else {
/*
this.expectData(length, function(data) {
opcodes['10'].finish.call(self, null, data);
});*/
self.expectData(length, new PacketHandler(){
@Override
public void onPacket(ByteBuffer data) throws Exception {
finish(null, data);
}
});
}
}
@Override
public void finish(byte[] mask, ByteBuffer data) throws Exception {
self.onpong((ByteBuffer) self.unmask(mask, data, true), new opcOptions(self.state.masked, null, true));
self.endPacket();
}
};
}
/**
* Start processing a new packet.
*
* @api private
*/
private interface PacketHandler{
public void onPacket(ByteBuffer data) throws Exception;
}
private class processPacket implements PacketHandler {
@Override
public void onPacket(ByteBuffer data) throws Exception {
debug(TAG, "processPacket.onPacket: "+data);
if (data != null) {
String dstr = "";
for (int i = 0; i < data.capacity(); i ++)
dstr += " "+data.get(i);
debug(TAG, dstr);
}
///if ((data[0] & 0x70) != 0) {
if ((data.get(0) & 0x70) != 0) {
error("reserved fields must be empty", 1002);
return;
}
state.lastFragment = (data.get(0) & 0x80) == 0x80;
state.masked = (data.get(1) & 0x80) == 0x80;
int opcode = data.get(0) & 0xf;
debug(TAG, "opcode: "+opcode+", masked:"+state.masked+",lastFragment:"+state.fragmentedOperation);
if (opcode == 0) {
// continuation frame
state.fragmentedOperation = true;
state.opcode = state.activeFragmentedOperation;
if (!(state.opcode == 1 || state.opcode == 2)) {
error("continuation frame cannot follow current opcode", 1002);
return;
}
}
else {
if (opcode < 3 && state.activeFragmentedOperation != -1) {
error("data frames after the initial data frame must have opcode 0", 1002);
return;
}
state.opcode = opcode;
if (state.lastFragment == false) {
state.fragmentedOperation = true;
state.activeFragmentedOperation = opcode;
}
else state.fragmentedOperation = false;
}
OpcHandler handler = opcodes(state.opcode); ///opcodes[state.opcode];
if (handler == null) error("no handler for opcode " + state.opcode, 1002);
else {
handler.start(data);
}
}
}
/**
* Opcode handlers
*/
private interface OpcHandler {
void start(ByteBuffer data) throws Exception;
void getData(int length) throws Exception;
void finish(byte[] mask, ByteBuffer data) throws Exception;
}
private OpcHandler opcodes(int opcode) {
// text
if (opcode == 1) return _handler_text;
// binary
if (opcode == 2) return _handler_binary;
// close
if (opcode == 8) return _handler_close;
// ping
if (opcode == 9) return _handler_ping;
// pong
if (opcode == 10) return _handler_pong;
return null;
}
/**
* Unmask received data.
* @throws CharacterCodingException
*
* @api private
*/
private Object unmask(byte[] mask, ByteBuffer buf, boolean binary) throws CharacterCodingException {
if (mask != null && buf != null) return BufferUtil.unmask(buf, mask);
if (binary) return buf;
// TBD...
///return buf != null ? buf.toString("utf8") : "";
return buf != null ? _utf8_decoder.decode(buf).toString() : "";
}
/**
* Concatenates a list of buffers.
* @throws Exception
*
* @api private
*/
private ByteBuffer concatBuffers(List<Object> buffers) throws Exception {
/*
var length = 0;
for (var i = 0, l = buffers.length; i < l; ++i) length += buffers[i].length;
var mergedBuffer = new Buffer(length);
bufferUtil.merge(mergedBuffer, buffers);
return mergedBuffer;*/
return Util.concatByteBuffer(buffers, 0);
}
/**
* Handles an error
* @throws Exception
*
* @api private
*/
private Receiver error(String reason, int protocolErrorCode) throws Exception {
this.reset();
this.onerror(reason, protocolErrorCode);
return this;
}
/**
* Endprocessing a packet.
* @throws Exception
*
* @api private
*/
private void endPacket() throws Exception {
if (!this.state.fragmentedOperation) this.unfragmentedBufferPool.reset(true);
else if (this.state.lastFragment) this.fragmentedBufferPool.reset(false);
this.expectOffset = 0;
this.expectBuffer = null;
this.expectHandler = null;
if (this.state.lastFragment && this.state.opcode == this.state.activeFragmentedOperation) {
// end current fragmented operation
this.state.activeFragmentedOperation = -1; ///null;
}
this.state.lastFragment = false;
this.state.opcode = this.state.activeFragmentedOperation != -1 ? this.state.activeFragmentedOperation : 0;
this.state.masked = false;
this.expectHeader(2, new processPacket());
};
/**
* Reset the parser state.
*
* @api private
*/
private void reset() {
if (this.dead) return;
/*this.state = {
activeFragmentedOperation: null,
lastFragment: false,
masked: false,
opcode: 0,
fragmentedOperation: false
};*/
this.state = new State(-1, false, false, 0, false);
this.fragmentedBufferPool.reset(true);
this.unfragmentedBufferPool.reset(true);
this.expectOffset = 0;
this.expectBuffer = null;
this.expectHandler = null;
this.overflow = new LinkedList<ByteBuffer>();///[];
this.currentMessage = new LinkedList<Object>();///[];
}
/**
* Waits for a certain amount of header bytes to be available, then fires a callback.
* @throws Exception
*
* @api private
*/
private void expectHeader(int length, PacketHandler handler) throws Exception {
debug(TAG, "expectHeader, length:"+length+",handler:"+handler);
if (length == 0) {
handler.onPacket(null);
return;
}
this.expectBuffer = (ByteBuffer) Util.chunkSlice(this.headerBuffer, this.expectOffset, this.expectOffset + length); /// this.headerBuffer.slice(this.expectOffset, this.expectOffset + length);
this.expectHandler = handler;
int toRead = length;
while (toRead > 0 && this.overflow.size() > 0) {
ByteBuffer fromOverflow = this.overflow.remove(this.overflow.size() - 1); ///this.overflow.pop();
if (toRead < fromOverflow.capacity()) this.overflow.add((ByteBuffer) Util.chunkSlice(fromOverflow, toRead, fromOverflow.capacity())/*fromOverflow.slice(toRead)*/);
int read = Math.min(fromOverflow.capacity(), toRead);
BufferUtil.fastCopy(read, (ByteBuffer) fromOverflow, this.expectBuffer, this.expectOffset);
this.expectOffset += read;
toRead -= read;
}
debug(TAG, "expectHeader, expectBuffer:"+expectBuffer+",expectOffset:"+expectOffset);
}
/**
* Waits for a certain amount of data bytes to be available, then fires a callback.
*
* @api private
*/
private void expectData(int length, PacketHandler handler) throws Exception {
debug(TAG, "expectData, length:"+length+",handler:"+handler);
if (length == 0) {
handler.onPacket(null);
return;
}
this.expectBuffer = this.allocateFromPool(length, this.state.fragmentedOperation);
this.expectHandler = handler;
int toRead = length;
while (toRead > 0 && this.overflow.size() > 0) {
ByteBuffer fromOverflow = this.overflow.remove(this.overflow.size() - 1); ///this.overflow.pop();
if (toRead < fromOverflow.capacity()) this.overflow.add((ByteBuffer) Util.chunkSlice(fromOverflow, toRead, fromOverflow.capacity())/*fromOverflow.slice(toRead)*/);
int read = Math.min(fromOverflow.capacity(), toRead);
BufferUtil.fastCopy(read, fromOverflow, this.expectBuffer, this.expectOffset);
this.expectOffset += read;
toRead -= read;
}
debug(TAG, "expectData, expectBuffer:"+expectBuffer+",expectOffset:"+expectOffset);
}
/**
* Allocates memory from the buffer pool.
*
* @api private
*/
private ByteBuffer allocateFromPool(int length, boolean isFragmented) {
return (isFragmented ? this.fragmentedBufferPool : this.unfragmentedBufferPool).get(length);
}
/**
* Add new data to the parser.
* @throws Exception
*
* @api public
*/
protected void add(ByteBuffer data) throws Exception {
debug(TAG, "add data: "+data);
int dataLength = data!=null ? data.capacity() : 0; ///Util.chunkLength(data); ///data.length;
if (dataLength == 0) return;
if (this.expectBuffer == null) {
this.overflow.add(data);
return;
}
debug(TAG, "add data: ... 2");
int toRead = Math.min(dataLength, this.expectBuffer.capacity() - this.expectOffset);
BufferUtil.fastCopy(toRead, data, this.expectBuffer, this.expectOffset);
debug(TAG, "add data: ... 3");
this.expectOffset += toRead;
if (toRead < dataLength) {
this.overflow.add((ByteBuffer) Util.chunkSlice(data, toRead, data.capacity())/*data.slice(toRead)*/);
}
debug(TAG, "add data: ... 5");
while (this.expectBuffer!=null && this.expectOffset == this.expectBuffer.capacity()) {
ByteBuffer bufferForHandler = this.expectBuffer;
this.expectBuffer = null;
this.expectOffset = 0;
///this.expectHandler.call(this, bufferForHandler);
this.expectHandler.onPacket(bufferForHandler);
}
debug(TAG, "add data: ... 6");
}
/**
* Releases all resources used by the receiver.
*
* @api public
*/
protected void cleanup() {
this.dead = true;
this.overflow = null;
this.headerBuffer = null;
this.expectBuffer = null;
this.expectHandler = null;
this.unfragmentedBufferPool = null;
this.fragmentedBufferPool = null;
this.state = null;
this.currentMessage = null;
/*
this.onerror = null;
this.ontext = null;
this.onbinary = null;
this.onclose = null;
this.onping = null;
this.onpong = null;*/
}
// Abstract methods
protected abstract void onerror(String reason, int protocolErrorCode) throws Exception;
protected abstract void ontext(String text, opcOptions options) throws Exception;
protected abstract void onbinary(ByteBuffer buf, opcOptions options) throws Exception;
protected abstract void onclose(int code, String message, opcOptions options) throws Exception;
protected abstract void onping(ByteBuffer buf, opcOptions options) throws Exception;
protected abstract void onpong(ByteBuffer buf, opcOptions options) throws Exception;
}