blob: bd65b71144e4bd3cf892026d07fc6a4b315819b8 [file] [log] [blame]
Jake Farrellb95b0ff2012-03-22 21:49:10 +00001/*
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/**
21 * Defines the basic interface for a Thrift protocol and associated exception
22 * types.
23 *
24 * Most parts of the protocol API are typically not used in client code, as
25 * the actual serialization code is generated by thrift.codegen.* – the only
26 * interesting thing usually is that there are protocols which can be created
27 * from transports and passed around.
28 */
29module thrift.protocol.base;
30
31import thrift.base;
32import thrift.transport.base;
33
34/**
35 * The field types Thrift protocols support.
36 */
37enum TType : byte {
38 STOP = 0, /// Used to mark the end of a sequence of fields.
39 VOID = 1, ///
40 BOOL = 2, ///
41 BYTE = 3, ///
42 DOUBLE = 4, ///
43 I16 = 6, ///
44 I32 = 8, ///
45 I64 = 10, ///
46 STRING = 11, ///
47 STRUCT = 12, ///
48 MAP = 13, ///
49 SET = 14, ///
50 LIST = 15 ///
51}
52
53/**
54 * Types of Thrift RPC messages.
55 */
56enum TMessageType : byte {
57 CALL = 1, /// Call of a normal, two-way RPC method.
58 REPLY = 2, /// Reply to a normal method call.
59 EXCEPTION = 3, /// Reply to a method call if target raised a TApplicationException.
60 ONEWAY = 4 /// Call of a one-way RPC method which is not followed by a reply.
61}
62
63/**
64 * Descriptions of Thrift entities.
65 */
66struct TField {
67 string name;
68 TType type;
69 short id;
70}
71
72/// ditto
73struct TList {
74 TType elemType;
75 size_t size;
76}
77
78/// ditto
79struct TMap {
80 TType keyType;
81 TType valueType;
82 size_t size;
83}
84
85/// ditto
86struct TMessage {
87 string name;
88 TMessageType type;
89 int seqid;
90}
91
92/// ditto
93struct TSet {
94 TType elemType;
95 size_t size;
96}
97
98/// ditto
99struct TStruct {
100 string name;
101}
102
103/**
104 * Interface for a Thrift protocol implementation. Essentially, it defines
105 * a way of reading and writing all the base types, plus a mechanism for
106 * writing out structs with indexed fields.
107 *
108 * TProtocol objects should not be shared across multiple encoding contexts,
109 * as they may need to maintain internal state in some protocols (e.g. JSON).
110 * Note that is is acceptable for the TProtocol module to do its own internal
111 * buffered reads/writes to the underlying TTransport where appropriate (i.e.
112 * when parsing an input XML stream, reading could be batched rather than
113 * looking ahead character by character for a close tag).
114 */
115interface TProtocol {
116 /// The underlying transport used by the protocol.
117 TTransport transport() @property;
118
119 /*
120 * Writing methods.
121 */
122
123 void writeBool(bool b); ///
124 void writeByte(byte b); ///
125 void writeI16(short i16); ///
126 void writeI32(int i32); ///
127 void writeI64(long i64); ///
128 void writeDouble(double dub); ///
129 void writeString(string str); ///
130 void writeBinary(ubyte[] buf); ///
131
132 void writeMessageBegin(TMessage message); ///
133 void writeMessageEnd(); ///
134 void writeStructBegin(TStruct tstruct); ///
135 void writeStructEnd(); ///
136 void writeFieldBegin(TField field); ///
137 void writeFieldEnd(); ///
138 void writeFieldStop(); ///
139 void writeListBegin(TList list); ///
140 void writeListEnd(); ///
141 void writeMapBegin(TMap map); ///
142 void writeMapEnd(); ///
143 void writeSetBegin(TSet set); ///
144 void writeSetEnd(); ///
145
146 /*
147 * Reading methods.
148 */
149
150 bool readBool(); ///
151 byte readByte(); ///
152 short readI16(); ///
153 int readI32(); ///
154 long readI64(); ///
155 double readDouble(); ///
156 string readString(); ///
157 ubyte[] readBinary(); ///
158
159 TMessage readMessageBegin(); ///
160 void readMessageEnd(); ///
161 TStruct readStructBegin(); ///
162 void readStructEnd(); ///
163 TField readFieldBegin(); ///
164 void readFieldEnd(); ///
165 TList readListBegin(); ///
166 void readListEnd(); ///
167 TMap readMapBegin(); ///
168 void readMapEnd(); ///
169 TSet readSetBegin(); ///
170 void readSetEnd(); ///
171
172 /**
173 * Reset any internal state back to a blank slate, if the protocol is
174 * stateful.
175 */
176 void reset();
177}
178
179/**
180 * true if T is a TProtocol.
181 */
182template isTProtocol(T) {
183 enum isTProtocol = is(T : TProtocol);
184}
185
186unittest {
187 static assert(isTProtocol!TProtocol);
188 static assert(!isTProtocol!void);
189}
190
191/**
192 * Creates a protocol operating on a given transport.
193 */
194interface TProtocolFactory {
195 ///
196 TProtocol getProtocol(TTransport trans);
197}
198
199/**
200 * A protocol-level exception.
201 */
202class TProtocolException : TException {
203 /// The possible exception types.
204 enum Type {
205 UNKNOWN, ///
206 INVALID_DATA, ///
207 NEGATIVE_SIZE, ///
208 SIZE_LIMIT, ///
209 BAD_VERSION, ///
210 NOT_IMPLEMENTED ///
211 }
212
213 ///
214 this(Type type, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
215 static string msgForType(Type type) {
216 switch (type) {
217 case Type.UNKNOWN: return "Unknown protocol exception";
218 case Type.INVALID_DATA: return "Invalid data";
219 case Type.NEGATIVE_SIZE: return "Negative size";
220 case Type.SIZE_LIMIT: return "Exceeded size limit";
221 case Type.BAD_VERSION: return "Invalid version";
222 case Type.NOT_IMPLEMENTED: return "Not implemented";
223 default: return "(Invalid exception type)";
224 }
225 }
226 this(msgForType(type), type, file, line, next);
227 }
228
229 ///
230 this(string msg, string file = __FILE__, size_t line = __LINE__,
231 Throwable next = null)
232 {
233 this(msg, Type.UNKNOWN, file, line, next);
234 }
235
236 ///
237 this(string msg, Type type, string file = __FILE__, size_t line = __LINE__,
238 Throwable next = null)
239 {
240 super(msg, file, line, next);
241 type_ = type;
242 }
243
244 ///
245 Type type() const @property {
246 return type_;
247 }
248
249protected:
250 Type type_;
251}
252
253/**
254 * Skips a field of the given type on the protocol.
255 *
Jake Farrellc02efe22012-08-18 03:31:28 +0000256 * The main purpose of skip() is to allow treating struct and container types,
Jake Farrellb95b0ff2012-03-22 21:49:10 +0000257 * (where multiple primitive types have to be skipped) the same as scalar types
258 * in generated code.
259 */
260void skip(Protocol)(Protocol prot, TType type) if (is(Protocol : TProtocol)) {
261 final switch (type) {
262 case TType.BOOL:
263 prot.readBool();
264 break;
265
266 case TType.BYTE:
267 prot.readByte();
268 break;
269
270 case TType.I16:
271 prot.readI16();
272 break;
273
274 case TType.I32:
275 prot.readI32();
276 break;
277
278 case TType.I64:
279 prot.readI64();
280 break;
281
282 case TType.DOUBLE:
283 prot.readDouble();
284 break;
285
286 case TType.STRING:
287 prot.readBinary();
288 break;
289
290 case TType.STRUCT:
291 prot.readStructBegin();
292 while (true) {
293 auto f = prot.readFieldBegin();
294 if (f.type == TType.STOP) break;
295 skip(prot, f.type);
296 prot.readFieldEnd();
297 }
298 prot.readStructEnd();
299 break;
300
301 case TType.LIST:
302 auto l = prot.readListBegin();
303 foreach (i; 0 .. l.size) {
304 skip(prot, l.elemType);
305 }
306 prot.readListEnd();
307 break;
308
309 case TType.MAP:
310 auto m = prot.readMapBegin();
311 foreach (i; 0 .. m.size) {
312 skip(prot, m.keyType);
313 skip(prot, m.valueType);
314 }
315 prot.readMapEnd();
316 break;
317
318 case TType.SET:
319 auto s = prot.readSetBegin();
320 foreach (i; 0 .. s.size) {
321 skip(prot, s.elemType);
322 }
323 prot.readSetEnd();
324 break;
Jake Farrellc02efe22012-08-18 03:31:28 +0000325 case TType.STOP: goto case;
326 case TType.VOID:
327 assert(false, "Invalid field type passed.");
Jake Farrellb95b0ff2012-03-22 21:49:10 +0000328 }
329}
330
331/**
332 * Application-level exception.
333 *
334 * It is thrown if an RPC call went wrong on the application layer, e.g. if
335 * the receiver does not know the method name requested or a method invoked by
336 * the service processor throws an exception not part of the Thrift API.
337 */
338class TApplicationException : TException {
339 /// The possible exception types.
340 enum Type {
341 UNKNOWN = 0, ///
342 UNKNOWN_METHOD = 1, ///
343 INVALID_MESSAGE_TYPE = 2, ///
344 WRONG_METHOD_NAME = 3, ///
345 BAD_SEQUENCE_ID = 4, ///
346 MISSING_RESULT = 5, ///
347 INTERNAL_ERROR = 6, ///
Roger Meier01931492012-12-22 21:31:03 +0100348 PROTOCOL_ERROR = 7, ///
349 INVALID_TRANSFORM = 8, ///
350 INVALID_PROTOCOL = 9, ///
351 UNSUPPORTED_CLIENT_TYPE = 10 ///
Jake Farrellb95b0ff2012-03-22 21:49:10 +0000352 }
353
354 ///
355 this(Type type, string file = __FILE__, size_t line = __LINE__, Throwable next = null) {
356 static string msgForType(Type type) {
357 switch (type) {
358 case Type.UNKNOWN: return "Unknown application exception";
359 case Type.UNKNOWN_METHOD: return "Unknown method";
360 case Type.INVALID_MESSAGE_TYPE: return "Invalid message type";
361 case Type.WRONG_METHOD_NAME: return "Wrong method name";
362 case Type.BAD_SEQUENCE_ID: return "Bad sequence identifier";
363 case Type.MISSING_RESULT: return "Missing result";
Roger Meier01931492012-12-22 21:31:03 +0100364 case Type.INTERNAL_ERROR: return "Internal error";
365 case Type.PROTOCOL_ERROR: return "Protocol error";
366 case Type.INVALID_TRANSFORM: return "Invalid transform";
367 case Type.INVALID_PROTOCOL: return "Invalid protocol";
368 case Type.UNSUPPORTED_CLIENT_TYPE: return "Unsupported client type";
Jake Farrellb95b0ff2012-03-22 21:49:10 +0000369 default: return "(Invalid exception type)";
370 }
371 }
372 this(msgForType(type), type, file, line, next);
373 }
374
375 ///
376 this(string msg, string file = __FILE__, size_t line = __LINE__,
377 Throwable next = null)
378 {
379 this(msg, Type.UNKNOWN, file, line, next);
380 }
381
382 ///
383 this(string msg, Type type, string file = __FILE__, size_t line = __LINE__,
384 Throwable next = null)
385 {
386 super(msg, file, line, next);
387 type_ = type;
388 }
389
390 ///
391 Type type() @property const {
392 return type_;
393 }
394
395 // TODO: Replace hand-written read()/write() with thrift.codegen templates.
396
397 ///
398 void read(TProtocol iprot) {
399 iprot.readStructBegin();
400 while (true) {
401 auto f = iprot.readFieldBegin();
402 if (f.type == TType.STOP) break;
403
404 switch (f.id) {
405 case 1:
406 if (f.type == TType.STRING) {
407 msg = iprot.readString();
408 } else {
409 skip(iprot, f.type);
410 }
411 break;
412 case 2:
413 if (f.type == TType.I32) {
414 type_ = cast(Type)iprot.readI32();
415 } else {
416 skip(iprot, f.type);
417 }
418 break;
419 default:
420 skip(iprot, f.type);
421 break;
422 }
423 }
424 iprot.readStructEnd();
425 }
426
427 ///
428 void write(TProtocol oprot) const {
429 oprot.writeStructBegin(TStruct("TApplicationException"));
430
431 if (msg != null) {
432 oprot.writeFieldBegin(TField("message", TType.STRING, 1));
433 oprot.writeString(msg);
434 oprot.writeFieldEnd();
435 }
436
437 oprot.writeFieldBegin(TField("type", TType.I32, 2));
438 oprot.writeI32(type_);
439 oprot.writeFieldEnd();
440
441 oprot.writeFieldStop();
442 oprot.writeStructEnd();
443 }
444
445private:
446 Type type_;
447}