package play.server.hybi10;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.http.websocket.DefaultWebSocketFrame;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
import play.Logger;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
/**
* Encodes frames going out. Frames are not masked.
*/
public class Hybi10WebSocketFrameEncoder extends OneToOneEncoder {
private static final byte OPCODE_TEXT = 0x1;
private static final byte OPCODE_BINARY = 0x2;
private static final byte OPCODE_PING = 0x9;
private static final byte OPCODE_PONG = 0xA;
@Override
protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
if (msg instanceof DefaultWebSocketFrame) {
final DefaultWebSocketFrame frame = (DefaultWebSocketFrame) msg;
ChannelBuffer data = frame.getBinaryData();
if (data == null) {
data = ChannelBuffers.EMPTY_BUFFER;
}
byte opcode;
// TODO: Close and CONTINUATION
if(frame instanceof Ping) {
opcode = OPCODE_PING;
} else if(frame instanceof Pong) {
opcode = OPCODE_PONG;
} else {
opcode = frame.isText() ? OPCODE_TEXT : OPCODE_BINARY;
}
int length = data.readableBytes();
int b0 = 0;
b0 |= (1 << 7);
// TODO: RSV, for now it is set to 0
b0 |= (0 % 8) << 4;
b0 |= opcode % 128;
ChannelBuffer header;
ChannelBuffer body;
// TODO: if there is no mask
int maskLength = 4;
if (length <= 125) {
header = ChannelBuffers.buffer(2 + maskLength);
header.writeByte(b0);
byte b = (byte) (0x80 | (byte) length);
header.writeByte(b);
} else if (length <= 0xFFFF) {
header = ChannelBuffers.buffer(4 + maskLength);
header.writeByte(b0);
header.writeByte(0x80 | 126);
header.writeByte((length >>> 8) & 0xFF);
header.writeByte((length) & 0xFF);
} else {
header = ChannelBuffers.buffer(10 + maskLength);
header.writeByte(b0);
header.writeByte(0x80 | 127);
header.writeLong(length);
}
Integer random = (int) (Math.random() * Integer.MAX_VALUE);
byte[] mask = ByteBuffer.allocate(4).putInt(random).array();
header.writeBytes(mask);
body = ChannelBuffers.buffer(length);
int counter = 0;
while (data.readableBytes() > 0) {
byte byteData = data.readByte();
body.writeByte(byteData ^ mask[+counter++ % 4]);
}
return ChannelBuffers.wrappedBuffer(header, body);
}
return msg;
}
}