blob: a4848049540745f0b4c4888647731b90b71c7b9e [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2015 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.common;
import static org.hamcrest.Matchers.is;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.frames.ContinuationFrame;
import org.eclipse.jetty.websocket.common.frames.DataFrame;
import org.eclipse.jetty.websocket.common.frames.PingFrame;
import org.eclipse.jetty.websocket.common.frames.PongFrame;
import org.eclipse.jetty.websocket.common.frames.TextFrame;
import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture;
import org.eclipse.jetty.websocket.common.test.UnitGenerator;
import org.eclipse.jetty.websocket.common.test.UnitParser;
import org.eclipse.jetty.websocket.common.util.Hex;
import org.junit.Assert;
import org.junit.Test;
public class ParserTest
{
/**
* Similar to the server side 5.15 testcase. A normal 2 fragment text text message, followed by another continuation.
*/
@Test
public void testParseCase5_15()
{
List<WebSocketFrame> send = new ArrayList<>();
send.add(new TextFrame().setPayload("fragment1").setFin(false));
send.add(new ContinuationFrame().setPayload("fragment2").setFin(true));
send.add(new ContinuationFrame().setPayload("fragment3").setFin(false)); // bad frame
send.add(new TextFrame().setPayload("fragment4").setFin(true));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
ByteBuffer completeBuf = UnitGenerator.generate(send);
UnitParser parser = new UnitParser();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parseQuietly(completeBuf);
capture.assertErrorCount(1);
capture.assertHasFrame(OpCode.TEXT,1);
capture.assertHasFrame(OpCode.CONTINUATION,1);
}
/**
* Similar to the server side 5.18 testcase. Text message fragmented as 2 frames, both as opcode=TEXT
*/
@Test
public void testParseCase5_18()
{
List<WebSocketFrame> send = new ArrayList<>();
send.add(new TextFrame().setPayload("fragment1").setFin(false));
send.add(new TextFrame().setPayload("fragment2").setFin(true)); // bad frame, must be continuation
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
ByteBuffer completeBuf = UnitGenerator.generate(send);
UnitParser parser = new UnitParser();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parseQuietly(completeBuf);
capture.assertErrorCount(1);
capture.assertHasFrame(OpCode.TEXT,1); // fragment 1
}
/**
* Similar to the server side 5.19 testcase. text message, send in 5 frames/fragments, with 2 pings in the mix.
*/
@Test
public void testParseCase5_19()
{
List<WebSocketFrame> send = new ArrayList<>();
send.add(new TextFrame().setPayload("f1").setFin(false));
send.add(new ContinuationFrame().setPayload(",f2").setFin(false));
send.add(new PingFrame().setPayload("pong-1"));
send.add(new ContinuationFrame().setPayload(",f3").setFin(false));
send.add(new ContinuationFrame().setPayload(",f4").setFin(false));
send.add(new PingFrame().setPayload("pong-2"));
send.add(new ContinuationFrame().setPayload(",f5").setFin(true));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
ByteBuffer completeBuf = UnitGenerator.generate(send);
UnitParser parser = new UnitParser();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parseQuietly(completeBuf);
capture.assertErrorCount(0);
capture.assertHasFrame(OpCode.TEXT,1);
capture.assertHasFrame(OpCode.CONTINUATION,4);
capture.assertHasFrame(OpCode.CLOSE,1);
capture.assertHasFrame(OpCode.PING,2);
}
/**
* Similar to the server side 5.6 testcase. pong, then text, then close frames.
*/
@Test
public void testParseCase5_6()
{
List<WebSocketFrame> send = new ArrayList<>();
send.add(new PongFrame().setPayload("ping"));
send.add(new TextFrame().setPayload("hello, world"));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
ByteBuffer completeBuf = UnitGenerator.generate(send);
UnitParser parser = new UnitParser();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(completeBuf);
capture.assertErrorCount(0);
capture.assertHasFrame(OpCode.TEXT,1);
capture.assertHasFrame(OpCode.CLOSE,1);
capture.assertHasFrame(OpCode.PONG,1);
}
/**
* Similar to the server side 6.2.3 testcase. Lots of small 1 byte UTF8 Text frames, representing 1 overall text message.
*/
@Test
public void testParseCase6_2_3()
{
String utf8 = "Hello-\uC2B5@\uC39F\uC3A4\uC3BC\uC3A0\uC3A1-UTF-8!!";
byte msg[] = StringUtil.getUtf8Bytes(utf8);
List<WebSocketFrame> send = new ArrayList<>();
int textCount = 0;
int continuationCount = 0;
int len = msg.length;
boolean continuation = false;
byte mini[];
for (int i = 0; i < len; i++)
{
DataFrame frame = null;
if (continuation)
{
frame = new ContinuationFrame();
continuationCount++;
}
else
{
frame = new TextFrame();
textCount++;
}
mini = new byte[1];
mini[0] = msg[i];
frame.setPayload(ByteBuffer.wrap(mini));
boolean isLast = (i >= (len - 1));
frame.setFin(isLast);
send.add(frame);
continuation = true;
}
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
ByteBuffer completeBuf = UnitGenerator.generate(send);
UnitParser parser = new UnitParser();
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(completeBuf);
capture.assertErrorCount(0);
capture.assertHasFrame(OpCode.TEXT,textCount);
capture.assertHasFrame(OpCode.CONTINUATION,continuationCount);
capture.assertHasFrame(OpCode.CLOSE,1);
}
@Test
public void testParseNothing()
{
ByteBuffer buf = ByteBuffer.allocate(16);
// Put nothing in the buffer.
buf.flip();
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new UnitParser(policy);
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
parser.parse(buf);
capture.assertNoErrors();
Assert.assertThat("Frame Count",capture.getFrames().size(),is(0));
}
@Test
public void testWindowedParseLargeFrame()
{
// Create frames
byte payload[] = new byte[65536];
Arrays.fill(payload,(byte)'*');
List<WebSocketFrame> frames = new ArrayList<>();
TextFrame text = new TextFrame();
text.setPayload(ByteBuffer.wrap(payload));
text.setMask(Hex.asByteArray("11223344"));
frames.add(text);
frames.add(new CloseInfo(StatusCode.NORMAL).asFrame());
// Build up raw (network bytes) buffer
ByteBuffer networkBytes = UnitGenerator.generate(frames);
// Parse, in 4096 sized windows
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
Parser parser = new UnitParser(policy);
IncomingFramesCapture capture = new IncomingFramesCapture();
parser.setIncomingFramesHandler(capture);
while (networkBytes.remaining() > 0)
{
ByteBuffer window = networkBytes.slice();
int windowSize = Math.min(window.remaining(),4096);
window.limit(windowSize);
parser.parse(window);
networkBytes.position(networkBytes.position() + windowSize);
}
capture.assertNoErrors();
Assert.assertThat("Frame Count",capture.getFrames().size(),is(2));
WebSocketFrame frame = capture.getFrames().poll();
Assert.assertThat("Frame[0].opcode",frame.getOpCode(),is(OpCode.TEXT));
ByteBuffer actualPayload = frame.getPayload();
Assert.assertThat("Frame[0].payload.length",actualPayload.remaining(),is(payload.length));
// Should be all '*' characters (if masking is correct)
for (int i = actualPayload.position(); i < actualPayload.remaining(); i++)
{
Assert.assertThat("Frame[0].payload[i]",actualPayload.get(i),is((byte)'*'));
}
}
}