// // ======================================================================== // Copyright (c) 1995-2017 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); } } }