From b7b96247d3b2b66d57a3ceed1752ad4e29f46f9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aslak=20Helles=C3=B8y?= Date: Mon, 10 Oct 2011 20:40:20 +0100 Subject: [PATCH] Autobahn tests - still a lot to fix. --- .gitignore | 1 + Makefile | 5 + README.md | 6 +- fuzzing_client_spec.json | 39 ++++ .../netty/Hybi10WebSocketFrameDecoder.java | 202 ++++++++---------- .../netty/Hybi10WebSocketFrameEncoder.java | 44 +--- .../org/webbitserver/netty/HybiFrame.java | 73 +++++++ .../netty/NettyWebSocketChannelHandler.java | 48 ++--- .../netty/NettyWebSocketConnection.java | 11 +- .../java/org/webbitserver/netty/Opcodes.java | 10 + .../java/org/webbitserver/netty/Ping.java | 9 - .../java/org/webbitserver/netty/Pong.java | 10 - src/test/Autobahn | 2 +- src/test/java/samples/echo/Main.java | 11 +- 14 files changed, 251 insertions(+), 220 deletions(-) create mode 100644 fuzzing_client_spec.json create mode 100644 src/main/java/org/webbitserver/netty/HybiFrame.java create mode 100644 src/main/java/org/webbitserver/netty/Opcodes.java delete mode 100644 src/main/java/org/webbitserver/netty/Ping.java delete mode 100644 src/main/java/org/webbitserver/netty/Pong.java diff --git a/.gitignore b/.gitignore index 4bf59080..a4e2db1d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ pom.xml.releaseBackup pom.xml.asc package.fig +reports diff --git a/Makefile b/Makefile index 38d283fb..be7f2daa 100644 --- a/Makefile +++ b/Makefile @@ -62,9 +62,14 @@ build/.tests-pass: build/$(LIBRARY)-tests.jar java -cp dist/$(LIBRARY).jar:build/$(LIBRARY)-tests.jar:$(CLASSPATH) org.junit.runner.JUnitCore $(call extracttests,build/$(LIBRARY)-tests.jar) @touch $@ +# Run Autobahn tests +autobahn: + PYTHONPATH=src/test/Autobahn/lib/python python src/test/Autobahn/testsuite/websockets/fuzzing_client.py + # Clean up clean: rm -rf build dist out + .PHONY: clean again: clean all diff --git a/README.md b/README.md index 46bebef6..9cd23dfe 100644 --- a/README.md +++ b/README.md @@ -94,10 +94,7 @@ We're using it to test Webbit. Installing Autobahn git submodule update --init - pushd src/test/Autobahn/lib/python easy_install twisted - python setup.py install - popd Running Autobahn tests @@ -107,8 +104,7 @@ In shell A: In shell B: - cd src/test/Autobahn/testsuite/websockets - python fuzzing_client.py + make autobahn More ----------- diff --git a/fuzzing_client_spec.json b/fuzzing_client_spec.json new file mode 100644 index 00000000..a05a9bd0 --- /dev/null +++ b/fuzzing_client_spec.json @@ -0,0 +1,39 @@ +{ + "servers": [{"agent": "AutobahnServer", "hostname": "localhost", "port": 9000, "version": 13}], + "cases": [ + "1.1.1", + "1.1.2", + "1.1.3", + "1.1.4", + "1.1.5", + "1.1.6", + "1.1.7", + "1.1.8", + "1.2.1", + "1.2.2", + "1.2.3", + "1.2.4", + "1.2.5", + "1.2.6", + "1.2.7", + "1.2.8", + "2.1", + "2.2", + "2.3", + "2.4", + "2.5", + "2.6", + "2.7", + "2.8", + "2.9", + "2.10", + "2.11", + "3.1", + "3.2", + "3.3", + "3.4", + "3.5", + "3.6", + "3.7" + ] +} \ No newline at end of file diff --git a/src/main/java/org/webbitserver/netty/Hybi10WebSocketFrameDecoder.java b/src/main/java/org/webbitserver/netty/Hybi10WebSocketFrameDecoder.java index a3063d58..ab01c187 100644 --- a/src/main/java/org/webbitserver/netty/Hybi10WebSocketFrameDecoder.java +++ b/src/main/java/org/webbitserver/netty/Hybi10WebSocketFrameDecoder.java @@ -1,40 +1,32 @@ package org.webbitserver.netty; 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.frame.CorruptedFrameException; import org.jboss.netty.handler.codec.frame.TooLongFrameException; -import org.jboss.netty.handler.codec.http.websocket.DefaultWebSocketFrame; import org.jboss.netty.handler.codec.replay.ReplayingDecoder; import java.util.ArrayList; import java.util.List; -import static java.lang.Integer.toHexString; - public class Hybi10WebSocketFrameDecoder extends ReplayingDecoder { - private static final byte OPCODE_CONT = 0x0; - private static final byte OPCODE_TEXT = 0x1; - private static final byte OPCODE_BINARY = 0x2; - private static final byte OPCODE_CLOSE = 0x8; - private static final byte OPCODE_PING = 0x9; - private static final byte OPCODE_PONG = 0xA; - - public static final int MAX_LENGTH = 16384; - - private Byte fragmentOpcode; - private Byte opcode = null; - private int currentFrameLength; + private long framePayloadLen; private ChannelBuffer maskingKey; private List frames = new ArrayList(); + private boolean isServer = true; + private boolean requireMaskedClientFrames = true; + private boolean insideMessage; + private byte frameOpcode; + private Byte fragmentOpcode; + private boolean frameFin; + private int frameRsv; + public static enum State { FRAME_START, - PARSING_LENGTH, MASKING_KEY, - PARSING_LENGTH_2, - PARSING_LENGTH_3, PAYLOAD } @@ -46,75 +38,92 @@ public Hybi10WebSocketFrameDecoder() { protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, State state) throws Exception { switch (state) { case FRAME_START: + // FIN, RSV, OPCODE byte b = buffer.readByte(); - byte fin = (byte) (b & 0x80); - byte reserved = (byte) (b & 0x70); - byte opcode = (byte) (b & 0x0F); + frameFin = (b & 0x80) != 0; + frameRsv = (b & 0x70) >> 4; + frameOpcode = (byte) (b & 0x0F); + + // MASK, PAYLOAD LEN 1 + b = buffer.readByte(); + boolean frameMasked = (b & 0x80) != 0; + int framePayloadLen1 = (b & 0x7F); - if (reserved != 0) { - throw new CorruptedFrameException("Reserved bits set: " + toZeroPaddedBinaryString(b)); + if (frameRsv != 0) { + throw new CorruptedFrameException("RSV != 0 and no extension negotiated"); } - if (!isOpcode(opcode)) { - throw new CorruptedFrameException("Invalid opcode " + toHexString(b)); + + if (isServer && requireMaskedClientFrames && !frameMasked) { + throw new CorruptedFrameException("unmasked client to server frame"); } - if (fin == 0) { - if (fragmentOpcode == null) { - if (!isDataOpcode(opcode)) { - throw new CorruptedFrameException("Fragmented frame with invalid opcode " + toHexString(opcode)); - } - fragmentOpcode = opcode; - } else if (opcode != OPCODE_CONT) { - throw new CorruptedFrameException("Continuation frame with invalid opcode " + toHexString(opcode)); + if (frameOpcode > 7) { // control frame (have MSB in opcode set) + + // control frames MUST NOT be fragmented + if (!frameFin) { + throw new CorruptedFrameException("fragmented control frame"); } - } else { - if (fragmentOpcode != null) { - if (!isControlOpcode(opcode) && opcode != OPCODE_CONT) { - throw new CorruptedFrameException("Final frame with invalid opcode " + toHexString(opcode)); - } - } else if (opcode == OPCODE_CONT) { - throw new CorruptedFrameException("Final frame with invalid opcode " + toHexString(opcode)); + + // control frames MUST have payload 125 octets or less + if (framePayloadLen1 > 125) { + throw new CorruptedFrameException("control frame with payload length > 125 octets"); } - this.opcode = opcode; - } - checkpoint(State.PARSING_LENGTH); - case PARSING_LENGTH: - b = buffer.readByte(); - byte masked = (byte) (b & 0x80); - if (masked == 0) { - throw new CorruptedFrameException("Unmasked frame received"); + // check for reserved control frame opcodes + if (!(frameOpcode == Opcodes.OPCODE_CLOSE || frameOpcode == Opcodes.OPCODE_PING || frameOpcode == Opcodes.OPCODE_PONG)) { + throw new CorruptedFrameException("control frame using reserved opcode " + frameOpcode); + } + + // close frame : if there is a body, the first two bytes of the body MUST be a 2-byte + // unsigned integer (in network byte order) representing a status code + if (frameOpcode == 8 && framePayloadLen1 == 1) { + throw new CorruptedFrameException("received close control frame with payload len 1"); + } + } else { // data frame + // check for reserved data frame opcodes + if (!(frameOpcode == Opcodes.OPCODE_CONT || frameOpcode == Opcodes.OPCODE_TEXT || frameOpcode == Opcodes.OPCODE_BINARY)) { + throw new CorruptedFrameException("data frame using reserved opcode " + frameOpcode); + } + +// // check opcode vs message fragmentation state 1/2 +// if (!insideMessage && frameOpcode == OPCODE_CONT) { +// throw new CorruptedFrameException("received continuation data frame outside fragmented message"); +// } +// +// // check opcode vs message fragmentation state 2/2 +// if (insideMessage && frameOpcode != OPCODE_CONT) { +// throw new CorruptedFrameException("received non-continuation data frame while inside fragmented message"); +// } } - int length = (byte) (b & 0x7F); + int maskLen = frameMasked ? 4 : 0; + + if (framePayloadLen1 == 126) { + framePayloadLen = buffer.readUnsignedShort(); + if (framePayloadLen < 126) { + throw new CorruptedFrameException("invalid data frame length (not using minimal length encoding)"); + } + } else if (framePayloadLen1 == 127) { + framePayloadLen = buffer.readLong(); + // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe just check if it's negative? - if (length < 126) { - currentFrameLength = length; - checkpoint(State.MASKING_KEY); - } else if (length == 126) { - checkpoint(State.PARSING_LENGTH_2); - } else if (length == 127) { - checkpoint(State.PARSING_LENGTH_3); + if (framePayloadLen < 65536) { + throw new CorruptedFrameException("invalid data frame length (not using minimal length encoding)"); + } + } else { + framePayloadLen = framePayloadLen1; } - return null; - case PARSING_LENGTH_2: - currentFrameLength = buffer.readShort() & 0xFFFF; - checkpoint(State.MASKING_KEY); - return null; - case PARSING_LENGTH_3: - currentFrameLength = buffer.readInt() & 0xFFFFFFFF; checkpoint(State.MASKING_KEY); return null; case MASKING_KEY: maskingKey = buffer.readBytes(4); checkpoint(State.PAYLOAD); case PAYLOAD: - ChannelBuffer frame = buffer.readBytes(currentFrameLength); - checkpoint(State.FRAME_START); + ChannelBuffer frame = buffer.readBytes(toFrameLength(framePayloadLen)); unmask(frame); - if (this.opcode == OPCODE_CONT) { - this.opcode = fragmentOpcode; + if (frameOpcode == Opcodes.OPCODE_CONT) { + frameOpcode = fragmentOpcode; frames.add(frame); frame = channel.getConfig().getBufferFactory().getBuffer(0); @@ -125,57 +134,36 @@ protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffe this.fragmentOpcode = null; frames.clear(); - } - - if (this.opcode == OPCODE_TEXT) { - if (frame.readableBytes() > MAX_LENGTH) { - throw new TooLongFrameException(); + } else { + checkpoint(State.FRAME_START); + if (frameOpcode == Opcodes.OPCODE_TEXT || frameOpcode == Opcodes.OPCODE_BINARY || frameOpcode == Opcodes.OPCODE_PONG) { + return new HybiFrame(frameOpcode, frameFin, frameRsv, frame); + } else if (frameOpcode == Opcodes.OPCODE_PING) { + channel.write(new HybiFrame(Opcodes.OPCODE_PONG, true, 0, frame)); + return null; + } else if (frameOpcode == Opcodes.OPCODE_CLOSE) { + channel.write(new HybiFrame(Opcodes.OPCODE_CLOSE, true, 0, ChannelBuffers.buffer(0))); + channel.close(); + return null; } - return new DefaultWebSocketFrame(0x00, frame); - } else if (this.opcode == OPCODE_BINARY) { - return new DefaultWebSocketFrame(0xFF, frame); - } else if (this.opcode == OPCODE_PING) { - channel.write(new Pong(0x00, frame)); - return null; - } else if (this.opcode == OPCODE_PONG) { - return new Pong(0x00, frame); - } else if (this.opcode == OPCODE_CLOSE) { - // TODO - return null; } default: throw new Error("Shouldn't reach here."); } } + private int toFrameLength(long l) throws TooLongFrameException { + if (l > Integer.MAX_VALUE) { + throw new TooLongFrameException("Length:" + l); + } else { + return (int) l; + } + } + private void unmask(ChannelBuffer frame) { byte[] bytes = frame.array(); for (int i = 0; i < bytes.length; i++) { frame.setByte(i, frame.getByte(i) ^ maskingKey.getByte(i % 4)); } } - - private String toZeroPaddedBinaryString(byte b) { - return String.format("%8s", Integer.toBinaryString(b)).replace(" ", "0"); - } - - private boolean isOpcode(int opcode) { - return opcode == OPCODE_CONT || - opcode == OPCODE_TEXT || - opcode == OPCODE_BINARY || - opcode == OPCODE_CLOSE || - opcode == OPCODE_PING || - opcode == OPCODE_PONG; - } - - private boolean isControlOpcode(int opcode) { - return opcode == OPCODE_CLOSE || - opcode == OPCODE_PING || - opcode == OPCODE_PONG; - } - - private boolean isDataOpcode(int opcode) { - return opcode == OPCODE_TEXT || - opcode == OPCODE_BINARY; - } } diff --git a/src/main/java/org/webbitserver/netty/Hybi10WebSocketFrameEncoder.java b/src/main/java/org/webbitserver/netty/Hybi10WebSocketFrameEncoder.java index 74fb64c1..145718d4 100644 --- a/src/main/java/org/webbitserver/netty/Hybi10WebSocketFrameEncoder.java +++ b/src/main/java/org/webbitserver/netty/Hybi10WebSocketFrameEncoder.java @@ -1,53 +1,15 @@ package org.webbitserver.netty; -import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; -import org.jboss.netty.handler.codec.http.websocket.WebSocketFrame; import org.jboss.netty.handler.codec.oneone.OneToOneEncoder; -/** - * 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 WebSocketFrame) { - WebSocketFrame frame = (WebSocketFrame) msg; - ChannelBuffer data = frame.getBinaryData(); - ChannelBuffer encoded = - channel.getConfig().getBufferFactory().getBuffer( - data.order(), data.readableBytes() + 6); - - byte opcode; - if(frame instanceof Ping) { - opcode = OPCODE_PING; - } else if(frame instanceof Pong) { - opcode = OPCODE_PONG; - } else { - opcode = frame.isText() ? OPCODE_TEXT : OPCODE_BINARY; - } - encoded.writeByte(0x80 | opcode); - - int length = data.readableBytes(); - if (length < 126) { - encoded.writeByte(length); - } else if (length < 65535) { - encoded.writeByte(126); - encoded.writeShort(length); - } else { - encoded.writeByte(127); - encoded.writeInt(length); - } - - encoded.writeBytes(data, data.readerIndex(), data.readableBytes()); - encoded = encoded.slice(0, encoded.writerIndex()); - return encoded; + if (msg instanceof HybiFrame) { + HybiFrame frame = (HybiFrame) msg; + return frame.encode(channel); } return msg; } diff --git a/src/main/java/org/webbitserver/netty/HybiFrame.java b/src/main/java/org/webbitserver/netty/HybiFrame.java new file mode 100644 index 00000000..83051e30 --- /dev/null +++ b/src/main/java/org/webbitserver/netty/HybiFrame.java @@ -0,0 +1,73 @@ +package org.webbitserver.netty; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.webbitserver.WebSocketHandler; + +import java.nio.charset.Charset; + +public class HybiFrame { + + public HybiFrame(int opcode, boolean fin, int rsv, ChannelBuffer data) { + this.opcode = opcode; + this.fin = fin; + this.rsv = rsv; + this.data = data; + } + + private final int opcode; + private final boolean fin; + private final int rsv; + private final ChannelBuffer data; + + public ChannelBuffer encode(Channel channel) { + int b0 = 0; + if (fin) { + b0 |= (1 << 7); + } + b0 |= (rsv % 8) << 4; + b0 |= opcode % 128; + + ChannelBuffer buffer; + int length = data.readableBytes(); + if (length <= 125) { + buffer = createBuffer(channel, data, 2); + buffer.writeByte(b0); + buffer.writeByte(length); + } else if (length <= 0xFFFF) { + buffer = createBuffer(channel, data, 4); + buffer.writeByte(b0); + buffer.writeByte(126); + buffer.writeByte((length >>> 8) & 0xFF); + buffer.writeByte((length) & 0xFF); + } else { + buffer = createBuffer(channel, data, 10); + buffer.writeByte(b0); + buffer.writeByte(127); + buffer.writeLong(length); + } + + buffer.writeBytes(data, data.readerIndex(), data.readableBytes()); + return buffer; + } + + private ChannelBuffer createBuffer(Channel channel, ChannelBuffer data, int headerLength) { + return channel.getConfig().getBufferFactory().getBuffer(data.order(), data.readableBytes() + headerLength); + } + + public void dispatch(WebSocketHandler handler, NettyWebSocketConnection connection) throws Throwable { + switch(opcode) { + case Opcodes.OPCODE_TEXT: + handler.onMessage(connection, data.toString(Charset.forName("UTF-8"))); + return; + case Opcodes.OPCODE_BINARY: + handler.onMessage(connection, data.array()); + return; + case Opcodes.OPCODE_PONG: + handler.onPong(connection, data.toString(Charset.forName("UTF-8"))); + return; + default: + throw new IllegalStateException("Unexpected opcode:" + opcode); + } + } +} diff --git a/src/main/java/org/webbitserver/netty/NettyWebSocketChannelHandler.java b/src/main/java/org/webbitserver/netty/NettyWebSocketChannelHandler.java index f7680e71..064cbeb0 100644 --- a/src/main/java/org/webbitserver/netty/NettyWebSocketChannelHandler.java +++ b/src/main/java/org/webbitserver/netty/NettyWebSocketChannelHandler.java @@ -2,17 +2,11 @@ import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; -import org.jboss.netty.channel.ChannelHandlerContext; -import org.jboss.netty.channel.ChannelPipeline; -import org.jboss.netty.channel.ChannelStateEvent; -import org.jboss.netty.channel.ExceptionEvent; -import org.jboss.netty.channel.MessageEvent; -import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.jboss.netty.channel.*; import org.jboss.netty.handler.codec.http.HttpHeaders; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; -import org.jboss.netty.handler.codec.http.websocket.WebSocketFrame; import org.jboss.netty.handler.codec.http.websocket.WebSocketFrameDecoder; import org.jboss.netty.handler.codec.http.websocket.WebSocketFrameEncoder; import org.webbitserver.WebSocketConnection; @@ -26,28 +20,20 @@ import java.security.NoSuchAlgorithmException; import java.util.concurrent.Executor; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONNECTION; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.ORIGIN; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_KEY1; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_KEY2; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_LOCATION; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_ORIGIN; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_PROTOCOL; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.UPGRADE; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.WEBSOCKET_LOCATION; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.WEBSOCKET_ORIGIN; -import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.WEBSOCKET_PROTOCOL; +import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*; import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET; public class NettyWebSocketChannelHandler extends SimpleChannelUpstreamHandler { private static final MessageDigest SHA_1; + static { try { SHA_1 = MessageDigest.getInstance("SHA1"); } catch (NoSuchAlgorithmException e) { throw new InternalError("SHA-1 not supported on this platform"); } - } + } + private static final Charset ASCII = Charset.forName("ASCII"); protected final Executor executor; protected final NettyHttpRequest nettyHttpRequest; @@ -56,6 +42,7 @@ public class NettyWebSocketChannelHandler extends SimpleChannelUpstreamHandler { protected final Thread.UncaughtExceptionHandler ioExceptionHandler; protected final WebSocketHandler handler; private static final String ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + private static final int MIN_HYBI_VERSION = 8; public NettyWebSocketChannelHandler( Executor executor, @@ -163,15 +150,15 @@ private boolean isHixie76WebSocketRequest(HttpRequest req) { } private void upgradeResponseHybi10(HttpRequest req, HttpResponse res) { - String version = req.getHeader("Sec-WebSocket-Version"); - if(!"8".equals(version)) { + int version = Integer.parseInt(req.getHeader("Sec-WebSocket-Version").trim()); + if (version < MIN_HYBI_VERSION) { res.setStatus(HttpResponseStatus.UPGRADE_REQUIRED); - res.setHeader("Sec-WebSocket-Version", "8"); + res.setHeader("Sec-WebSocket-Version", String.valueOf(MIN_HYBI_VERSION)); return; } String key = req.getHeader("Sec-WebSocket-Key"); - if(key == null) { + if (key == null) { res.setStatus(HttpResponseStatus.BAD_REQUEST); return; } @@ -240,19 +227,10 @@ public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) thr @Override public void run() { try { - if(e.getMessage() instanceof Pong) { - handler.onPong(webSocketConnection, ((WebSocketFrame) e.getMessage()).getTextData()); - } else { - WebSocketFrame frame = (WebSocketFrame) e.getMessage(); - if(frame.isText()) { - handler.onMessage(webSocketConnection, frame.getTextData()); - } else { - handler.onMessage(webSocketConnection, frame.getBinaryData().array()); - } - } + HybiFrame frame = (HybiFrame) e.getMessage(); + frame.dispatch(handler, webSocketConnection); } catch (Throwable t) { - // TODO - t.printStackTrace(); + exceptionHandler.uncaughtException(Thread.currentThread(), t); } } }); diff --git a/src/main/java/org/webbitserver/netty/NettyWebSocketConnection.java b/src/main/java/org/webbitserver/netty/NettyWebSocketConnection.java index 1b18122e..615ed47c 100644 --- a/src/main/java/org/webbitserver/netty/NettyWebSocketConnection.java +++ b/src/main/java/org/webbitserver/netty/NettyWebSocketConnection.java @@ -2,10 +2,9 @@ import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.ChannelHandlerContext; -import org.jboss.netty.handler.codec.http.websocket.DefaultWebSocketFrame; -import org.jboss.netty.handler.codec.http.websocket.WebSocketFrame; import org.webbitserver.WebSocketConnection; +import java.nio.charset.Charset; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; @@ -30,20 +29,20 @@ public NettyHttpRequest httpRequest() { @Override public NettyWebSocketConnection send(String message) { - return send(new DefaultWebSocketFrame(message)); + return send(new HybiFrame(Opcodes.OPCODE_TEXT, true, 0, ChannelBuffers.wrappedBuffer(message.getBytes(Charset.forName("UTF-8"))))); } @Override public NettyWebSocketConnection send(byte[] message) { - return send(new DefaultWebSocketFrame(0xFF, ChannelBuffers.wrappedBuffer(message))); + return send(new HybiFrame(Opcodes.OPCODE_BINARY, true, 0, ChannelBuffers.wrappedBuffer(message))); } @Override public NettyWebSocketConnection ping(String message) { - return send(new Ping(message)); + return send(new HybiFrame(Opcodes.OPCODE_PING, true, 0, ChannelBuffers.wrappedBuffer(message.getBytes(Charset.forName("UTF-8"))))); } - private NettyWebSocketConnection send(WebSocketFrame frame) { + private NettyWebSocketConnection send(HybiFrame frame) { ctx.getChannel().write(frame); return this; } diff --git a/src/main/java/org/webbitserver/netty/Opcodes.java b/src/main/java/org/webbitserver/netty/Opcodes.java new file mode 100644 index 00000000..55d60291 --- /dev/null +++ b/src/main/java/org/webbitserver/netty/Opcodes.java @@ -0,0 +1,10 @@ +package org.webbitserver.netty; + +public interface Opcodes { + static final int OPCODE_CONT = 0x0; + static final int OPCODE_TEXT = 0x1; + static final int OPCODE_BINARY = 0x2; + static final int OPCODE_CLOSE = 0x8; + static final int OPCODE_PING = 0x9; + static final int OPCODE_PONG = 0xA; +} diff --git a/src/main/java/org/webbitserver/netty/Ping.java b/src/main/java/org/webbitserver/netty/Ping.java deleted file mode 100644 index ddbc863a..00000000 --- a/src/main/java/org/webbitserver/netty/Ping.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.webbitserver.netty; - -import org.jboss.netty.handler.codec.http.websocket.DefaultWebSocketFrame; - -public class Ping extends DefaultWebSocketFrame { - public Ping(String message) { - super(message); - } -} diff --git a/src/main/java/org/webbitserver/netty/Pong.java b/src/main/java/org/webbitserver/netty/Pong.java deleted file mode 100644 index c04e1839..00000000 --- a/src/main/java/org/webbitserver/netty/Pong.java +++ /dev/null @@ -1,10 +0,0 @@ -package org.webbitserver.netty; - -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.handler.codec.http.websocket.DefaultWebSocketFrame; - -public class Pong extends DefaultWebSocketFrame { - public Pong(int type, ChannelBuffer frame) { - super(type, frame); - } -} diff --git a/src/test/Autobahn b/src/test/Autobahn index 3fcf654c..d7ab538f 160000 --- a/src/test/Autobahn +++ b/src/test/Autobahn @@ -1 +1 @@ -Subproject commit 3fcf654c9599619ae1c94e0e3234c0487dbe6aa2 +Subproject commit d7ab538f67e60ff0d53205f4923bd0e3b57fbbd2 diff --git a/src/test/java/samples/echo/Main.java b/src/test/java/samples/echo/Main.java index 33e640e3..07147f0c 100644 --- a/src/test/java/samples/echo/Main.java +++ b/src/test/java/samples/echo/Main.java @@ -16,30 +16,29 @@ public static void main(String[] args) throws Exception { WebServer webServer = createWebServer(9000).add(new HttpToWebSocketHandler(new WebSocketHandler() { @Override public void onOpen(WebSocketConnection connection) throws Exception { - System.out.println("connection = " + connection.version()); - System.out.print("O"); + System.out.print("OPEN"); } @Override public void onClose(WebSocketConnection connection) throws Exception { - System.out.print("C"); + System.out.println(" CLOSE"); } @Override public void onMessage(WebSocketConnection connection, String msg) throws Exception { - System.out.print("S"); + System.out.print(" STRING LENGTH:" + msg.length()); connection.send(msg); } @Override public void onMessage(WebSocketConnection connection, byte[] msg) { - System.out.print("B"); + System.out.print(" BINARY LENGTH:" + msg.length); connection.send(msg); } @Override public void onPong(WebSocketConnection connection, String msg) { - System.out.print("P"); + System.out.print(" PONG"); connection.ping(msg); } })).start();