More AB testing, reworked for Fuzzer

This commit is contained in:
Joakim Erdfelt 2012-07-31 12:52:22 -07:00
parent 9a34c08b8e
commit 9a568514a6
11 changed files with 1048 additions and 550 deletions

View File

@ -54,16 +54,19 @@ public class Parser
private ByteBuffer payload; private ByteBuffer payload;
private int payloadLength; private int payloadLength;
/** Is there an extension using RSV1 */
private boolean rsv1InUse = false;
/** Is there an extension using RSV2 */
private boolean rsv2InUse = false;
/** Is there an extension using RSV3 */
private boolean rsv3InUse = false;
private static final Logger LOG = Log.getLogger(Parser.class); private static final Logger LOG = Log.getLogger(Parser.class);
private IncomingFrames incomingFramesHandler; private IncomingFrames incomingFramesHandler;
private WebSocketPolicy policy; private WebSocketPolicy policy;
public Parser(WebSocketPolicy wspolicy) public Parser(WebSocketPolicy wspolicy)
{ {
/*
* TODO: Investigate addition of decompression factory similar to SPDY work in situation of negotiated deflate extension?
*/
this.policy = wspolicy; this.policy = wspolicy;
} }
@ -107,6 +110,21 @@ public class Parser
return policy; return policy;
} }
public boolean isRsv1InUse()
{
return rsv1InUse;
}
public boolean isRsv2InUse()
{
return rsv2InUse;
}
public boolean isRsv3InUse()
{
return rsv3InUse;
}
protected void notifyFrame(final WebSocketFrame f) protected void notifyFrame(final WebSocketFrame f)
{ {
if (LOG_FRAMES.isDebugEnabled()) if (LOG_FRAMES.isDebugEnabled())
@ -228,6 +246,27 @@ public class Parser
LOG.debug("OpCode {}, fin={}",opcode.name(),fin); LOG.debug("OpCode {}, fin={}",opcode.name(),fin);
/*
* RFC 6455 Section 5.2
*
* MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. If a nonzero value is received and none of the
* negotiated extensions defines the meaning of such a nonzero value, the receiving endpoint MUST _Fail the WebSocket Connection_.
*/
if (!rsv1InUse && rsv1)
{
throw new ProtocolException("RSV1 not allowed to be set");
}
if (!rsv2InUse && rsv2)
{
throw new ProtocolException("RSV2 not allowed to be set");
}
if (!rsv3InUse && rsv3)
{
throw new ProtocolException("RSV3 not allowed to be set");
}
if (opcode.isControlFrame() && !fin) if (opcode.isControlFrame() && !fin)
{ {
throw new ProtocolException("Fragmented Control Frame [" + opcode.name() + "]"); throw new ProtocolException("Fragmented Control Frame [" + opcode.name() + "]");
@ -442,6 +481,21 @@ public class Parser
this.incomingFramesHandler = incoming; this.incomingFramesHandler = incoming;
} }
public void setRsv1InUse(boolean rsv1InUse)
{
this.rsv1InUse = rsv1InUse;
}
public void setRsv2InUse(boolean rsv2InUse)
{
this.rsv2InUse = rsv2InUse;
}
public void setRsv3InUse(boolean rsv3InUse)
{
this.rsv3InUse = rsv3InUse;
}
@Override @Override
public String toString() public String toString()
{ {

View File

@ -388,14 +388,17 @@ public class WebSocketServerFactory extends AbstractLifeCycle implements WebSock
if (ext.useRsv1()) if (ext.useRsv1())
{ {
connection.getGenerator().setRsv1InUse(true); connection.getGenerator().setRsv1InUse(true);
connection.getParser().setRsv1InUse(true);
} }
if (ext.useRsv2()) if (ext.useRsv2())
{ {
connection.getGenerator().setRsv2InUse(true); connection.getGenerator().setRsv2InUse(true);
connection.getParser().setRsv2InUse(true);
} }
if (ext.useRsv3()) if (ext.useRsv3())
{ {
connection.getGenerator().setRsv3InUse(true); connection.getGenerator().setRsv3InUse(true);
connection.getParser().setRsv3InUse(true);
} }
} }

View File

@ -42,6 +42,11 @@ public class ByteBufferAssert
public static void assertEquals(String message, ByteBuffer expectedBuffer, ByteBuffer actualBuffer) public static void assertEquals(String message, ByteBuffer expectedBuffer, ByteBuffer actualBuffer)
{ {
if (expectedBuffer == null)
{
Assert.assertThat(message,actualBuffer,nullValue());
return;
}
byte expectedBytes[] = BufferUtil.toArray(expectedBuffer); byte expectedBytes[] = BufferUtil.toArray(expectedBuffer);
byte actualBytes[] = BufferUtil.toArray(actualBuffer); byte actualBytes[] = BufferUtil.toArray(actualBuffer);
assertEquals(message,expectedBytes,actualBytes); assertEquals(message,expectedBytes,actualBytes);

View File

@ -44,6 +44,16 @@ public abstract class AbstractABCase
server.stop(); server.stop();
} }
public Generator getLaxGenerator()
{
return laxGenerator;
}
public SimpleServletServer getServer()
{
return server;
}
protected byte[] masked(final byte[] data) protected byte[] masked(final byte[] data)
{ {
int len = data.length; int len = data.length;

View File

@ -5,7 +5,7 @@ import org.junit.runners.Suite;
@RunWith(Suite.class) @RunWith(Suite.class)
@Suite.SuiteClasses( @Suite.SuiteClasses(
{ TestABCase1.class, TestABCase5.class, TestABCase7_9.class }) { TestABCase1.class, TestABCase2.class, TestABCase3.class, TestABCase5.class, TestABCase7_9.class })
public class AllTests public class AllTests
{ {
/* let junit do the rest */ /* let junit do the rest */

View File

@ -0,0 +1,172 @@
package org.eclipse.jetty.websocket.server.ab;
import static org.hamcrest.Matchers.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.protocol.CloseInfo;
import org.eclipse.jetty.websocket.protocol.Generator;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
import org.eclipse.jetty.websocket.server.ByteBufferAssert;
import org.eclipse.jetty.websocket.server.blockhead.BlockheadClient;
import org.eclipse.jetty.websocket.server.helper.IncomingFramesCapture;
import org.junit.Assert;
/**
* Fuzzing utility for the AB tests.
*/
public class Fuzzer
{
public static enum SendMode
{
BULK,
PER_FRAME,
SLOW
}
private static final Logger LOG = Log.getLogger(Fuzzer.class);
// Client side framing mask
protected static final byte[] MASK =
{ 0x11, 0x22, 0x33, 0x44 };
private final BlockheadClient client;
private final Generator generator;
private SendMode sendMode = SendMode.BULK;
private int slowSendSegmentSize = 5;
public Fuzzer(AbstractABCase testcase) throws Exception
{
this.client = new BlockheadClient(testcase.getServer().getServerUri());
this.generator = testcase.getLaxGenerator();
}
public void close()
{
this.client.disconnect();
}
public void connect() throws IOException
{
if (!client.isConnected())
{
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
}
}
public void expect(List<WebSocketFrame> expect) throws IOException, TimeoutException
{
int expectedCount = expect.size();
// Read frames
IncomingFramesCapture capture = client.readFrames(expect.size(),TimeUnit.MILLISECONDS,500);
String prefix = "";
for (int i = 0; i < expectedCount; i++)
{
WebSocketFrame expected = expect.get(i);
WebSocketFrame actual = capture.getFrames().pop();
prefix = "Frame[" + i + "]";
Assert.assertThat(prefix + ".opcode",actual.getOpCode(),is(expected.getOpCode()));
prefix += "/" + actual.getOpCode();
if (expected.getOpCode() == OpCode.CLOSE)
{
CloseInfo expectedClose = new CloseInfo(expected);
CloseInfo actualClose = new CloseInfo(actual);
Assert.assertThat(prefix + ".statusCode",actualClose.getStatusCode(),is(expectedClose.getStatusCode()));
}
else
{
Assert.assertThat(prefix + ".payloadLength",actual.getPayloadLength(),is(expected.getPayloadLength()));
ByteBufferAssert.assertEquals(prefix + ".payload",expected.getPayload(),actual.getPayload());
}
}
}
public void expect(WebSocketFrame expect) throws IOException, TimeoutException
{
expect(Collections.singletonList(expect));
}
public SendMode getSendMode()
{
return sendMode;
}
public int getSlowSendSegmentSize()
{
return slowSendSegmentSize;
}
public void send(List<WebSocketFrame> send) throws IOException
{
Assert.assertThat("Client connected",client.isConnected(),is(true));
LOG.debug("Sending {} frames (mode {})",send.size(),sendMode);
if ((sendMode == SendMode.BULK) || (sendMode == SendMode.SLOW))
{
int buflen = 0;
for (WebSocketFrame f : send)
{
buflen += f.getPayloadLength() + Generator.OVERHEAD;
}
ByteBuffer buf = ByteBuffer.allocate(buflen);
BufferUtil.clearToFill(buf);
// Generate frames
for (WebSocketFrame f : send)
{
f.setMask(MASK); // make sure we have mask set
BufferUtil.put(generator.generate(f),buf);
}
BufferUtil.flipToFlush(buf,0);
// Write Data Frame
switch (sendMode)
{
case BULK:
client.writeRaw(buf);
break;
case SLOW:
client.writeRawSlowly(buf,slowSendSegmentSize);
break;
}
}
else if (sendMode == SendMode.PER_FRAME)
{
for (WebSocketFrame f : send)
{
// Using lax generator, generate and send
client.writeRaw(generator.generate(f));
client.flush();
}
}
}
public void send(WebSocketFrame send) throws IOException
{
send(Collections.singletonList(send));
}
public void setSendMode(SendMode sendMode)
{
this.sendMode = sendMode;
}
public void setSlowSendSegmentSize(int segmentSize)
{
this.slowSendSegmentSize = segmentSize;
}
}

View File

@ -15,201 +15,44 @@
//======================================================================== //========================================================================
package org.eclipse.jetty.websocket.server.ab; package org.eclipse.jetty.websocket.server.ab;
import static org.hamcrest.Matchers.*; import java.util.ArrayList;
import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.TimeUnit; import java.util.List;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.protocol.CloseInfo; import org.eclipse.jetty.websocket.protocol.CloseInfo;
import org.eclipse.jetty.websocket.protocol.Generator;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame; import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
import org.eclipse.jetty.websocket.server.ByteBufferAssert; import org.eclipse.jetty.websocket.server.ab.Fuzzer.SendMode;
import org.eclipse.jetty.websocket.server.blockhead.BlockheadClient;
import org.eclipse.jetty.websocket.server.helper.IncomingFramesCapture;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
public class TestABCase1 extends AbstractABCase public class TestABCase1 extends AbstractABCase
{ {
private void assertEchoEmptyFrame(OpCode opcode) throws Exception
{
BlockheadClient client = new BlockheadClient(server.getServerUri());
try
{
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
ByteBuffer buf = ByteBuffer.allocate(Generator.OVERHEAD);
BufferUtil.clearToFill(buf);
// Prepare Frame
buf.put((byte)(0x00 | FIN | opcode.getCode()));
putPayloadLength(buf,0);
putMask(buf);
// Write Data Frame
BufferUtil.flipToFlush(buf,0);
client.writeRaw(buf);
// Prepare Close Frame
CloseInfo close = new CloseInfo(StatusCode.NORMAL);
buf = strictGenerator.generate(close.asFrame());
// Write Close Frame
client.writeRaw(buf);
client.flush();
// Read frames
IncomingFramesCapture capture = client.readFrames(2,TimeUnit.MILLISECONDS,500);
// Validate echo'd frame
WebSocketFrame frame = capture.getFrames().get(0);
Assert.assertThat("frame should be " + opcode + " frame",frame.getOpCode(),is(opcode));
Assert.assertThat(opcode + ".payloadLength",frame.getPayloadLength(),is(0));
// Validate close
frame = capture.getFrames().get(1);
Assert.assertThat("CLOSE.frame.opcode",frame.getOpCode(),is(OpCode.CLOSE));
close = new CloseInfo(frame);
Assert.assertThat("CLOSE.statusCode",close.getStatusCode(),is(StatusCode.NORMAL));
}
finally
{
client.disconnect();
}
}
private void assertEchoFrame(OpCode opcode, byte[] payload) throws Exception
{
BlockheadClient client = new BlockheadClient(server.getServerUri());
try
{
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
ByteBuffer buf = ByteBuffer.allocate(payload.length + Generator.OVERHEAD);
BufferUtil.clearToFill(buf);
// Prepare Frame
buf.put((byte)(0x00 | FIN | opcode.getCode()));
putPayloadLength(buf,payload.length);
putMask(buf);
buf.put(masked(payload));
// Write Data Frame
BufferUtil.flipToFlush(buf,0);
client.writeRaw(buf);
// Prepare Close Frame
CloseInfo close = new CloseInfo(StatusCode.NORMAL);
WebSocketFrame closeFrame = close.asFrame();
closeFrame.setMask(MASK);
buf = strictGenerator.generate(closeFrame);
// Write Close Frame
client.writeRaw(buf);
client.flush();
// Read frames
IncomingFramesCapture capture = client.readFrames(2,TimeUnit.MILLISECONDS,1000);
// Validate echo'd frame
WebSocketFrame frame = capture.getFrames().get(0);
Assert.assertThat("frame should be " + opcode + " frame",frame.getOpCode(),is(opcode));
Assert.assertThat(opcode + ".payloadLength",frame.getPayloadLength(),is(payload.length));
ByteBufferAssert.assertEquals(opcode + ".payload",payload,frame.getPayload());
// Validate close
frame = capture.getFrames().get(1);
Assert.assertThat("CLOSE.frame.opcode",frame.getOpCode(),is(OpCode.CLOSE));
close = new CloseInfo(frame);
Assert.assertThat("CLOSE.statusCode",close.getStatusCode(),is(StatusCode.NORMAL));
}
finally
{
client.disconnect();
}
}
private void assertEchoSegmentedFrame(OpCode opcode, byte payload[], int segmentSize) throws Exception
{
BlockheadClient client = new BlockheadClient(server.getServerUri());
try
{
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
ByteBuffer buf = ByteBuffer.allocate(payload.length + Generator.OVERHEAD);
BufferUtil.clearToFill(buf);
// Prepare Frame
buf.put((byte)(0x00 | FIN | opcode.getCode()));
putPayloadLength(buf,payload.length);
putMask(buf);
buf.put(masked(payload));
// Write frame, in small blocks of segmentSize
BufferUtil.flipToFlush(buf,0);
int origLimit = buf.limit();
int limit = buf.limit();
int len;
int pos = buf.position();
int overallLeft = buf.remaining();
while (overallLeft > 0)
{
buf.position(pos);
limit = Math.min(origLimit,pos + segmentSize);
buf.limit(limit);
len = buf.remaining();
overallLeft -= len;
pos += len;
client.writeRaw(buf);
client.flush();
}
// Prepare Close Frame
CloseInfo close = new CloseInfo(StatusCode.NORMAL);
buf = strictGenerator.generate(close.asFrame());
// Write Close Frame
client.writeRaw(buf);
client.flush();
// Read frames
IncomingFramesCapture capture = client.readFrames(2,TimeUnit.MILLISECONDS,500);
// Validate echo'd frame
WebSocketFrame frame = capture.getFrames().get(0);
Assert.assertThat("frame should be " + opcode + " frame",frame.getOpCode(),is(opcode));
Assert.assertThat(opcode + ".payloadLength",frame.getPayloadLength(),is(payload.length));
ByteBufferAssert.assertEquals(opcode + ".payload",payload,frame.getPayload());
// Validate close
frame = capture.getFrames().get(1);
Assert.assertThat("CLOSE.frame.opcode",frame.getOpCode(),is(OpCode.CLOSE));
close = new CloseInfo(frame);
Assert.assertThat("CLOSE.statusCode",close.getStatusCode(),is(StatusCode.NORMAL));
}
finally
{
client.disconnect();
}
}
/** /**
* Echo 0 byte TEXT message * Echo 0 byte TEXT message
*/ */
@Test @Test
public void testCase1_1_1() throws Exception public void testCase1_1_1() throws Exception
{ {
assertEchoEmptyFrame(OpCode.TEXT); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text());
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text());
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -221,7 +64,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[125]; byte payload[] = new byte[125];
Arrays.fill(payload,(byte)'*'); Arrays.fill(payload,(byte)'*');
assertEchoFrame(OpCode.TEXT,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -233,7 +95,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[126]; byte payload[] = new byte[126];
Arrays.fill(payload,(byte)'*'); Arrays.fill(payload,(byte)'*');
assertEchoFrame(OpCode.TEXT,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -245,7 +126,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[127]; byte payload[] = new byte[127];
Arrays.fill(payload,(byte)'*'); Arrays.fill(payload,(byte)'*');
assertEchoFrame(OpCode.TEXT,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -257,7 +157,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[128]; byte payload[] = new byte[128];
Arrays.fill(payload,(byte)'*'); Arrays.fill(payload,(byte)'*');
assertEchoFrame(OpCode.TEXT,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -269,7 +188,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[65535]; byte payload[] = new byte[65535];
Arrays.fill(payload,(byte)'*'); Arrays.fill(payload,(byte)'*');
assertEchoFrame(OpCode.TEXT,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -281,7 +219,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[65536]; byte payload[] = new byte[65536];
Arrays.fill(payload,(byte)'*'); Arrays.fill(payload,(byte)'*');
assertEchoFrame(OpCode.TEXT,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -298,7 +255,27 @@ public class TestABCase1 extends AbstractABCase
Arrays.fill(payload,(byte)'*'); Arrays.fill(payload,(byte)'*');
int segmentSize = 997; int segmentSize = 997;
assertEchoSegmentedFrame(OpCode.TEXT,payload,segmentSize); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.SLOW);
fuzzer.setSlowSendSegmentSize(segmentSize);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -307,7 +284,26 @@ public class TestABCase1 extends AbstractABCase
@Test @Test
public void testCase1_2_1() throws Exception public void testCase1_2_1() throws Exception
{ {
assertEchoEmptyFrame(OpCode.BINARY); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.binary());
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.binary());
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -319,7 +315,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[125]; byte payload[] = new byte[125];
Arrays.fill(payload,(byte)0xFE); Arrays.fill(payload,(byte)0xFE);
assertEchoFrame(OpCode.BINARY,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.binary().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.binary().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -331,7 +346,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[126]; byte payload[] = new byte[126];
Arrays.fill(payload,(byte)0xFE); Arrays.fill(payload,(byte)0xFE);
assertEchoFrame(OpCode.BINARY,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.binary().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.binary().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -343,7 +377,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[127]; byte payload[] = new byte[127];
Arrays.fill(payload,(byte)0xFE); Arrays.fill(payload,(byte)0xFE);
assertEchoFrame(OpCode.BINARY,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.binary().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.binary().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -355,7 +408,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[128]; byte payload[] = new byte[128];
Arrays.fill(payload,(byte)0xFE); Arrays.fill(payload,(byte)0xFE);
assertEchoFrame(OpCode.BINARY,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.binary().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.binary().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -367,7 +439,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[65535]; byte payload[] = new byte[65535];
Arrays.fill(payload,(byte)0xFE); Arrays.fill(payload,(byte)0xFE);
assertEchoFrame(OpCode.BINARY,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.binary().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.binary().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -379,7 +470,26 @@ public class TestABCase1 extends AbstractABCase
byte payload[] = new byte[65536]; byte payload[] = new byte[65536];
Arrays.fill(payload,(byte)0xFE); Arrays.fill(payload,(byte)0xFE);
assertEchoFrame(OpCode.BINARY,payload); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.binary().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.binary().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -396,6 +506,26 @@ public class TestABCase1 extends AbstractABCase
Arrays.fill(payload,(byte)0xFE); Arrays.fill(payload,(byte)0xFE);
int segmentSize = 997; int segmentSize = 997;
assertEchoSegmentedFrame(OpCode.BINARY,payload,segmentSize); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.binary().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.binary().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(SendMode.SLOW);
fuzzer.setSlowSendSegmentSize(segmentSize);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
} }

View File

@ -1,224 +1,40 @@
package org.eclipse.jetty.websocket.server.ab; package org.eclipse.jetty.websocket.server.ab;
import static org.hamcrest.Matchers.*; import java.util.ArrayList;
import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.TimeUnit; import java.util.List;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.protocol.CloseInfo; import org.eclipse.jetty.websocket.protocol.CloseInfo;
import org.eclipse.jetty.websocket.protocol.Generator;
import org.eclipse.jetty.websocket.protocol.OpCode; import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame; import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
import org.eclipse.jetty.websocket.server.ByteBufferAssert;
import org.eclipse.jetty.websocket.server.blockhead.BlockheadClient;
import org.eclipse.jetty.websocket.server.helper.IncomingFramesCapture;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
public class TestABCase2 extends AbstractABCase public class TestABCase2 extends AbstractABCase
{ {
private void assertPingFrame(byte[] payload) throws Exception
{
boolean hasPayload = ((payload != null) && (payload.length > 0));
BlockheadClient client = new BlockheadClient(server.getServerUri());
try
{
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
int len = 0;
if (hasPayload)
{
len = payload.length;
}
ByteBuffer buf = ByteBuffer.allocate(len + Generator.OVERHEAD);
BufferUtil.clearToFill(buf);
// Prepare PING Frame
buf.put((byte)(0x00 | FIN | OpCode.PING.getCode()));
putPayloadLength(buf,len);
putMask(buf);
if (hasPayload)
{
buf.put(masked(payload));
}
// Prepare CLOSE Frame
buf.put((byte)(0x00 | FIN | OpCode.CLOSE.getCode()));
putPayloadLength(buf,2);
putMask(buf);
buf.put(masked(new byte[]
{ 0x03, (byte)0xE8 }));
// Write Data Frame
BufferUtil.flipToFlush(buf,0);
client.writeRaw(buf);
client.flush();
// Read frames
IncomingFramesCapture capture = client.readFrames(2,TimeUnit.MILLISECONDS,500);
// Validate echo'd frame
WebSocketFrame frame = capture.getFrames().get(0);
Assert.assertThat("frame should be PONG frame",frame.getOpCode(),is(OpCode.PONG));
if (hasPayload)
{
Assert.assertThat("PONG.payloadLength",frame.getPayloadLength(),is(payload.length));
ByteBufferAssert.assertEquals("PONG.payload",payload,frame.getPayload());
}
else
{
Assert.assertThat("PONG.payloadLength",frame.getPayloadLength(),is(0));
}
// Validate close
frame = capture.getFrames().get(1);
Assert.assertThat("CLOSE.frame.opcode",frame.getOpCode(),is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
Assert.assertThat("CLOSE.statusCode",close.getStatusCode(),is(StatusCode.NORMAL));
}
finally
{
client.disconnect();
}
}
private void assertProtocolError(byte[] payload) throws Exception
{
BlockheadClient client = new BlockheadClient(server.getServerUri());
try
{
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
ByteBuffer buf = ByteBuffer.allocate(payload.length + Generator.OVERHEAD);
BufferUtil.clearToFill(buf);
// Prepare PING Frame
buf.put((byte)(0x00 | FIN | OpCode.PING.getCode()));
putPayloadLength(buf,payload.length);
putMask(buf);
buf.put(masked(payload));
// Prepare CLOSE Frame
buf.put((byte)(0x00 | FIN | OpCode.CLOSE.getCode()));
putPayloadLength(buf,2);
putMask(buf);
buf.put(masked(new byte[]
{ 0x03, (byte)0xE8 }));
// Write Data Frame
BufferUtil.flipToFlush(buf,0);
client.writeRaw(buf);
client.flush();
// Read frames
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
// Validate close w/ Protocol Error
WebSocketFrame frame = capture.getFrames().pop();
Assert.assertThat("CLOSE.frame.opcode",frame.getOpCode(),is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
Assert.assertThat("CLOSE.statusCode",close.getStatusCode(),is(StatusCode.PROTOCOL));
}
finally
{
client.disconnect();
}
}
/**
* Send a ping frame as separate segments, in an inefficient way.
*
* @param payload
* the payload
* @param segmentSize
* the segment size for each inefficient segment (flush between)
*/
private void assertSegmentedPingFrame(byte[] payload, int segmentSize) throws Exception
{
Assert.assertThat("payload exists for segmented send",payload,notNullValue());
Assert.assertThat("payload exists for segmented send",payload.length,greaterThan(0));
BlockheadClient client = new BlockheadClient(server.getServerUri());
try
{
client.connect();
client.sendStandardRequest();
client.expectUpgradeResponse();
ByteBuffer buf = ByteBuffer.allocate(payload.length + Generator.OVERHEAD);
BufferUtil.clearToFill(buf);
// Prepare PING Frame
buf.put((byte)(0x00 | FIN | OpCode.PING.getCode()));
putPayloadLength(buf,payload.length);
putMask(buf);
buf.put(masked(payload));
// Prepare CLOSE Frame
buf.put((byte)(0x00 | FIN | OpCode.CLOSE.getCode()));
putPayloadLength(buf,2);
putMask(buf);
buf.put(masked(new byte[]
{ 0x03, (byte)0xE8 }));
// Write Data Frame
BufferUtil.flipToFlush(buf,0);
int origLimit = buf.limit();
int limit = buf.limit();
int len;
int pos = buf.position();
int overallLeft = buf.remaining();
while (overallLeft > 0)
{
buf.position(pos);
limit = Math.min(origLimit,pos + segmentSize);
buf.limit(limit);
len = buf.remaining();
overallLeft -= len;
pos += len;
client.writeRaw(buf);
client.flush();
}
// Read frames
IncomingFramesCapture capture = client.readFrames(2,TimeUnit.MILLISECONDS,500);
// Validate echo'd frame
WebSocketFrame frame = capture.getFrames().get(0);
Assert.assertThat("frame should be PONG frame",frame.getOpCode(),is(OpCode.PONG));
Assert.assertThat("PONG.payloadLength",frame.getPayloadLength(),is(payload.length));
ByteBufferAssert.assertEquals("PONG.payload",payload,frame.getPayload());
// Validate close
frame = capture.getFrames().get(1);
Assert.assertThat("CLOSE.frame.opcode",frame.getOpCode(),is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
Assert.assertThat("CLOSE.statusCode",close.getStatusCode(),is(StatusCode.NORMAL));
}
finally
{
client.disconnect();
}
}
/** /**
* Ping without payload * Ping without payload
*/ */
@Test @Test
public void testCase2_1() throws Exception public void testCase2_1() throws Exception
{ {
byte payload[] = new byte[0]; WebSocketFrame send = WebSocketFrame.ping();
assertPingFrame(payload);
WebSocketFrame expect = WebSocketFrame.pong();
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -229,8 +45,35 @@ public class TestABCase2 extends AbstractABCase
{ {
// send 10 pings each with unique payload // send 10 pings each with unique payload
// send close // send close
// expect 10 pongs with OUR payload // expect 10 pongs with our unique payload
// expect close // expect close
int pingCount = 10;
List<WebSocketFrame> send = new ArrayList<>();
List<WebSocketFrame> expect = new ArrayList<>();
for (int i = 0; i < pingCount; i++)
{
String payload = String.format("ping-%d[%X]",i,i);
send.add(WebSocketFrame.ping().setPayload(payload));
expect.add(WebSocketFrame.pong().setPayload(payload));
}
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -243,6 +86,34 @@ public class TestABCase2 extends AbstractABCase
// send close // send close
// expect 10 pongs with OUR payload // expect 10 pongs with OUR payload
// expect close // expect close
int pingCount = 10;
List<WebSocketFrame> send = new ArrayList<>();
List<WebSocketFrame> expect = new ArrayList<>();
for (int i = 0; i < pingCount; i++)
{
String payload = String.format("ping-%d[%X]",i,i);
send.add(WebSocketFrame.ping().setPayload(payload));
expect.add(WebSocketFrame.pong().setPayload(payload));
}
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
fuzzer.setSlowSendSegmentSize(5);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -252,7 +123,27 @@ public class TestABCase2 extends AbstractABCase
public void testCase2_2() throws Exception public void testCase2_2() throws Exception
{ {
byte payload[] = StringUtil.getUtf8Bytes("Hello world"); byte payload[] = StringUtil.getUtf8Bytes("Hello world");
assertPingFrame(payload);
List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.ping().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.pong().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -263,7 +154,27 @@ public class TestABCase2 extends AbstractABCase
{ {
byte payload[] = new byte[] byte payload[] = new byte[]
{ 0x00, (byte)0xFF, (byte)0xFE, (byte)0xFD, (byte)0xFC, (byte)0xFB, 0x00, (byte)0xFF }; { 0x00, (byte)0xFF, (byte)0xFE, (byte)0xFD, (byte)0xFC, (byte)0xFB, 0x00, (byte)0xFF };
assertPingFrame(payload);
List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.ping().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.pong().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -274,7 +185,27 @@ public class TestABCase2 extends AbstractABCase
{ {
byte payload[] = new byte[125]; byte payload[] = new byte[125];
Arrays.fill(payload,(byte)0xFE); Arrays.fill(payload,(byte)0xFE);
assertPingFrame(payload);
List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.ping().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.pong().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -283,9 +214,29 @@ public class TestABCase2 extends AbstractABCase
@Test @Test
public void testCase2_5() throws Exception public void testCase2_5() throws Exception
{ {
byte payload[] = new byte[126]; byte payload[] = new byte[126]; // intentionally too big
Arrays.fill(payload,(byte)0xFE); Arrays.fill(payload,(byte)0xFE);
assertProtocolError(payload);
List<WebSocketFrame> send = new ArrayList<>();
// trick websocket frame into making extra large payload for ping
send.add(WebSocketFrame.binary(payload).setOpCode(OpCode.PING));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -296,7 +247,28 @@ public class TestABCase2 extends AbstractABCase
{ {
byte payload[] = new byte[125]; byte payload[] = new byte[125];
Arrays.fill(payload,(byte)0xFE); Arrays.fill(payload,(byte)0xFE);
assertSegmentedPingFrame(payload,1);
List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.ping().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.pong().setPayload(payload));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
fuzzer.setSlowSendSegmentSize(1);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
} }
/** /**
@ -305,50 +277,25 @@ public class TestABCase2 extends AbstractABCase
@Test @Test
public void testCase2_7() throws Exception public void testCase2_7() throws Exception
{ {
BlockheadClient client = new BlockheadClient(server.getServerUri()); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.pong()); // unsolicited pong
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try try
{ {
client.connect(); fuzzer.connect();
client.sendStandardRequest(); fuzzer.setSendMode(Fuzzer.SendMode.BULK);
client.expectUpgradeResponse(); fuzzer.send(send);
fuzzer.expect(expect);
byte payload[] = new byte[0];
ByteBuffer buf = ByteBuffer.allocate(256);
BufferUtil.clearToFill(buf);
// Prepare Unsolicited PONG Frame
buf.put((byte)(0x00 | FIN | OpCode.PONG.getCode()));
putPayloadLength(buf,payload.length);
putMask(buf);
// buf.put(masked(payload));
// Prepare CLOSE Frame
buf.put((byte)(0x00 | FIN | OpCode.CLOSE.getCode()));
putPayloadLength(buf,2);
putMask(buf);
buf.put(masked(new byte[]
{ 0x03, (byte)0xE8 }));
// Write Data Frame
BufferUtil.flipToFlush(buf,0);
client.writeRaw(buf);
client.flush();
// Read frames
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
// Validate close
WebSocketFrame frame = capture.getFrames().pop();
Assert.assertThat("CLOSE.frame.opcode",frame.getOpCode(),is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
Assert.assertThat("CLOSE.statusCode",close.getStatusCode(),is(StatusCode.NORMAL));
} }
finally finally
{ {
client.disconnect(); fuzzer.close();
} }
} }
/** /**
@ -357,48 +304,24 @@ public class TestABCase2 extends AbstractABCase
@Test @Test
public void testCase2_8() throws Exception public void testCase2_8() throws Exception
{ {
BlockheadClient client = new BlockheadClient(server.getServerUri()); List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.pong().setPayload("unsolicited")); // unsolicited pong
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try try
{ {
client.connect(); fuzzer.connect();
client.sendStandardRequest(); fuzzer.setSendMode(Fuzzer.SendMode.BULK);
client.expectUpgradeResponse(); fuzzer.send(send);
fuzzer.expect(expect);
byte payload[] = StringUtil.getUtf8Bytes("unsolicited");
ByteBuffer buf = ByteBuffer.allocate(256);
BufferUtil.clearToFill(buf);
// Prepare Unsolicited PONG Frame
buf.put((byte)(0x00 | FIN | OpCode.PONG.getCode()));
putPayloadLength(buf,payload.length);
putMask(buf);
buf.put(masked(payload));
// Prepare CLOSE Frame
buf.put((byte)(0x00 | FIN | OpCode.CLOSE.getCode()));
putPayloadLength(buf,2);
putMask(buf);
buf.put(masked(new byte[]
{ 0x03, (byte)0xE8 }));
// Write Data Frame
BufferUtil.flipToFlush(buf,0);
client.writeRaw(buf);
client.flush();
// Read frames
IncomingFramesCapture capture = client.readFrames(1,TimeUnit.MILLISECONDS,500);
// Validate close
WebSocketFrame frame = capture.getFrames().pop();
Assert.assertThat("CLOSE.frame.opcode",frame.getOpCode(),is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
Assert.assertThat("CLOSE.statusCode",close.getStatusCode(),is(StatusCode.NORMAL));
} }
finally finally
{ {
client.disconnect(); fuzzer.close();
} }
} }
@ -408,69 +331,26 @@ public class TestABCase2 extends AbstractABCase
@Test @Test
public void testCase2_9() throws Exception public void testCase2_9() throws Exception
{ {
// send unsolicited pong with payload. List<WebSocketFrame> send = new ArrayList<>();
// send OUR ping with payload send.add(WebSocketFrame.pong().setPayload("unsolicited")); // unsolicited pong
// send close send.add(WebSocketFrame.ping().setPayload("our ping")); // our ping
// expect pong with OUR payload send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
// expect close
BlockheadClient client = new BlockheadClient(server.getServerUri()); List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.pong().setPayload("our ping")); // our pong
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try try
{ {
client.connect(); fuzzer.connect();
client.sendStandardRequest(); fuzzer.setSendMode(Fuzzer.SendMode.BULK);
client.expectUpgradeResponse(); fuzzer.send(send);
fuzzer.expect(expect);
byte pongPayload[] = StringUtil.getUtf8Bytes("unsolicited");
ByteBuffer buf = ByteBuffer.allocate(512);
BufferUtil.clearToFill(buf);
// Prepare Unsolicited PONG Frame
buf.put((byte)(0x00 | FIN | OpCode.PONG.getCode()));
putPayloadLength(buf,pongPayload.length);
putMask(buf);
buf.put(masked(pongPayload));
// Prepare our PING with payload
byte pingPayload[] = StringUtil.getUtf8Bytes("ping me");
buf.put((byte)(0x00 | FIN | OpCode.PING.getCode()));
putPayloadLength(buf,pingPayload.length);
putMask(buf);
buf.put(masked(pingPayload));
// Prepare CLOSE Frame
buf.put((byte)(0x00 | FIN | OpCode.CLOSE.getCode()));
putPayloadLength(buf,2);
putMask(buf);
buf.put(masked(new byte[]
{ 0x03, (byte)0xE8 }));
// Write Data Frame
BufferUtil.flipToFlush(buf,0);
client.writeRaw(buf);
client.flush();
// Read frames
IncomingFramesCapture capture = client.readFrames(2,TimeUnit.MILLISECONDS,500);
// Validate PONG
WebSocketFrame frame = capture.getFrames().pop();
Assert.assertThat("frame should be PONG frame",frame.getOpCode(),is(OpCode.PONG));
Assert.assertThat("PONG.payloadLength",frame.getPayloadLength(),is(pingPayload.length));
ByteBufferAssert.assertEquals("PONG.payload",pingPayload,frame.getPayload());
// Validate close
frame = capture.getFrames().pop();
Assert.assertThat("CLOSE.frame.opcode",frame.getOpCode(),is(OpCode.CLOSE));
CloseInfo close = new CloseInfo(frame);
Assert.assertThat("CLOSE.statusCode",close.getStatusCode(),is(StatusCode.NORMAL));
} }
finally finally
{ {
client.disconnect(); fuzzer.close();
} }
} }
} }

View File

@ -0,0 +1,216 @@
package org.eclipse.jetty.websocket.server.ab;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.protocol.CloseInfo;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
import org.junit.Test;
public class TestABCase3 extends AbstractABCase
{
/**
* Send small text frame, with RSV1 == true, with no extensions defined.
*/
@Test
public void testCase3_1() throws Exception
{
WebSocketFrame send = WebSocketFrame.text("small").setRsv1(true); // intentionally bad
WebSocketFrame expect = new CloseInfo(StatusCode.PROTOCOL).asFrame();
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
}
/**
* Send small text frame, send again with RSV2 == true, then ping, with no extensions defined.
*/
@Test
public void testCase3_2() throws Exception
{
List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text("small"));
send.add(WebSocketFrame.text("small").setRsv2(true)); // intentionally bad
send.add(WebSocketFrame.ping().setPayload("ping"));
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text("small")); // echo on good frame
expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
}
/**
* Send small text frame, send again with (RSV1 & RSV2), then ping, with no extensions defined.
*/
@Test
public void testCase3_3() throws Exception
{
List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text("small"));
send.add(WebSocketFrame.text("small").setRsv1(true).setRsv2(true)); // intentionally bad
send.add(WebSocketFrame.ping().setPayload("ping"));
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text("small")); // echo on good frame
expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.PER_FRAME);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
}
/**
* Send small text frame, send again with (RSV3), then ping, with no extensions defined.
*/
@Test
public void testCase3_4() throws Exception
{
List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.text("small"));
send.add(WebSocketFrame.text("small").setRsv3(true)); // intentionally bad
send.add(WebSocketFrame.ping().setPayload("ping"));
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(WebSocketFrame.text("small")); // echo on good frame
expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.SLOW);
fuzzer.setSlowSendSegmentSize(1);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
}
/**
* Send binary frame with (RSV3 & RSV1), with no extensions defined.
*/
@Test
public void testCase3_5() throws Exception
{
byte payload[] = new byte[8];
Arrays.fill(payload,(byte)0xFF);
List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.binary(payload).setRsv3(true).setRsv1(true)); // intentionally bad
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
}
/**
* Send ping frame with (RSV3 & RSV2), with no extensions defined.
*/
@Test
public void testCase3_6() throws Exception
{
byte payload[] = new byte[8];
Arrays.fill(payload,(byte)0xFF);
List<WebSocketFrame> send = new ArrayList<>();
send.add(WebSocketFrame.ping().setPayload(payload).setRsv3(true).setRsv2(true)); // intentionally bad
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
}
/**
* Send close frame with (RSV3 & RSV2 & RSV1), with no extensions defined.
*/
@Test
public void testCase3_7() throws Exception
{
byte payload[] = new byte[8];
Arrays.fill(payload,(byte)0xFF);
List<WebSocketFrame> send = new ArrayList<>();
WebSocketFrame frame = new CloseInfo(StatusCode.NORMAL).asFrame();
frame.setRsv1(true);
frame.setRsv2(true);
frame.setRsv3(true);
send.add(frame); // intentionally bad
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.PROTOCOL).asFrame());
Fuzzer fuzzer = new Fuzzer(this);
try
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
finally
{
fuzzer.close();
}
}
}

View File

@ -174,13 +174,16 @@ public class BlockheadClient implements IncomingFrames, OutgoingFrames
LOG.debug("disconnect"); LOG.debug("disconnect");
IO.close(in); IO.close(in);
IO.close(out); IO.close(out);
try if (socket != null)
{ {
socket.close(); try
} {
catch (IOException ignore) socket.close();
{ }
/* ignore */ catch (IOException ignore)
{
/* ignore */
}
} }
} }
@ -318,6 +321,11 @@ public class BlockheadClient implements IncomingFrames, OutgoingFrames
incomingFrameQueue.incoming(copy); incomingFrameQueue.incoming(copy);
} }
public boolean isConnected()
{
return (socket != null) && (socket.isConnected());
}
public void lookFor(String string) throws IOException public void lookFor(String string) throws IOException
{ {
String orig = string; String orig = string;
@ -547,4 +555,24 @@ public class BlockheadClient implements IncomingFrames, OutgoingFrames
LOG.debug("write((String)[{}]){}{})",str.length(),'\n',str); LOG.debug("write((String)[{}]){}{})",str.length(),'\n',str);
out.write(StringUtil.getBytes(str,StringUtil.__ISO_8859_1)); out.write(StringUtil.getBytes(str,StringUtil.__ISO_8859_1));
} }
public void writeRawSlowly(ByteBuffer buf, int segmentSize) throws IOException
{
int origLimit = buf.limit();
int limit = buf.limit();
int len;
int pos = buf.position();
int overallLeft = buf.remaining();
while (overallLeft > 0)
{
buf.position(pos);
limit = Math.min(origLimit,pos + segmentSize);
buf.limit(limit);
len = buf.remaining();
overallLeft -= len;
pos += len;
writeRaw(buf);
flush();
}
}
} }

View File

@ -1,15 +1,15 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.io.LEVEL=WARN org.eclipse.jetty.LEVEL=WARN
org.eclipse.jetty.server.LEVEL=WARN org.eclipse.jetty.server.LEVEL=WARN
# org.eclipse.jetty.websocket.LEVEL=WARN # org.eclipse.jetty.websocket.LEVEL=WARN
org.eclipse.jetty.websocket.server.helper.RFCSocket.LEVEL=OFF org.eclipse.jetty.websocket.server.helper.RFCSocket.LEVEL=OFF
# See the read/write traffic # See the read/write traffic
org.eclipse.jetty.websocket.io.Frames.LEVEL=DEBUG # org.eclipse.jetty.websocket.io.Frames.LEVEL=DEBUG
# org.eclipse.jetty.websocket.io.LEVEL=DEBUG # org.eclipse.jetty.websocket.io.LEVEL=DEBUG
# org.eclipse.jetty.websocket.io.WebSocketAsyncConnection.LEVEL=DEBUG # org.eclipse.jetty.websocket.io.WebSocketAsyncConnection.LEVEL=DEBUG
# org.eclipse.jetty.util.thread.QueuedThreadPool.LEVEL=DEBUG # org.eclipse.jetty.util.thread.QueuedThreadPool.LEVEL=DEBUG
# org.eclipse.jetty.io.SelectorManager.LEVEL=INFO # org.eclipse.jetty.io.SelectorManager.LEVEL=INFO
org.eclipse.jetty.websocket.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=DEBUG
# org.eclipse.jetty.websocket.driver.WebSocketEventDriver.LEVEL=DEBUG # org.eclipse.jetty.websocket.driver.WebSocketEventDriver.LEVEL=DEBUG
# org.eclipse.jetty.websocket.extensions.LEVEL=DEBUG # org.eclipse.jetty.websocket.extensions.LEVEL=DEBUG
# org.eclipse.jetty.websocket.protocol.Generator.LEVEL=INFO # org.eclipse.jetty.websocket.protocol.Generator.LEVEL=INFO