blob: 582798f16e4a684349d856578e9663647f3ca0c2 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2016 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.server.ab;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
import org.eclipse.jetty.toolchain.test.annotation.Slow;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.common.CloseInfo;
import org.eclipse.jetty.websocket.common.Parser;
import org.eclipse.jetty.websocket.common.WebSocketFrame;
import org.eclipse.jetty.websocket.common.frames.ContinuationFrame;
import org.eclipse.jetty.websocket.common.frames.DataFrame;
import org.eclipse.jetty.websocket.common.frames.TextFrame;
import org.eclipse.jetty.websocket.common.test.Fuzzer;
import org.eclipse.jetty.websocket.common.util.Hex;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* UTF-8 Tests
*/
@RunWith(AdvancedRunner.class)
public class TestABCase6 extends AbstractABCase
{
/**
* Split a message byte array into a series of fragments (frames + continuations) of 1 byte message contents each.
* @param frames the frames
* @param msg the message
*/
protected void fragmentText(List<WebSocketFrame> frames, byte msg[])
{
int len = msg.length;
boolean continuation = false;
byte mini[];
for (int i = 0; i < len; i++)
{
DataFrame frame = null;
if (continuation)
{
frame = new ContinuationFrame();
}
else
{
frame = new TextFrame();
}
mini = new byte[1];
mini[0] = msg[i];
frame.setPayload(ByteBuffer.wrap(mini));
boolean isLast = (i >= (len - 1));
frame.setFin(isLast);
frames.add(frame);
continuation = true;
}
}
/**
* text message, 1 frame, 0 length
* @throws Exception on test failure
*/
@Test
public void testCase6_1_1() throws Exception
{
List<WebSocketFrame> send = new ArrayList<>();
send.add(new TextFrame());
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new TextFrame());
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this))
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
}
/**
* text message, 0 length, 3 fragments
* @throws Exception on test failure
*/
@Test
public void testCase6_1_2() throws Exception
{
List<WebSocketFrame> send = new ArrayList<>();
send.add(new TextFrame().setFin(false));
send.add(new ContinuationFrame().setFin(false));
send.add(new ContinuationFrame().setFin(true));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new TextFrame());
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this))
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
}
/**
* text message, small length, 3 fragments (only middle frame has payload)
* @throws Exception on test failure
*/
@Test
public void testCase6_1_3() throws Exception
{
List<WebSocketFrame> send = new ArrayList<>();
send.add(new TextFrame().setFin(false));
send.add(new ContinuationFrame().setPayload("middle").setFin(false));
send.add(new ContinuationFrame().setFin(true));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new TextFrame().setPayload("middle"));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this))
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
}
/**
* valid utf8 text message, 2 fragments (on UTF8 code point boundary)
* @throws Exception on test failure
*/
@Test
public void testCase6_2_2() throws Exception
{
String utf1 = "Hello-\uC2B5@\uC39F\uC3A4";
String utf2 = "\uC3BC\uC3A0\uC3A1-UTF-8!!";
ByteBuffer b1 = ByteBuffer.wrap(StringUtil.getUtf8Bytes(utf1));
ByteBuffer b2 = ByteBuffer.wrap(StringUtil.getUtf8Bytes(utf2));
List<WebSocketFrame> send = new ArrayList<>();
send.add(new TextFrame().setPayload(b1).setFin(false));
send.add(new ContinuationFrame().setPayload(b2).setFin(true));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
ByteBuffer e1 = ByteBuffer.allocate(100);
e1.put(StringUtil.getUtf8Bytes(utf1));
e1.put(StringUtil.getUtf8Bytes(utf2));
e1.flip();
expect.add(new TextFrame().setPayload(e1));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this))
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
}
/**
* valid utf8 text message, many fragments (1 byte each)
* @throws Exception on test failure
*/
@Test
public void testCase6_2_3() throws Exception
{
String utf8 = "Hello-\uC2B5@\uC39F\uC3A4\uC3BC\uC3A0\uC3A1-UTF-8!!";
byte msg[] = StringUtil.getUtf8Bytes(utf8);
List<WebSocketFrame> send = new ArrayList<>();
fragmentText(send,msg);
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new TextFrame().setPayload(ByteBuffer.wrap(msg)));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this))
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
}
/**
* valid utf8 text message, many fragments (1 byte each)
* @throws Exception on test failure
*/
@Test
public void testCase6_2_4() throws Exception
{
byte msg[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5");
List<WebSocketFrame> send = new ArrayList<>();
fragmentText(send,msg);
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new TextFrame().setPayload(ByteBuffer.wrap(msg)));
expect.add(new CloseInfo(StatusCode.NORMAL).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this))
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
}
/**
* invalid utf8 text message, many fragments (1 byte each)
* @throws Exception on test failure
*/
@Test
public void testCase6_3_2() throws Exception
{
byte invalid[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5EDA080656469746564");
List<WebSocketFrame> send = new ArrayList<>();
fragmentText(send,invalid);
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this))
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(send);
fuzzer.expect(expect);
}
}
/**
* invalid text message, 3 fragments.
* <p>
* fragment #1 and fragment #3 are both valid in themselves.
* <p>
* fragment #2 contains the invalid utf8 code point.
* @throws Exception on test failure
*/
@Test
@Slow
public void testCase6_4_1() throws Exception
{
byte part1[] = StringUtil.getUtf8Bytes("\u03BA\u1F79\u03C3\u03BC\u03B5");
byte part2[] = Hex.asByteArray("F4908080"); // invalid
byte part3[] = StringUtil.getUtf8Bytes("edited");
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this))
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(new TextFrame().setPayload(ByteBuffer.wrap(part1)).setFin(false));
TimeUnit.SECONDS.sleep(1);
fuzzer.send(new ContinuationFrame().setPayload(ByteBuffer.wrap(part2)).setFin(false));
TimeUnit.SECONDS.sleep(1);
fuzzer.send(new ContinuationFrame().setPayload(ByteBuffer.wrap(part3)).setFin(true));
fuzzer.expect(expect);
}
}
/**
* invalid text message, 3 fragments.
* <p>
* fragment #1 is valid and ends in the middle of an incomplete code point.
* <p>
* fragment #2 finishes the UTF8 code point but it is invalid
* <p>
* fragment #3 contains the remainder of the message.
* @throws Exception on test failure
*/
@Test
@Slow
public void testCase6_4_2() throws Exception
{
byte part1[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5F4"); // split code point
byte part2[] = Hex.asByteArray("90"); // continue code point & invalid
byte part3[] = Hex.asByteArray("8080656469746564"); // continue code point & finish
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this))
{
fuzzer.connect();
fuzzer.setSendMode(Fuzzer.SendMode.BULK);
fuzzer.send(new TextFrame().setPayload(ByteBuffer.wrap(part1)).setFin(false));
TimeUnit.SECONDS.sleep(1);
fuzzer.send(new ContinuationFrame().setPayload(ByteBuffer.wrap(part2)).setFin(false));
TimeUnit.SECONDS.sleep(1);
fuzzer.send(new ContinuationFrame().setPayload(ByteBuffer.wrap(part3)).setFin(true));
fuzzer.expect(expect);
}
}
/**
* invalid text message, 1 frame/fragment (slowly, and split within code points)
* @throws Exception on test failure
*/
@Test
@Slow
public void testCase6_4_3() throws Exception
{
// Disable Long Stacks from Parser (we know this test will throw an exception)
try (StacklessLogging scope = new StacklessLogging(Parser.class))
{
ByteBuffer payload = ByteBuffer.allocate(64);
BufferUtil.clearToFill(payload);
payload.put(TypeUtil.fromHexString("cebae1bdb9cf83cebcceb5")); // good
payload.put(TypeUtil.fromHexString("f4908080")); // INVALID
payload.put(TypeUtil.fromHexString("656469746564")); // good
BufferUtil.flipToFlush(payload,0);
List<WebSocketFrame> send = new ArrayList<>();
send.add(new TextFrame().setPayload(payload));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this))
{
fuzzer.connect();
ByteBuffer net = fuzzer.asNetworkBuffer(send);
int splits[] = { 17, 21, net.limit() };
ByteBuffer part1 = net.slice(); // Header + good UTF
part1.limit(splits[0]);
ByteBuffer part2 = net.slice(); // invalid UTF
part2.position(splits[0]);
part2.limit(splits[1]);
ByteBuffer part3 = net.slice(); // good UTF
part3.position(splits[1]);
part3.limit(splits[2]);
fuzzer.send(part1); // the header + good utf
TimeUnit.MILLISECONDS.sleep(500);
fuzzer.send(part2); // the bad UTF
TimeUnit.MILLISECONDS.sleep(500);
fuzzer.send(part3); // the rest (shouldn't work)
fuzzer.expect(expect);
}
}
}
/**
* invalid text message, 1 frame/fragment (slowly, and split within code points)
* @throws Exception on test failure
*/
@Test
@Slow
public void testCase6_4_4() throws Exception
{
byte invalid[] = Hex.asByteArray("CEBAE1BDB9CF83CEBCCEB5F49080808080656469746564");
List<WebSocketFrame> send = new ArrayList<>();
send.add(new TextFrame().setPayload(ByteBuffer.wrap(invalid)));
send.add(new CloseInfo(StatusCode.NORMAL).asFrame());
List<WebSocketFrame> expect = new ArrayList<>();
expect.add(new CloseInfo(StatusCode.BAD_PAYLOAD).asFrame());
try (Fuzzer fuzzer = new Fuzzer(this); StacklessLogging scope = new StacklessLogging(Parser.class))
{
fuzzer.connect();
ByteBuffer net = fuzzer.asNetworkBuffer(send);
fuzzer.send(net,6);
fuzzer.send(net,11);
TimeUnit.SECONDS.sleep(1);
fuzzer.send(net,1);
TimeUnit.SECONDS.sleep(1);
fuzzer.send(net,100); // the rest
fuzzer.expect(expect);
}
}
}