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