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