1 /* 2 * Copyright (c) Facebook, Inc. and its affiliates. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.facebook.thrift.protocol; 18 19 import com.facebook.thrift.TException; 20 import java.util.Collections; 21 22 /** Utility class with static methods for interacting with protocol data streams. */ 23 public class TProtocolUtil { 24 25 /** The maximum recursive depth the skip() function will traverse before throwing a TException. */ 26 private static int maxSkipDepth = Integer.MAX_VALUE; 27 28 /** 29 * Specifies the maximum recursive depth that the skip function will traverse before throwing a 30 * TException. This is a global setting, so any call to skip in this JVM will enforce this value. 31 * 32 * @param depth the maximum recursive depth. A value of 2 would allow the skip function to skip a 33 * structure or collection with basic children, but it would not permit skipping a struct that 34 * had a field containing a child struct. A value of 1 would only allow skipping of simple 35 * types and empty structs/collections. 36 */ setMaxSkipDepth(int depth)37 public static void setMaxSkipDepth(int depth) { 38 maxSkipDepth = depth; 39 } 40 41 /** 42 * Skips over the next data element from the provided input TProtocol object. 43 * 44 * @param prot the protocol object to read from 45 * @param type the next value will be intepreted as this TType value. 46 */ skip(TProtocol prot, byte type)47 public static void skip(TProtocol prot, byte type) throws TException { 48 skip(prot, type, maxSkipDepth); 49 } 50 51 /** 52 * Skips over the next data element from the provided input TProtocol object. 53 * 54 * @param prot the protocol object to read from 55 * @param type the next value will be intepreted as this TType value. 56 * @param maxDepth this function will only skip complex objects to this recursive depth, to 57 * prevent Java stack overflow. 58 */ skip(TProtocol prot, byte type, int maxDepth)59 public static void skip(TProtocol prot, byte type, int maxDepth) throws TException { 60 if (maxDepth <= 0) { 61 throw new TException("Maximum skip depth exceeded"); 62 } 63 switch (type) { 64 case TType.BOOL: 65 { 66 prot.readBool(); 67 break; 68 } 69 case TType.BYTE: 70 { 71 prot.readByte(); 72 break; 73 } 74 case TType.I16: 75 { 76 prot.readI16(); 77 break; 78 } 79 case TType.I32: 80 { 81 prot.readI32(); 82 break; 83 } 84 case TType.I64: 85 { 86 prot.readI64(); 87 break; 88 } 89 case TType.DOUBLE: 90 { 91 prot.readDouble(); 92 break; 93 } 94 case TType.FLOAT: 95 { 96 prot.readFloat(); 97 break; 98 } 99 case TType.STRING: 100 { 101 prot.skipBinary(); 102 break; 103 } 104 case TType.STRUCT: 105 { 106 prot.readStructBegin( 107 Collections.<Integer, com.facebook.thrift.meta_data.FieldMetaData>emptyMap()); 108 while (true) { 109 TField field = prot.readFieldBegin(); 110 if (field.type == TType.STOP) { 111 break; 112 } 113 skip(prot, field.type, maxDepth - 1); 114 prot.readFieldEnd(); 115 } 116 prot.readStructEnd(); 117 break; 118 } 119 case TType.MAP: 120 { 121 TMap map = prot.readMapBegin(); 122 for (int i = 0; (map.size < 0) ? prot.peekMap() : (i < map.size); i++) { 123 skip(prot, map.keyType, maxDepth - 1); 124 skip(prot, map.valueType, maxDepth - 1); 125 } 126 prot.readMapEnd(); 127 break; 128 } 129 case TType.SET: 130 { 131 TSet set = prot.readSetBegin(); 132 for (int i = 0; (set.size < 0) ? prot.peekSet() : (i < set.size); i++) { 133 skip(prot, set.elemType, maxDepth - 1); 134 } 135 prot.readSetEnd(); 136 break; 137 } 138 case TType.LIST: 139 { 140 TList list = prot.readListBegin(); 141 for (int i = 0; (list.size < 0) ? prot.peekList() : (i < list.size); i++) { 142 skip(prot, list.elemType, maxDepth - 1); 143 } 144 prot.readListEnd(); 145 break; 146 } 147 default: 148 { 149 throw new TProtocolException( 150 TProtocolException.INVALID_DATA, "Invalid type encountered during skipping: " + type); 151 } 152 } 153 } 154 155 /** 156 * Attempt to determine the protocol used to serialize some data. 157 * 158 * <p>The guess is based on known specificities of supported protocols. In some cases, no guess 159 * can be done, in that case we return the fallback TProtocolFactory. To be certain to correctly 160 * detect the protocol, the first encoded field should have a field id < 256 161 * 162 * @param data The serialized data to guess the protocol for. 163 * @param fallback The TProtocol to return if no guess can be made. 164 * @return a Class implementing TProtocolFactory which can be used to create a deserializer. 165 */ guessProtocolFactory(byte[] data, TProtocolFactory fallback)166 public static TProtocolFactory guessProtocolFactory(byte[] data, TProtocolFactory fallback) { 167 // 168 // If the first and last bytes are opening/closing curly braces we guess the protocol as 169 // being TJSONProtocol. 170 // It could not be a TCompactBinary encoding for a field of type 0xb (Map) 171 // with delta id 7 as the last byte for TCompactBinary is always 0. 172 // 173 174 if ('{' == data[0] && '}' == data[data.length - 1]) { 175 return new TJSONProtocol.Factory(); 176 } 177 178 // 179 // If the last byte is not 0, then it cannot be TCompactProtocol, it must be 180 // TBinaryProtocol. 181 // 182 183 if (data[data.length - 1] != 0) { 184 return new TBinaryProtocol.Factory(); 185 } 186 187 // 188 // A first byte of value > 16 indicates TCompactProtocol was used, and the first byte 189 // encodes a delta field id (id <= 15) and a field type. 190 // 191 192 if (data[0] > 0x10) { 193 return new TCompactProtocol.Factory(); 194 } 195 196 // 197 // If the second byte is 0 then it is a field id < 256 encoded by TBinaryProtocol. 198 // It cannot possibly be TCompactProtocol since a value of 0 would imply a field id 199 // of 0 as the zig zag varint encoding would end. 200 // 201 202 if (data.length > 1 && 0 == data[1]) { 203 return new TBinaryProtocol.Factory(); 204 } 205 206 // 207 // If bit 7 of the first byte of the field id is set then we have two choices: 208 // 1. A field id > 63 was encoded with TCompactProtocol. 209 // 2. A field id > 0x7fff (32767) was encoded with TBinaryProtocol and the last byte of the 210 // serialized data is 0. 211 // Option 2 is impossible since field ids are short and thus limited to 32767. 212 // 213 214 if (data.length > 1 && (data[1] & 0x80) != 0) { 215 return new TCompactProtocol.Factory(); 216 } 217 218 // 219 // The remaining case is either a field id <= 63 encoded as TCompactProtocol, 220 // one >= 256 encoded with TBinaryProtocol with a last byte at 0, or an empty structure. 221 // As we cannot really decide, we return the fallback protocol. 222 // 223 return fallback; 224 } 225 } 226