Full core testing of DeflateFrameExtension

+ Renaming FrameParserCapture -> IncomingFramesCapture
+ Adding OutgoingFramesCapture
+ Fixing DeflateFrameExtension of small payloads
This commit is contained in:
Joakim Erdfelt 2012-07-23 13:52:08 -07:00
parent 5a182c6a9d
commit 93b5d97379
17 changed files with 681 additions and 151 deletions

View File

@ -27,7 +27,6 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.BadPayloadException;
import org.eclipse.jetty.websocket.api.Extension;
import org.eclipse.jetty.websocket.api.MessageTooLargeException;
import org.eclipse.jetty.websocket.api.ProtocolException;
import org.eclipse.jetty.websocket.protocol.ExtensionConfig;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
@ -42,10 +41,8 @@ public class DeflateFrameExtension extends Extension
private Deflater deflater;
private Inflater inflater;
// TODO: bring this method into some sort of ProtocolEnforcement class to share with Parser
private void assertSanePayloadLength(WebSocketFrame frame, int len)
private void assertSanePayloadLength(int len)
{
LOG.debug("Payload Length: " + len);
// Since we use ByteBuffer so often, having lengths over Integer.MAX_VALUE is really impossible.
if (len > Integer.MAX_VALUE)
{
@ -53,24 +50,59 @@ public class DeflateFrameExtension extends Extension
throw new MessageTooLargeException("[int-sane!] cannot handle payload lengths larger than " + Integer.MAX_VALUE);
}
getPolicy().assertValidPayloadLength(len);
}
switch (frame.getOpCode())
public ByteBuffer deflate(ByteBuffer data)
{
int length = data.remaining();
// prepare the uncompressed input
deflater.reset();
deflater.setInput(BufferUtil.toArray(data));
deflater.finish();
// prepare the output buffer
ByteBuffer buf = getBufferPool().acquire(length,false);
BufferUtil.clearToFill(buf);
// write the uncompressed length
if (length > 0xFF_FF)
{
case CLOSE:
if (len == 1)
{
throw new ProtocolException("Invalid close frame payload length, [" + len + "]");
}
// fall thru
case PING:
case PONG:
if (len > WebSocketFrame.MAX_CONTROL_PAYLOAD)
{
throw new ProtocolException("Invalid control frame payload length, [" + len + "] cannot exceed [" + WebSocketFrame.MAX_CONTROL_PAYLOAD
+ "]");
}
break;
buf.put((byte)0x7F);
buf.put((byte)0x00);
buf.put((byte)0x00);
buf.put((byte)0x00);
buf.put((byte)0x00);
buf.put((byte)((length >> 24) & 0xFF));
buf.put((byte)((length >> 16) & 0xFF));
buf.put((byte)((length >> 8) & 0xFF));
buf.put((byte)(length & 0xFF));
}
else if (length >= 0x7E)
{
buf.put((byte)0x7E);
buf.put((byte)(length >> 8));
buf.put((byte)(length & 0xFF));
}
else
{
buf.put((byte)(length & 0x7F));
}
while (!deflater.finished())
{
byte out[] = new byte[length];
int len = deflater.deflate(out,0,length,Deflater.FULL_FLUSH);
if (LOG.isDebugEnabled())
{
LOG.debug("Deflater: finished={}, needsInput={}, len={} / input.len={}",deflater.finished(),deflater.needsInput(),len,length);
}
buf.put(out,0,len);
}
BufferUtil.flipToFlush(buf,0);
return buf;
}
@Override
@ -84,37 +116,12 @@ public class DeflateFrameExtension extends Extension
}
ByteBuffer data = frame.getPayload();
// first 1 to 8 bytes contains post-inflated payload size.
int uncompressedLength = readUncompresseLength(frame,data);
// Set the data that is compressed to the inflater
inflater.setInput(BufferUtil.toArray(frame.getPayload()));
// Establish place for inflated data
byte buf[] = new byte[uncompressedLength];
try
{
int left = buf.length;
while (inflater.getRemaining() > 0)
{
// TODO: worry about the ByteBuffer.array here??
int inflated = inflater.inflate(buf,0,left);
if (inflated == 0)
{
throw new DataFormatException("insufficient data");
}
left -= inflated;
}
frame.setPayload(buf);
ByteBuffer uncompressed = inflate(data);
frame.setPayload(uncompressed);
nextIncoming(frame);
}
catch (DataFormatException e)
{
LOG.warn(e);
throw new BadPayloadException(e);
}
finally
{
// release original buffer (no longer needed)
@ -122,6 +129,51 @@ public class DeflateFrameExtension extends Extension
}
}
public ByteBuffer inflate(ByteBuffer data)
{
if (LOG.isDebugEnabled())
{
LOG.debug("inflate: {}",BufferUtil.toDetailString(data));
}
// first 1 to 8 bytes contains post-inflated payload size.
int uncompressedLength = readUncompresseLength(data);
if (LOG.isDebugEnabled())
{
LOG.debug("uncompressedLength={}, data={}",uncompressedLength,BufferUtil.toDetailString(data));
}
// Set the data that is compressed to the inflater
byte compressed[] = BufferUtil.toArray(data);
inflater.reset();
inflater.setInput(compressed,0,compressed.length);
// Establish place for inflated data
byte buf[] = new byte[uncompressedLength];
try
{
int inflated = inflater.inflate(buf);
if (inflated == 0)
{
throw new DataFormatException("Insufficient compressed data");
}
ByteBuffer ret = ByteBuffer.wrap(buf);
if (LOG.isDebugEnabled())
{
LOG.debug("uncompressed={}",BufferUtil.toDetailString(ret));
}
return ret;
}
catch (DataFormatException e)
{
LOG.warn(e);
throw new BadPayloadException(e);
}
}
@Override
public <C> void output(C context, Callback<C> callback, WebSocketFrame frame)
{
@ -140,52 +192,30 @@ public class DeflateFrameExtension extends Extension
}
ByteBuffer data = frame.getPayload();
int length = frame.getPayloadLength();
// prepare the uncompressed input
deflater.reset();
deflater.setInput(BufferUtil.toArray(data));
deflater.finish();
// prepare the output buffer
byte out[] = new byte[length];
int out_offset = 0;
// write the uncompressed length
if (length > 0xFF_FF)
try
{
out[out_offset++] = 0x7F;
out[out_offset++] = (byte)0;
out[out_offset++] = (byte)0;
out[out_offset++] = (byte)0;
out[out_offset++] = (byte)0;
out[out_offset++] = (byte)((length >> 24) & 0xff);
out[out_offset++] = (byte)((length >> 16) & 0xff);
out[out_offset++] = (byte)((length >> 8) & 0xff);
out[out_offset++] = (byte)(length & 0xff);
// deflate data
ByteBuffer buf = deflate(data);
frame.setPayload(buf);
frame.setRsv1(deflater.finished());
nextOutput(context,callback,frame);
}
else if (length >= 0x7E)
finally
{
out[out_offset++] = 0x7E;
out[out_offset++] = (byte)(length >> 8);
out[out_offset++] = (byte)(length & 0xff);
// free original data buffer
getBufferPool().release(data);
}
else
{
out[out_offset++] = (byte)(length & 0x7f);
}
deflater.deflate(out,out_offset,length - out_offset);
frame.setPayload(out);
frame.setRsv1(deflater.finished());
nextOutput(context,callback,frame);
// free original data buffer
getBufferPool().release(data);
}
private int readUncompresseLength(WebSocketFrame frame, ByteBuffer data)
/**
* Read the uncompressed length indicator in the frame.
* <p>
* Will modify the position of the buffer.
*
* @param data
* @return
*/
public int readUncompresseLength(ByteBuffer data)
{
int length = data.get();
int bytes = 0;
@ -208,7 +238,7 @@ public class DeflateFrameExtension extends Extension
length |= (b & 0xFF) << (8 * bytes);
}
assertSanePayloadLength(frame,length);
assertSanePayloadLength(length);
return length;
}
@ -220,7 +250,8 @@ public class DeflateFrameExtension extends Extension
minLength = config.getParameter("minLength",minLength);
deflater = new Deflater();
deflater = new Deflater(Deflater.BEST_COMPRESSION);
deflater.setStrategy(Deflater.DEFAULT_STRATEGY);
inflater = new Inflater();
}
}

View File

@ -24,7 +24,7 @@ import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.masks.FixedMasker;
import org.eclipse.jetty.websocket.masks.RandomMasker;
import org.eclipse.jetty.websocket.protocol.FrameParseCapture;
import org.eclipse.jetty.websocket.protocol.IncomingFramesCapture;
import org.eclipse.jetty.websocket.protocol.Generator;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.Parser;
@ -41,7 +41,7 @@ public class GeneratorParserRoundtripTest
StandardByteBufferPool bufferPool = new StandardByteBufferPool();
Generator gen = new Generator(policy,bufferPool);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
String message = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";
@ -79,7 +79,7 @@ public class GeneratorParserRoundtripTest
StandardByteBufferPool bufferPool = new StandardByteBufferPool();
Generator gen = new Generator(policy,bufferPool);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
String message = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF";

View File

@ -22,7 +22,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.ByteBufferAssert;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.protocol.FrameParseCapture;
import org.eclipse.jetty.websocket.protocol.IncomingFramesCapture;
import org.eclipse.jetty.websocket.protocol.Generator;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.Parser;
@ -310,7 +310,7 @@ public class TestABCase1_1
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -344,7 +344,7 @@ public class TestABCase1_1
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -378,7 +378,7 @@ public class TestABCase1_1
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -412,7 +412,7 @@ public class TestABCase1_1
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -451,7 +451,7 @@ public class TestABCase1_1
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setMaxTextMessageSize(length);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -488,7 +488,7 @@ public class TestABCase1_1
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setMaxTextMessageSize(length);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -512,7 +512,7 @@ public class TestABCase1_1
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);

View File

@ -23,7 +23,7 @@ import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.ByteBufferAssert;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.protocol.FrameParseCapture;
import org.eclipse.jetty.websocket.protocol.IncomingFramesCapture;
import org.eclipse.jetty.websocket.protocol.Generator;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.Parser;
@ -326,7 +326,7 @@ public class TestABCase1_2
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -360,7 +360,7 @@ public class TestABCase1_2
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -394,7 +394,7 @@ public class TestABCase1_2
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -428,7 +428,7 @@ public class TestABCase1_2
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -463,7 +463,7 @@ public class TestABCase1_2
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setMaxTextMessageSize(length);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -500,7 +500,7 @@ public class TestABCase1_2
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setMaxTextMessageSize(length);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -524,7 +524,7 @@ public class TestABCase1_2
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);

View File

@ -25,7 +25,7 @@ import org.eclipse.jetty.websocket.api.ProtocolException;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.protocol.FrameParseCapture;
import org.eclipse.jetty.websocket.protocol.IncomingFramesCapture;
import org.eclipse.jetty.websocket.protocol.Generator;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.Parser;
@ -191,7 +191,7 @@ public class TestABCase2
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -221,7 +221,7 @@ public class TestABCase2
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -244,7 +244,7 @@ public class TestABCase2
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -275,7 +275,7 @@ public class TestABCase2
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -318,7 +318,7 @@ public class TestABCase2
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);

View File

@ -20,7 +20,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.protocol.FrameParseCapture;
import org.eclipse.jetty.websocket.protocol.IncomingFramesCapture;
import org.eclipse.jetty.websocket.protocol.Parser;
import org.junit.Assert;
import org.junit.Test;
@ -40,7 +40,7 @@ public class TestABCase4
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -62,7 +62,7 @@ public class TestABCase4
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -85,7 +85,7 @@ public class TestABCase4
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -107,7 +107,7 @@ public class TestABCase4
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);

View File

@ -27,7 +27,7 @@ import org.eclipse.jetty.websocket.api.ProtocolException;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.protocol.CloseInfo;
import org.eclipse.jetty.websocket.protocol.FrameParseCapture;
import org.eclipse.jetty.websocket.protocol.IncomingFramesCapture;
import org.eclipse.jetty.websocket.protocol.Generator;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.Parser;
@ -69,7 +69,7 @@ public class TestABCase7_3
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -103,7 +103,7 @@ public class TestABCase7_3
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -143,7 +143,7 @@ public class TestABCase7_3
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -201,7 +201,7 @@ public class TestABCase7_3
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -271,7 +271,7 @@ public class TestABCase7_3
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);
@ -341,7 +341,7 @@ public class TestABCase7_3
expected.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(expected);

View File

@ -0,0 +1,303 @@
// ========================================================================
// Copyright 2011-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
//========================================================================
package org.eclipse.jetty.websocket.extensions;
import static org.hamcrest.Matchers.*;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.eclipse.jetty.io.StandardByteBufferPool;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.websocket.ByteBufferAssert;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.extensions.deflate.DeflateFrameExtension;
import org.eclipse.jetty.websocket.protocol.ExtensionConfig;
import org.eclipse.jetty.websocket.protocol.IncomingFramesCapture;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.OutgoingFramesCapture;
import org.eclipse.jetty.websocket.protocol.OutgoingFramesCapture.Write;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
import org.junit.Assert;
import org.junit.Test;
public class DeflateFrameExtensionTest
{
@Test
public void testFlate()
{
DeflateFrameExtension ext = new DeflateFrameExtension();
ext.setBufferPool(new StandardByteBufferPool());
ext.setPolicy(WebSocketPolicy.newServerPolicy());
ExtensionConfig config = ExtensionConfig.parse("x-deflate-frame;minLength=8");
ext.setConfig(config);
// Quote
StringBuilder quote = new StringBuilder();
quote.append("No amount of experimentation can ever prove me right;\n");
quote.append("a single experiment can prove me wrong.\n");
quote.append("-- Albert Einstein");
String expected = quote.toString();
ByteBuffer orig = BufferUtil.toBuffer(expected,StringUtil.__UTF8_CHARSET);
// compress
ByteBuffer compressed = ext.deflate(orig);
// decompress
ByteBuffer decompressed = ext.inflate(compressed);
// validate
String actual = BufferUtil.toUTF8String(decompressed);
Assert.assertEquals(expected,actual);
}
@Test
public void testFlateManySmall()
{
DeflateFrameExtension ext = new DeflateFrameExtension();
ext.setBufferPool(new StandardByteBufferPool());
ext.setPolicy(WebSocketPolicy.newServerPolicy());
ExtensionConfig config = ExtensionConfig.parse("x-deflate-frame;minLength=8");
ext.setConfig(config);
// Quote
List<String> quote = new ArrayList<>();
quote.add("No amount of experimentation can ever prove me right;");
quote.add("a single experiment can prove me wrong.");
quote.add("-- Albert Einstein");
for (String expected : quote)
{
ByteBuffer orig = BufferUtil.toBuffer(expected,StringUtil.__UTF8_CHARSET);
// compress
ByteBuffer compressed = ext.deflate(orig);
// decompress
ByteBuffer decompressed = ext.inflate(compressed);
// validate
String actual = BufferUtil.toUTF8String(decompressed);
Assert.assertEquals(expected,actual);
}
}
/**
* Verify that incoming frames are unmodified
*/
@Test
public void testIncomingFrames()
{
IncomingFramesCapture capture = new IncomingFramesCapture();
DeflateFrameExtension ext = new DeflateFrameExtension();
ext.setBufferPool(new StandardByteBufferPool());
ext.setPolicy(WebSocketPolicy.newServerPolicy());
ExtensionConfig config = ExtensionConfig.parse("x-deflate-frame;minLength=16");
ext.setConfig(config);
ext.setNextIncomingFrames(capture);
// Quote
List<String> quote = new ArrayList<>();
quote.add("No amount of experimentation can ever prove me right;");
quote.add("a single experiment can prove me wrong.");
quote.add("-- Albert Einstein");
// Manually compress frame and pass into extension
for (String q : quote)
{
ByteBuffer data = BufferUtil.toBuffer(q,StringUtil.__UTF8_CHARSET);
WebSocketFrame frame = new WebSocketFrame(OpCode.TEXT);
frame.setPayload(ext.deflate(data));
frame.setRsv1(true); // required by extension
ext.incoming(frame);
}
int len = quote.size();
capture.assertFrameCount(len);
capture.assertHasFrame(OpCode.TEXT,len);
String prefix;
for (int i = 0; i < len; i++)
{
prefix = "Frame[" + i + "]";
WebSocketFrame actual = capture.getFrames().get(i);
Assert.assertThat(prefix + ".opcode",actual.getOpCode(),is(OpCode.TEXT));
Assert.assertThat(prefix + ".fin",actual.isFin(),is(true));
Assert.assertThat(prefix + ".rsv1",actual.isRsv1(),is(true));
Assert.assertThat(prefix + ".rsv2",actual.isRsv2(),is(false));
Assert.assertThat(prefix + ".rsv3",actual.isRsv3(),is(false));
ByteBuffer expected = BufferUtil.toBuffer(quote.get(i),StringUtil.__UTF8_CHARSET);
Assert.assertThat(prefix + ".payloadLength",actual.getPayloadLength(),is(expected.remaining()));
ByteBufferAssert.assertEquals(prefix + ".payload",expected,actual.getPayload().slice());
}
}
/**
* Incoming PING (Control Frame) should pass through extension unmodified
*/
@Test
public void testIncomingPing() {
IncomingFramesCapture capture = new IncomingFramesCapture();
DeflateFrameExtension ext = new DeflateFrameExtension();
ext.setBufferPool(new StandardByteBufferPool());
ext.setPolicy(WebSocketPolicy.newServerPolicy());
ExtensionConfig config = ExtensionConfig.parse("x-deflate-frame;minLength=16");
ext.setConfig(config);
ext.setNextIncomingFrames(capture);
String payload = "Are you there?";
WebSocketFrame ping = WebSocketFrame.ping().setPayload(payload);
ext.incoming(ping);
capture.assertFrameCount(1);
capture.assertHasFrame(OpCode.PING,1);
WebSocketFrame actual = capture.getFrames().getFirst();
Assert.assertThat("Frame.opcode",actual.getOpCode(),is(OpCode.PING));
Assert.assertThat("Frame.fin",actual.isFin(),is(true));
Assert.assertThat("Frame.rsv1",actual.isRsv1(),is(false));
Assert.assertThat("Frame.rsv2",actual.isRsv2(),is(false));
Assert.assertThat("Frame.rsv3",actual.isRsv3(),is(false));
ByteBuffer expected = BufferUtil.toBuffer(payload,StringUtil.__UTF8_CHARSET);
Assert.assertThat("Frame.payloadLength",actual.getPayloadLength(),is(expected.remaining()));
ByteBufferAssert.assertEquals("Frame.payload",expected,actual.getPayload().slice());
}
/**
* Verify that outgoing text frames are compressed.
*/
@Test
public void testOutgoingFrames()
{
OutgoingFramesCapture capture = new OutgoingFramesCapture();
DeflateFrameExtension ext = new DeflateFrameExtension();
ext.setBufferPool(new StandardByteBufferPool());
ext.setPolicy(WebSocketPolicy.newServerPolicy());
ExtensionConfig config = ExtensionConfig.parse("x-deflate-frame;minLength=16");
ext.setConfig(config);
ext.setNextOutgoingFrames(capture);
// Quote
List<String> quote = new ArrayList<>();
quote.add("No amount of experimentation can ever prove me right;");
quote.add("a single experiment can prove me wrong.");
quote.add("-- Albert Einstein");
// Write quote as separate frames
List<Callback<?>> callbacks = new ArrayList<>();
for (String section : quote)
{
WebSocketFrame frame = WebSocketFrame.text(section);
FutureCallback<String> callback = new FutureCallback<>();
ext.output("Q" + (callbacks.size()),callback,frame);
callbacks.add(callback);
}
int len = quote.size();
capture.assertFrameCount(len);
capture.assertHasFrame(OpCode.TEXT,len);
String prefix;
LinkedList<Write<?>> writes = capture.getWrites();
for (int i = 0; i < len; i++)
{
prefix = "Write[" + i + "]";
Write<?> write = writes.get(i);
// Validate callbacks
Assert.assertThat(prefix + ".context",(String)write.context,is("Q" + i));
Assert.assertSame(prefix + ".callback",callbacks.get(i),write.callback);
// Validate Frame
WebSocketFrame actual = write.frame;
prefix = ".frame";
Assert.assertThat(prefix + ".opcode",actual.getOpCode(),is(OpCode.TEXT));
Assert.assertThat(prefix + ".fin",actual.isFin(),is(true));
Assert.assertThat(prefix + ".rsv1",actual.isRsv1(),is(true));
Assert.assertThat(prefix + ".rsv2",actual.isRsv2(),is(false));
Assert.assertThat(prefix + ".rsv3",actual.isRsv3(),is(false));
// Validate Payload
ByteBuffer expected = BufferUtil.toBuffer(quote.get(i),StringUtil.__UTF8_CHARSET);
// Decompress payload
ByteBuffer compressed = actual.getPayload().slice();
ByteBuffer uncompressed = ext.inflate(compressed);
System.err.printf("Expected : %s%n",BufferUtil.toDetailString(expected));
System.err.printf("Compressed : %s%n",BufferUtil.toDetailString(compressed));
System.err.printf("Uncompressed: %s%n",BufferUtil.toDetailString(uncompressed));
Assert.assertThat(prefix + ".payloadLength",uncompressed.remaining(),is(expected.remaining()));
ByteBufferAssert.assertEquals(prefix + ".payload",expected,uncompressed);
}
}
/**
* Outgoing PING (Control Frame) should pass through extension unmodified
*/
@Test
public void testOutgoingPing()
{
OutgoingFramesCapture capture = new OutgoingFramesCapture();
DeflateFrameExtension ext = new DeflateFrameExtension();
ext.setBufferPool(new StandardByteBufferPool());
ext.setPolicy(WebSocketPolicy.newServerPolicy());
ExtensionConfig config = ExtensionConfig.parse("x-deflate-frame;minLength=16");
ext.setConfig(config);
ext.setNextOutgoingFrames(capture);
String payload = "Are you there?";
WebSocketFrame ping = WebSocketFrame.ping().setPayload(payload);
FutureCallback<String> callback = new FutureCallback<>();
ext.output("TenFour",callback,ping);
capture.assertFrameCount(1);
capture.assertHasFrame(OpCode.PING,1);
Write<?> write = capture.getWrites().getFirst();
Assert.assertThat("Write.context",(String)write.context,is("TenFour"));
Assert.assertSame("Write.callback",callback,write.callback);
WebSocketFrame actual = write.frame;
Assert.assertThat("Frame.opcode",actual.getOpCode(),is(OpCode.PING));
Assert.assertThat("Frame.fin",actual.isFin(),is(true));
Assert.assertThat("Frame.rsv1",actual.isRsv1(),is(false));
Assert.assertThat("Frame.rsv2",actual.isRsv2(),is(false));
Assert.assertThat("Frame.rsv3",actual.isRsv3(),is(false));
ByteBuffer expected = BufferUtil.toBuffer(payload,StringUtil.__UTF8_CHARSET);
Assert.assertThat("Frame.payloadLength",actual.getPayloadLength(),is(expected.remaining()));
ByteBufferAssert.assertEquals("Frame.payload",expected,actual.getPayload().slice());
}
}

View File

@ -0,0 +1,102 @@
// ========================================================================
// Copyright 2011-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
//========================================================================
package org.eclipse.jetty.websocket.extensions;
import static org.hamcrest.Matchers.*;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.websocket.ByteBufferAssert;
import org.eclipse.jetty.websocket.api.Extension;
import org.eclipse.jetty.websocket.extensions.identity.IdentityExtension;
import org.eclipse.jetty.websocket.protocol.IncomingFramesCapture;
import org.eclipse.jetty.websocket.protocol.OpCode;
import org.eclipse.jetty.websocket.protocol.OutgoingFramesCapture;
import org.eclipse.jetty.websocket.protocol.OutgoingFramesCapture.Write;
import org.eclipse.jetty.websocket.protocol.WebSocketFrame;
import org.junit.Assert;
import org.junit.Test;
public class IdentityExtensionTest
{
/**
* Verify that incoming frames are unmodified
*/
@Test
public void testIncomingFrames()
{
IncomingFramesCapture capture = new IncomingFramesCapture();
Extension ext = new IdentityExtension();
ext.setNextIncomingFrames(capture);
WebSocketFrame frame = WebSocketFrame.text("hello");
ext.incoming(frame);
capture.assertFrameCount(1);
capture.assertHasFrame(OpCode.TEXT,1);
WebSocketFrame actual = capture.getFrames().getFirst();
Assert.assertThat("Frame.opcode",actual.getOpCode(),is(OpCode.TEXT));
Assert.assertThat("Frame.fin",actual.isFin(),is(true));
Assert.assertThat("Frame.rsv1",actual.isRsv1(),is(false));
Assert.assertThat("Frame.rsv2",actual.isRsv2(),is(false));
Assert.assertThat("Frame.rsv3",actual.isRsv3(),is(false));
ByteBuffer expected = BufferUtil.toBuffer("hello",StringUtil.__UTF8_CHARSET);
Assert.assertThat("Frame.payloadLength",actual.getPayloadLength(),is(expected.remaining()));
ByteBufferAssert.assertEquals("Frame.payload",expected,actual.getPayload().slice());
}
/**
* Verify that outgoing frames are unmodified
*/
@Test
public void testOutgoingFrames()
{
OutgoingFramesCapture capture = new OutgoingFramesCapture();
Extension ext = new IdentityExtension();
ext.setNextOutgoingFrames(capture);
WebSocketFrame frame = WebSocketFrame.text("hello");
FutureCallback<String> callback = new FutureCallback<>();
ext.output("WORLD",callback,frame);
capture.assertFrameCount(1);
capture.assertHasFrame(OpCode.TEXT,1);
Write<?> write = capture.getWrites().getFirst();
Assert.assertThat("Write.context",(String)write.context,is("WORLD"));
Assert.assertSame("Write.callback",callback,write.callback);
WebSocketFrame actual = write.frame;
Assert.assertThat("Frame.opcode",actual.getOpCode(),is(OpCode.TEXT));
Assert.assertThat("Frame.fin",actual.isFin(),is(true));
Assert.assertThat("Frame.rsv1",actual.isRsv1(),is(false));
Assert.assertThat("Frame.rsv2",actual.isRsv2(),is(false));
Assert.assertThat("Frame.rsv3",actual.isRsv3(),is(false));
ByteBuffer expected = BufferUtil.toBuffer("hello",StringUtil.__UTF8_CHARSET);
Assert.assertThat("Frame.payloadLength",actual.getPayloadLength(),is(expected.remaining()));
ByteBufferAssert.assertEquals("Frame.payload",expected,actual.getPayload().slice());
}
}

View File

@ -51,7 +51,7 @@ public class ClosePayloadParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);

View File

@ -93,7 +93,7 @@ public class GeneratorTest
// Parse complete buffer.
WebSocketPolicy policy = WebSocketPolicy.newServerPolicy();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
BufferUtil.flipToFlush(completeBuf,0);

View File

@ -17,8 +17,7 @@ package org.eclipse.jetty.websocket.protocol;
import static org.hamcrest.Matchers.*;
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedList;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -26,11 +25,21 @@ import org.eclipse.jetty.websocket.api.WebSocketException;
import org.eclipse.jetty.websocket.io.IncomingFrames;
import org.junit.Assert;
public class FrameParseCapture implements IncomingFrames
public class IncomingFramesCapture implements IncomingFrames
{
private static final Logger LOG = Log.getLogger(FrameParseCapture.class);
private List<WebSocketFrame> frames = new ArrayList<>();
private List<WebSocketException> errors = new ArrayList<>();
private static final Logger LOG = Log.getLogger(IncomingFramesCapture.class);
private LinkedList<WebSocketFrame> frames = new LinkedList<>();
private LinkedList<WebSocketException> errors = new LinkedList<>();
public void assertErrorCount(int expectedCount)
{
Assert.assertThat("Captured error count",errors.size(),is(expectedCount));
}
public void assertFrameCount(int expectedCount)
{
Assert.assertThat("Captured frame count",frames.size(),is(expectedCount));
}
public void assertHasErrors(Class<? extends WebSocketException> errorType, int expectedCount)
{
@ -69,7 +78,7 @@ public class FrameParseCapture implements IncomingFrames
return count;
}
public List<WebSocketException> getErrors()
public LinkedList<WebSocketException> getErrors()
{
return errors;
}
@ -86,7 +95,7 @@ public class FrameParseCapture implements IncomingFrames
return count;
}
public List<WebSocketFrame> getFrames()
public LinkedList<WebSocketFrame> getFrames()
{
return frames;
}
@ -94,7 +103,7 @@ public class FrameParseCapture implements IncomingFrames
@Override
public void incoming(WebSocketException e)
{
LOG.warn(e);
LOG.debug(e);
errors.add(e);
}

View File

@ -0,0 +1,85 @@
// ========================================================================
// Copyright 2011-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
//========================================================================
package org.eclipse.jetty.websocket.protocol;
import static org.hamcrest.Matchers.*;
import java.util.LinkedList;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.websocket.io.OutgoingFrames;
import org.junit.Assert;
public class OutgoingFramesCapture implements OutgoingFrames
{
public static class Write<C>
{
public C context;
public Callback<C> callback;
public WebSocketFrame frame;
}
private LinkedList<Write<?>> writes = new LinkedList<>();
public void assertFrameCount(int expectedCount)
{
Assert.assertThat("Captured frame count",writes.size(),is(expectedCount));
}
public void assertHasFrame(OpCode op)
{
Assert.assertThat(op.name(),getFrameCount(op),greaterThanOrEqualTo(1));
}
public void assertHasFrame(OpCode op, int expectedCount)
{
Assert.assertThat(op.name(),getFrameCount(op),is(expectedCount));
}
public void assertHasNoFrames()
{
Assert.assertThat("Has no frames",writes.size(),is(0));
}
public int getFrameCount(OpCode op)
{
int count = 0;
for (Write<?> write : writes)
{
WebSocketFrame frame = write.frame;
if (frame.getOpCode() == op)
{
count++;
}
}
return count;
}
public LinkedList<Write<?>> getWrites()
{
return writes;
}
@Override
public <C> void output(C context, Callback<C> callback, WebSocketFrame frame)
{
Write<C> write = new Write<C>();
write.context = context;
write.callback = callback;
write.frame = frame;
writes.add(write);
}
}

View File

@ -36,7 +36,7 @@ public class ParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);

View File

@ -41,7 +41,7 @@ public class PingPayloadParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);

View File

@ -37,7 +37,7 @@ public class RFC6455ExamplesParserTest
{
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
ByteBuffer buf = ByteBuffer.allocate(16);
@ -81,7 +81,7 @@ public class RFC6455ExamplesParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
@ -104,7 +104,7 @@ public class RFC6455ExamplesParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
@ -134,7 +134,7 @@ public class RFC6455ExamplesParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
@ -174,7 +174,7 @@ public class RFC6455ExamplesParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
policy.setBufferSize(80000);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
@ -205,7 +205,7 @@ public class RFC6455ExamplesParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
@ -228,7 +228,7 @@ public class RFC6455ExamplesParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);

View File

@ -54,7 +54,7 @@ public class TextPayloadParserTest
buf.flip();
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
@ -92,7 +92,7 @@ public class TextPayloadParserTest
policy.setBufferSize(100000);
policy.setMaxPayloadSize(100000);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
@ -127,7 +127,7 @@ public class TextPayloadParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
@ -164,7 +164,7 @@ public class TextPayloadParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
@ -191,7 +191,7 @@ public class TextPayloadParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
@ -217,7 +217,7 @@ public class TextPayloadParserTest
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new Parser(policy);
FrameParseCapture capture = new FrameParseCapture();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);