@@ -0,0 +1,288 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.facebook.thrift;

import com.facebook.thrift.java.test.MyListStruct;
import com.facebook.thrift.java.test.MyMapStruct;
import com.facebook.thrift.java.test.MySetStruct;
import com.facebook.thrift.protocol.TBinaryProtocol;
import com.facebook.thrift.protocol.TCompactProtocol;
import com.facebook.thrift.protocol.TProtocol;
import com.facebook.thrift.protocol.TProtocolException;
import com.facebook.thrift.protocol.TType;
import com.facebook.thrift.transport.TMemoryInputTransport;
import org.junit.Test;

public class TruncatedFrameTest extends junit.framework.TestCase {
private static final byte[] kBinaryListEncoding = {
TType.LIST, // Field Type = List
(byte) 0x00,
(byte) 0x01, // Field id = 1
TType.I64, // List type = i64
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0xFF, // List length (255 > 3!)
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x01, // value = 1L
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x01, // value = 2L
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x01, // value = 3L
(byte) 0x00, // Stop
};

private static final byte[] kCompactListEncoding = {
(byte) 0b00011001, // field id delta (0001) + type (1001) = List
(byte) 0b11100110, // list size (0111) and 7>3 + list type (0110) = i64
(byte) 0x02, // value = 1 (zigzag encoded)
(byte) 0x04, // value = 2 (zigzag encoded)
(byte) 0x06, // value = 3 (zigzag encoded)
(byte) 0x00, // Stop
};

private static final byte[] kCompactListEncoding2 = {
(byte) 0b00011001, // field id delta (0001) + type (1001) = List
(byte) 0b11110110, // list size magic marker (1111) + list type (0110) = i64
(byte) 0x64, // list actual size (varint of 1 byte here) = 100
(byte) 0x02, // value = 1 (zigzag encoded)
(byte) 0x04, // value = 2 (zigzag encoded)
(byte) 0x06, // value = 3 (zigzag encoded)
(byte) 0x00, // Stop
};

public static void testTruncated(TBase struct, TProtocol iprot) throws Exception {
try {
struct.read(iprot);
assertTrue("Not reachable", false);
} catch (TProtocolException ex) {
assertEquals(
"Not enough bytes to read the entire message, the data appears to be truncated",
ex.getMessage());
}
}

@Test
public static void testListBinary() throws Exception {
TMemoryInputTransport buf = new TMemoryInputTransport(kBinaryListEncoding);
TProtocol iprot = new TBinaryProtocol(buf);
testTruncated(new MyListStruct(), iprot);
}

@Test
public static void testListCompact() throws Exception {
TMemoryInputTransport buf = new TMemoryInputTransport(kCompactListEncoding);
TProtocol iprot = new TCompactProtocol(buf);
testTruncated(new MyListStruct(), iprot);
}

@Test
public static void testLongListCompact() throws Exception {
TMemoryInputTransport buf = new TMemoryInputTransport(kCompactListEncoding2);
TProtocol iprot = new TCompactProtocol(buf);
testTruncated(new MyListStruct(), iprot);
}

private static final byte[] kBinarySetEncoding = {
TType.SET, // Field Type = Set
(byte) 0x00,
(byte) 0x01, // Field id = 1
TType.I64, // Set type = i64
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0xFF, // Set length (255 > 3!)
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x01, // value = 1L
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x01, // value = 2L
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x01, // value = 3L
(byte) 0x00, // Stop
};

private static final byte[] kCompactSetEncoding = {
(byte) 0b00011010, // field id delta (0001) + type (1010) = Set
(byte) 0b01110110, // set size (0111) and 7>3 + set type (0110) = i64
(byte) 0x02, // value = 1 (zigzag encoded)
(byte) 0x04, // value = 2 (zigzag encoded)
(byte) 0x06, // value = 3 (zigzag encoded)
(byte) 0x00, // Stop
};

private static final byte[] kCompactSetEncoding2 = {
(byte) 0b00011010, // field id delta (0001) + type (1010) = Set
(byte) 0b11110110, // set size magic marker (1111) + set type (0110) = i64
(byte) 0x64, // set actual size (varint of 1 byte here) = 100
(byte) 0x02, // value = 1 (zigzag encoded)
(byte) 0x04, // value = 2 (zigzag encoded)
(byte) 0x06, // value = 3 (zigzag encoded)
(byte) 0x00, // Stop
};

@Test
public static void testSetBinary() throws Exception {
TMemoryInputTransport buf = new TMemoryInputTransport(kBinarySetEncoding);
TProtocol iprot = new TBinaryProtocol(buf);
testTruncated(new MySetStruct(), iprot);
}

@Test
public static void testSetCompact() throws Exception {
TMemoryInputTransport buf = new TMemoryInputTransport(kCompactSetEncoding);
TProtocol iprot = new TCompactProtocol(buf);
testTruncated(new MySetStruct(), iprot);
}

@Test
public static void testLongSetCompact() throws Exception {
TMemoryInputTransport buf = new TMemoryInputTransport(kCompactSetEncoding2);
TProtocol iprot = new TCompactProtocol(buf);
testTruncated(new MySetStruct(), iprot);
}

private static final byte[] kBinaryMapEncoding = {
TType.MAP, // field type = Map
(byte) 0x00,
(byte) 0x01, // field id = 1
TType.I64, // key type = i64
TType.STRING, // value type = string
(byte) 0x00,
(byte) 0xFF,
(byte) 0xFF,
(byte) 0xFF, // size = 0x00FFFFFF
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00, // key = 0
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x01, // string size = 1
(byte) 0x30, // string value = "0"
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x01, // key = 1
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x01, // string size = 1
(byte) 0x31, // string value = "1"
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x02, // key = 2
(byte) 0x00,
(byte) 0x00,
(byte) 0x00,
(byte) 0x01, // string size = 1
(byte) 0x32, // string value = "2"
(byte) 0x00, // Stop
};

private static final byte[] kCompactMapEncoding = {
(byte) 0b00011011, // field id delta (0001) + type (1011) = Map
(byte) 0x64, // map size (varint = 100)
(byte) 0b01101000, // key type (0110) i64, value type (1000) string
(byte) 0x00, // key value = 0
(byte) 0x01, // value: string size = 1
(byte) 0x30, // string content = "0"
(byte) 0x02, // key value = 1 (zigzag encoded)
(byte) 0x01, // value: string size = 1
(byte) 0x31, // string content = "1"
(byte) 0x04, // key value = 2 (zigzag encoded)
(byte) 0x01, // value: string size = 1
(byte) 0x32, // string content = "2"
(byte) 0x00, // Stop
};

@Test
public static void testMapBinary() throws Exception {
TMemoryInputTransport buf = new TMemoryInputTransport(kBinaryMapEncoding);
TProtocol iprot = new TBinaryProtocol(buf);
testTruncated(new MyMapStruct(), iprot);
}

@Test
public static void testMapCompact() throws Exception {
TMemoryInputTransport buf = new TMemoryInputTransport(kCompactMapEncoding);
TProtocol iprot = new TCompactProtocol(buf);
testTruncated(new MyMapStruct(), iprot);
}

private static final char[] hexArray = "0123456789ABCDEF".toCharArray();

private static String bytesToHex(byte[] bytes, int length) {
String out = "";
for (int j = 0; j < length; j++) {
int v = bytes[j] & 0xFF;
out += hexArray[v >>> 4];
out += hexArray[v & 0x0F];
out += " ";
}
return out;
}
}