blob: 3b489e57a07c295de86cc715585b4df3caf19b13 [file] [log] [blame]
Henrique Mendonça095ddb72013-09-20 19:38:03 +02001/*
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/*jshint evil:true*/
21
22/**
23 * The Thrift namespace houses the Apache Thrift JavaScript library
24 * elements providing JavaScript bindings for the Apache Thrift RPC
25 * system. Users will typically only directly make use of the
26 * Transport and Protocol constructors.
27 * @namespace
28 * @example
29 * var transport = new Thrift.Transport("http://localhost:8585");
30 * var protocol = new Thrift.Protocol(transport);
31 * var client = new MyThriftSvcClient(protocol);
32 * var result = client.MyMethod();
33 */
34var Thrift = {
35 /**
36 * Thrift JavaScript library version.
37 * @readonly
38 * @const {string} Version
39 * @memberof Thrift
40 */
41 Version: '1.0.0-dev',
42
43 /**
44 * Thrift IDL type string to Id mapping.
45 * @readonly
46 * @property {number} STOP - End of a set of fields.
47 * @property {number} VOID - No value (only legal for return types).
48 * @property {number} BOOL - True/False integer.
49 * @property {number} BYTE - Signed 8 bit integer.
50 * @property {number} I08 - Signed 8 bit integer.
51 * @property {number} DOUBLE - 64 bit IEEE 854 floating point.
52 * @property {number} I16 - Signed 16 bit integer.
53 * @property {number} I32 - Signed 32 bit integer.
54 * @property {number} I64 - Signed 64 bit integer.
55 * @property {number} STRING - Array of bytes representing a string of characters.
56 * @property {number} UTF7 - Array of bytes representing a string of UTF7 encoded characters.
57 * @property {number} STRUCT - A multifield type.
58 * @property {number} MAP - A collection type (map/associative-array/dictionary).
59 * @property {number} SET - A collection type (unordered and without repeated values).
60 * @property {number} LIST - A collection type (unordered).
61 * @property {number} UTF8 - Array of bytes representing a string of UTF8 encoded characters.
62 * @property {number} UTF16 - Array of bytes representing a string of UTF16 encoded characters.
63 */
64 Type: {
65 'STOP' : 0,
66 'VOID' : 1,
67 'BOOL' : 2,
68 'BYTE' : 3,
69 'I08' : 3,
70 'DOUBLE' : 4,
71 'I16' : 6,
72 'I32' : 8,
73 'I64' : 10,
74 'STRING' : 11,
75 'UTF7' : 11,
76 'STRUCT' : 12,
77 'MAP' : 13,
78 'SET' : 14,
79 'LIST' : 15,
80 'UTF8' : 16,
81 'UTF16' : 17
82 },
83
84 /**
85 * Thrift RPC message type string to Id mapping.
86 * @readonly
87 * @property {number} CALL - RPC call sent from client to server.
88 * @property {number} REPLY - RPC call normal response from server to client.
89 * @property {number} EXCEPTION - RPC call exception response from server to client.
90 * @property {number} ONEWAY - Oneway RPC call from client to server with no response.
91 */
92 MessageType: {
93 'CALL' : 1,
94 'REPLY' : 2,
95 'EXCEPTION' : 3,
96 'ONEWAY' : 4
97 },
98
99 /**
100 * Utility function returning the count of an object's own properties.
101 * @param {object} obj - Object to test.
102 * @returns {number} number of object's own properties
103 */
104 objectLength: function(obj) {
105 var length = 0;
106 for (var k in obj) {
107 if (obj.hasOwnProperty(k)) {
108 length++;
109 }
110 }
111
112 return length;
113 },
114
115 /**
116 * Utility function to establish prototype inheritance.
117 * @see {@link http://javascript.crockford.com/prototypal.html|Prototypal Inheritance}
118 * @param {function} constructor - Contstructor function to set as derived.
119 * @param {function} superConstructor - Contstructor function to set as base.
120 * @param {string} [name] - Type name to set as name property in derived prototype.
121 */
122 inherits: function(constructor, superConstructor, name) {
123 function F() {}
124 F.prototype = superConstructor.prototype;
125 constructor.prototype = new F();
126 constructor.prototype.name = name || "";
127 }
128};
129
130/**
131 * Initializes a Thrift TException instance.
132 * @constructor
133 * @augments Error
134 * @param {string} message - The TException message (distinct from the Error message).
135 * @classdesc TException is the base class for all Thrift exceptions types.
136 */
137Thrift.TException = function(message) {
138 this.message = message;
139};
140Thrift.inherits(Thrift.TException, Error, 'TException');
141
142/**
143 * Returns the message set on the exception.
144 * @readonly
145 * @returns {string} exception message
146 */
147Thrift.TException.prototype.getMessage = function() {
148 return this.message;
149};
150
151/**
152 * Thrift Application Exception type string to Id mapping.
153 * @readonly
154 * @property {number} UNKNOWN - Unknown/undefined.
155 * @property {number} UNKNOWN_METHOD - Client attempted to call a method unknown to the server.
156 * @property {number} INVALID_MESSAGE_TYPE - Client passed an unknown/unsupported MessageType.
157 * @property {number} WRONG_METHOD_NAME - Unused.
158 * @property {number} BAD_SEQUENCE_ID - Unused in Thrift RPC, used to flag proprietary sequence number errors.
159 * @property {number} MISSING_RESULT - Raised by a server processor if a handler fails to supply the required return result.
160 * @property {number} INTERNAL_ERROR - Something bad happened.
161 * @property {number} PROTOCOL_ERROR - The protocol layer failed to serialize or deserialize data.
162 * @property {number} INVALID_TRANSFORM - Unused.
163 * @property {number} INVALID_PROTOCOL - The protocol (or version) is not supported.
164 * @property {number} UNSUPPORTED_CLIENT_TYPE - Unused.
165 */
166Thrift.TApplicationExceptionType = {
167 'UNKNOWN' : 0,
168 'UNKNOWN_METHOD' : 1,
169 'INVALID_MESSAGE_TYPE' : 2,
170 'WRONG_METHOD_NAME' : 3,
171 'BAD_SEQUENCE_ID' : 4,
172 'MISSING_RESULT' : 5,
173 'INTERNAL_ERROR' : 6,
174 'PROTOCOL_ERROR' : 7,
175 'INVALID_TRANSFORM' : 8,
176 'INVALID_PROTOCOL' : 9,
177 'UNSUPPORTED_CLIENT_TYPE' : 10
178};
179
180/**
181 * Initializes a Thrift TApplicationException instance.
182 * @constructor
183 * @augments Thrift.TException
184 * @param {string} message - The TApplicationException message (distinct from the Error message).
185 * @param {Thrift.TApplicationExceptionType} [code] - The TApplicationExceptionType code.
186 * @classdesc TApplicationException is the exception class used to propagate exceptions from an RPC server back to a calling client.
187*/
188Thrift.TApplicationException = function(message, code) {
189 this.message = message;
190 this.code = typeof code === "number" ? code : 0;
191};
192Thrift.inherits(Thrift.TApplicationException, Thrift.TException, 'TApplicationException');
193
194/**
195 * Read a TApplicationException from the supplied protocol.
196 * @param {object} input - The input protocol to read from.
197 */
198Thrift.TApplicationException.prototype.read = function(input) {
199 while (1) {
200 var ret = input.readFieldBegin();
201
202 if (ret.ftype == Thrift.Type.STOP) {
203 break;
204 }
205
206 var fid = ret.fid;
207
208 switch (fid) {
209 case 1:
210 if (ret.ftype == Thrift.Type.STRING) {
211 ret = input.readString();
212 this.message = ret.value;
213 } else {
214 ret = input.skip(ret.ftype);
215 }
216 break;
217 case 2:
218 if (ret.ftype == Thrift.Type.I32) {
219 ret = input.readI32();
220 this.code = ret.value;
221 } else {
222 ret = input.skip(ret.ftype);
223 }
224 break;
225 default:
226 ret = input.skip(ret.ftype);
227 break;
228 }
229
230 input.readFieldEnd();
231 }
232
233 input.readStructEnd();
234};
235
236/**
237 * Wite a TApplicationException to the supplied protocol.
238 * @param {object} output - The output protocol to write to.
239 */
240Thrift.TApplicationException.prototype.write = function(output) {
241 output.writeStructBegin('TApplicationException');
242
243 if (this.message) {
244 output.writeFieldBegin('message', Thrift.Type.STRING, 1);
245 output.writeString(this.getMessage());
246 output.writeFieldEnd();
247 }
248
249 if (this.code) {
250 output.writeFieldBegin('type', Thrift.Type.I32, 2);
251 output.writeI32(this.code);
252 output.writeFieldEnd();
253 }
254
255 output.writeFieldStop();
256 output.writeStructEnd();
257};
258
259/**
260 * Returns the application exception code set on the exception.
261 * @readonly
262 * @returns {Thrift.TApplicationExceptionType} exception code
263 */
264Thrift.TApplicationException.prototype.getCode = function() {
265 return this.code;
266};
267
268/**
269 * Initializes a Thrift Http[s] transport instance.
270 * Note: If you do not specify a url then you must handle XHR on your own
271 * (this is how to use js bindings in a async fashion).
272 * @constructor
273 * @param {string} [url] - The URL to connect to.
274 * @classdesc The Apache Thrift Transport layer performs byte level I/O between RPC
275 * clients and servers. The JavaScript Transport object type uses Http[s]/XHR and is
276 * the sole browser based Thrift transport. Target servers must implement the http[s]
277 * transport (see: node.js example server).
278 * @example
279 * var transport = new Thrift.Transport("http://localhost:8585");
280 */
281Thrift.Transport = function(url) {
282 this.url = url;
283 this.wpos = 0;
284 this.rpos = 0;
285
286 this.send_buf = '';
287 this.recv_buf = '';
288};
289
290Thrift.Transport.prototype = {
291 /**
292 * Gets the browser specific XmlHttpRequest Object.
293 * @returns {object} the browser XHR interface object
294 */
295 getXmlHttpRequestObject: function() {
296 try { return new XMLHttpRequest(); } catch (e1) { }
297 try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e2) { }
298 try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e3) { }
299
300 throw "Your browser doesn't support XHR.";
301 },
302
303 /**
304 * Sends the current XRH request if the transport was created with a URL and
305 * the async parameter if false. If the transport was not created with a URL
306 * or the async parameter is True or the URL is an empty string, the current
307 * send buffer is returned.
308 * @param {object} async - If true the current send buffer is returned.
309 * @returns {undefined|string} Nothing or the current send buffer.
310 * @throws {string} If XHR fails.
311 */
312 flush: function(async) {
313 //async mode
314 if (async || this.url === undefined || this.url === '') {
315 return this.send_buf;
316 }
317
318 var xreq = this.getXmlHttpRequestObject();
319
320 if (xreq.overrideMimeType) {
321 xreq.overrideMimeType('application/json');
322 }
323
324 xreq.open('POST', this.url, false);
325 xreq.send(this.send_buf);
326
327 if (xreq.readyState != 4) {
328 throw 'encountered an unknown ajax ready state: ' + xreq.readyState;
329 }
330
331 if (xreq.status != 200) {
332 throw 'encountered a unknown request status: ' + xreq.status;
333 }
334
335 this.recv_buf = xreq.responseText;
336 this.recv_buf_sz = this.recv_buf.length;
337 this.wpos = this.recv_buf.length;
338 this.rpos = 0;
339 },
340
341 /**
342 * Creates a jQuery XHR object to be used for a Thrift server call.
343 * @param {object} client - The Thrift Service client object generated by the IDL compiler.
344 * @param {object} postData - The message to send to the server.
345 * @param {function} args - The function to call if the request suceeds.
346 * @param {function} recv_method - The Thrift Service Client receive method for the call.
347 * @returns {object} A new jQuery XHR object.
348 * @throws {string} If the jQuery version is prior to 1.5 or if jQuery is not found.
349 */
350 jqRequest: function(client, postData, args, recv_method) {
351 if (typeof jQuery === 'undefined' ||
352 typeof jQuery.Deferred === 'undefined') {
353 throw 'Thrift.js requires jQuery 1.5+ to use asynchronous requests';
354 }
355
356 var thriftTransport = this;
357
358 var jqXHR = jQuery.ajax({
359 url: this.url,
360 data: postData,
361 type: 'POST',
362 cache: false,
363 contentType: 'application/json',
364 dataType: 'text thrift',
365 converters: {
366 'text thrift' : function(responseData) {
367 thriftTransport.setRecvBuffer(responseData);
368 var value = recv_method.call(client);
369 return value;
370 }
371 },
372 context: client,
373 success: jQuery.makeArray(args).pop()
374 });
375
376 return jqXHR;
377 },
378
379 /**
380 * Sets the buffer to use when receiving server responses.
381 * @param {string} buf - The buffer to receive server responses.
382 */
383 setRecvBuffer: function(buf) {
384 this.recv_buf = buf;
385 this.recv_buf_sz = this.recv_buf.length;
386 this.wpos = this.recv_buf.length;
387 this.rpos = 0;
388 },
389
390 /**
391 * Returns true if the transport is open, in browser based JavaScript
392 * this function always returns true.
393 * @readonly
394 * @returns {boolean} Always True.
395 */
396 isOpen: function() {
397 return true;
398 },
399
400 /**
401 * Opens the transport connection, in browser based JavaScript
402 * this function is a nop.
403 */
404 open: function() {},
405
406 /**
407 * Closes the transport connection, in browser based JavaScript
408 * this function is a nop.
409 */
410 close: function() {},
411
412 /**
413 * Returns the specified number of characters from the response
414 * buffer.
415 * @param {number} len - The number of characters to return.
416 * @returns {string} Characters sent by the server.
417 */
418 read: function(len) {
419 var avail = this.wpos - this.rpos;
420
421 if (avail === 0) {
422 return '';
423 }
424
425 var give = len;
426
427 if (avail < len) {
428 give = avail;
429 }
430
431 var ret = this.read_buf.substr(this.rpos, give);
432 this.rpos += give;
433
434 //clear buf when complete?
435 return ret;
436 },
437
438 /**
439 * Returns the entire response buffer.
440 * @returns {string} Characters sent by the server.
441 */
442 readAll: function() {
443 return this.recv_buf;
444 },
445
446 /**
447 * Sets the send buffer to buf.
448 * @param {string} buf - The buffer to send.
449 */
450 write: function(buf) {
451 this.send_buf = buf;
452 },
453
454 /**
455 * Returns the send buffer.
456 * @readonly
457 * @returns {string} The send buffer.
458 */
459 getSendBuffer: function() {
460 return this.send_buf;
461 }
462
463};
464
465/**
466 * Initializes a Thrift JSON protocol instance.
467 * @constructor
468 * @param {Thrift.Transport} transport - The transport to serialize to/from.
469 * @classdesc Apache Thrift Protocols perform serialization which enables cross
470 * language RPC. The Protocol type is the JavaScript browser implementation
471 * of the Apache Thrift TJSONProtocol.
472 * @example
473 * var protocol = new Thrift.Protocol(transport);
474 */
475Thrift.Protocol = function(transport) {
476 this.transport = transport;
477};
478
479/**
480 * Thrift IDL type Id to string mapping.
481 * @readonly
482 * @see {@link Thrift.Type}
483 */
484Thrift.Protocol.Type = {};
485Thrift.Protocol.Type[Thrift.Type.BOOL] = '"tf"';
486Thrift.Protocol.Type[Thrift.Type.BYTE] = '"i8"';
487Thrift.Protocol.Type[Thrift.Type.I16] = '"i16"';
488Thrift.Protocol.Type[Thrift.Type.I32] = '"i32"';
489Thrift.Protocol.Type[Thrift.Type.I64] = '"i64"';
490Thrift.Protocol.Type[Thrift.Type.DOUBLE] = '"dbl"';
491Thrift.Protocol.Type[Thrift.Type.STRUCT] = '"rec"';
492Thrift.Protocol.Type[Thrift.Type.STRING] = '"str"';
493Thrift.Protocol.Type[Thrift.Type.MAP] = '"map"';
494Thrift.Protocol.Type[Thrift.Type.LIST] = '"lst"';
495Thrift.Protocol.Type[Thrift.Type.SET] = '"set"';
496
497/**
498 * Thrift IDL type string to Id mapping.
499 * @readonly
500 * @see {@link Thrift.Type}
501 */
502Thrift.Protocol.RType = {};
503Thrift.Protocol.RType.tf = Thrift.Type.BOOL;
504Thrift.Protocol.RType.i8 = Thrift.Type.BYTE;
505Thrift.Protocol.RType.i16 = Thrift.Type.I16;
506Thrift.Protocol.RType.i32 = Thrift.Type.I32;
507Thrift.Protocol.RType.i64 = Thrift.Type.I64;
508Thrift.Protocol.RType.dbl = Thrift.Type.DOUBLE;
509Thrift.Protocol.RType.rec = Thrift.Type.STRUCT;
510Thrift.Protocol.RType.str = Thrift.Type.STRING;
511Thrift.Protocol.RType.map = Thrift.Type.MAP;
512Thrift.Protocol.RType.lst = Thrift.Type.LIST;
513Thrift.Protocol.RType.set = Thrift.Type.SET;
514
515/**
516 * The TJSONProtocol version number.
517 * @readonly
518 * @const {number} Version
519 * @memberof Thrift.Protocol
520 */
521 Thrift.Protocol.Version = 1;
522
523Thrift.Protocol.prototype = {
524 /**
525 * Returns the underlying transport.
526 * @readonly
527 * @returns {Thrift.Transport} The underlying transport.
528 */
529 getTransport: function() {
530 return this.transport;
531 },
532
533 /**
534 * Serializes the beginning of a Thrift RPC message.
535 * @param {string} name - The service method to call.
536 * @param {Thrift.MessageType} messageType - The type of method call.
537 * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift).
538 */
539 writeMessageBegin: function(name, messageType, seqid) {
540 this.tstack = [];
541 this.tpos = [];
542
543 this.tstack.push([Thrift.Protocol.Version, '"' +
544 name + '"', messageType, seqid]);
545 },
546
547 /**
548 * Serializes the end of a Thrift RPC message.
549 */
550 writeMessageEnd: function() {
551 var obj = this.tstack.pop();
552
553 this.wobj = this.tstack.pop();
554 this.wobj.push(obj);
555
556 this.wbuf = '[' + this.wobj.join(',') + ']';
557
558 this.transport.write(this.wbuf);
559 },
560
561
562 /**
563 * Serializes the beginning of a struct.
564 * @param {string} name - The name of the struct.
565 */
566 writeStructBegin: function(name) {
567 this.tpos.push(this.tstack.length);
568 this.tstack.push({});
569 },
570
571 /**
572 * Serializes the end of a struct.
573 */
574 writeStructEnd: function() {
575
576 var p = this.tpos.pop();
577 var struct = this.tstack[p];
578 var str = '{';
579 var first = true;
580 for (var key in struct) {
581 if (first) {
582 first = false;
583 } else {
584 str += ',';
585 }
586
587 str += key + ':' + struct[key];
588 }
589
590 str += '}';
591 this.tstack[p] = str;
592 },
593
594 /**
595 * Serializes the beginning of a struct field.
596 * @param {string} name - The name of the field.
597 * @param {Thrift.Protocol.Type} fieldType - The data type of the field.
598 * @param {number} fieldId - The field's unique identifier.
599 */
600 writeFieldBegin: function(name, fieldType, fieldId) {
601 this.tpos.push(this.tstack.length);
602 this.tstack.push({ 'fieldId': '"' +
603 fieldId + '"', 'fieldType': Thrift.Protocol.Type[fieldType]
604 });
605
606 },
607
608 /**
609 * Serializes the end of a field.
610 */
611 writeFieldEnd: function() {
612 var value = this.tstack.pop();
613 var fieldInfo = this.tstack.pop();
614
615 this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
616 fieldInfo.fieldType + ':' + value + '}';
617 this.tpos.pop();
618 },
619
620 /**
621 * Serializes the end of the set of fields for a struct.
622 */
623 writeFieldStop: function() {
624 //na
625 },
626
627 /**
628 * Serializes the beginning of a map collection.
629 * @param {Thrift.Type} keyType - The data type of the key.
630 * @param {Thrift.Type} valType - The data type of the value.
631 * @param {number} [size] - The number of elements in the map (ignored).
632 */
633 writeMapBegin: function(keyType, valType, size) {
634 this.tpos.push(this.tstack.length);
635 this.tstack.push([Thrift.Protocol.Type[keyType],
636 Thrift.Protocol.Type[valType], 0]);
637 },
638
639 /**
640 * Serializes the end of a map.
641 */
642 writeMapEnd: function() {
643 var p = this.tpos.pop();
644
645 if (p == this.tstack.length) {
646 return;
647 }
648
649 if ((this.tstack.length - p - 1) % 2 !== 0) {
650 this.tstack.push('');
651 }
652
653 var size = (this.tstack.length - p - 1) / 2;
654
655 this.tstack[p][this.tstack[p].length - 1] = size;
656
657 var map = '}';
658 var first = true;
659 while (this.tstack.length > p + 1) {
660 var v = this.tstack.pop();
661 var k = this.tstack.pop();
662 if (first) {
663 first = false;
664 } else {
665 map = ',' + map;
666 }
667
668 if (! isNaN(k)) { k = '"' + k + '"'; } //json "keys" need to be strings
669 map = k + ':' + v + map;
670 }
671 map = '{' + map;
672
673 this.tstack[p].push(map);
674 this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
675 },
676
677 /**
678 * Serializes the beginning of a list collection.
679 * @param {Thrift.Type} elemType - The data type of the elements.
680 * @param {number} size - The number of elements in the list.
681 */
682 writeListBegin: function(elemType, size) {
683 this.tpos.push(this.tstack.length);
684 this.tstack.push([Thrift.Protocol.Type[elemType], size]);
685 },
686
687 /**
688 * Serializes the end of a list.
689 */
690 writeListEnd: function() {
691 var p = this.tpos.pop();
692
693 while (this.tstack.length > p + 1) {
694 var tmpVal = this.tstack[p + 1];
695 this.tstack.splice(p + 1, 1);
696 this.tstack[p].push(tmpVal);
697 }
698
699 this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
700 },
701
702 /**
703 * Serializes the beginning of a set collection.
704 * @param {Thrift.Type} elemType - The data type of the elements.
705 * @param {number} size - The number of elements in the list.
706 */
707 writeSetBegin: function(elemType, size) {
708 this.tpos.push(this.tstack.length);
709 this.tstack.push([Thrift.Protocol.Type[elemType], size]);
710 },
711
712 /**
713 * Serializes the end of a set.
714 */
715 writeSetEnd: function() {
716 var p = this.tpos.pop();
717
718 while (this.tstack.length > p + 1) {
719 var tmpVal = this.tstack[p + 1];
720 this.tstack.splice(p + 1, 1);
721 this.tstack[p].push(tmpVal);
722 }
723
724 this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
725 },
726
727 /** Serializes a boolean */
728 writeBool: function(value) {
729 this.tstack.push(value ? 1 : 0);
730 },
731
732 /** Serializes a number */
733 writeByte: function(i8) {
734 this.tstack.push(i8);
735 },
736
737 /** Serializes a number */
738 writeI16: function(i16) {
739 this.tstack.push(i16);
740 },
741
742 /** Serializes a number */
743 writeI32: function(i32) {
744 this.tstack.push(i32);
745 },
746
747 /** Serializes a number */
748 writeI64: function(i64) {
749 this.tstack.push(i64);
750 },
751
752 /** Serializes a number */
753 writeDouble: function(dbl) {
754 this.tstack.push(dbl);
755 },
756
757 /** Serializes a string */
758 writeString: function(str) {
759 // We do not encode uri components for wire transfer:
760 if (str === null) {
761 this.tstack.push(null);
762 } else {
763 // concat may be slower than building a byte buffer
764 var escapedString = '';
765 for (var i = 0; i < str.length; i++) {
766 var ch = str.charAt(i); // a single double quote: "
767 if (ch === '\"') {
768 escapedString += '\\\"'; // write out as: \"
769 } else if (ch === '\\') { // a single backslash: \
770 escapedString += '\\\\'; // write out as: \\
771 /* Currently escaped forward slashes break TJSONProtocol.
772 * As it stands, we can simply pass forward slashes into
773 * our strings across the wire without being escaped.
774 * I think this is the protocol's bug, not thrift.js
775 * } else if(ch === '/') { // a single forward slash: /
776 * escapedString += '\\/'; // write out as \/
777 * }
778 */
779 } else if (ch === '\b') { // a single backspace: invisible
780 escapedString += '\\b'; // write out as: \b"
781 } else if (ch === '\f') { // a single formfeed: invisible
782 escapedString += '\\f'; // write out as: \f"
783 } else if (ch === '\n') { // a single newline: invisible
784 escapedString += '\\n'; // write out as: \n"
785 } else if (ch === '\r') { // a single return: invisible
786 escapedString += '\\r'; // write out as: \r"
787 } else if (ch === '\t') { // a single tab: invisible
788 escapedString += '\\t'; // write out as: \t"
789 } else {
790 escapedString += ch; // Else it need not be escaped
791 }
792 }
793 this.tstack.push('"' + escapedString + '"');
794 }
795 },
796
797 /** Serializes a string */
798 writeBinary: function(str) {
799 this.writeString(str);
800 },
801
802 /**
803 @class
804 @name AnonReadMessageBeginReturn
805 @property {string} fname - The name of the service method.
806 @property {Thrift.MessageType} mtype - The type of message call.
807 @property {number} rseqid - The sequence number of the message (0 in Thrift RPC).
808 */
809 /**
810 * Deserializes the beginning of a message.
811 * @returns {AnonReadMessageBeginReturn}
812 */
813 readMessageBegin: function() {
814 this.rstack = [];
815 this.rpos = [];
816
817 if (typeof jQuery !== 'undefined') {
818 this.robj = jQuery.parseJSON(this.transport.readAll());
819 } else {
820 this.robj = eval(this.transport.readAll());
821 }
822
823 var r = {};
824 var version = this.robj.shift();
825
826 if (version != Thrift.Protocol.Version) {
827 throw 'Wrong thrift protocol version: ' + version;
828 }
829
830 r.fname = this.robj.shift();
831 r.mtype = this.robj.shift();
832 r.rseqid = this.robj.shift();
833
834
835 //get to the main obj
836 this.rstack.push(this.robj.shift());
837
838 return r;
839 },
840
841 /** Deserializes the end of a message. */
842 readMessageEnd: function() {
843 },
844
845 /**
846 * Deserializes the beginning of a struct.
847 * @param {string} [name] - The name of the struct (ignored)
848 * @returns {object} - An object with an empty string fname property
849 */
850 readStructBegin: function(name) {
851 var r = {};
852 r.fname = '';
853
854 //incase this is an array of structs
855 if (this.rstack[this.rstack.length - 1] instanceof Array) {
856 this.rstack.push(this.rstack[this.rstack.length - 1].shift());
857 }
858
859 return r;
860 },
861
862 /** Deserializes the end of a struct. */
863 readStructEnd: function() {
864 if (this.rstack[this.rstack.length - 2] instanceof Array) {
865 this.rstack.pop();
866 }
867 },
868
869 /**
870 @class
871 @name AnonReadFieldBeginReturn
872 @property {string} fname - The name of the field (always '').
873 @property {Thrift.Type} ftype - The data type of the field.
874 @property {number} fid - The unique identifier of the field.
875 */
876 /**
877 * Deserializes the beginning of a field.
878 * @returns {AnonReadFieldBeginReturn}
879 */
880 readFieldBegin: function() {
881 var r = {};
882
883 var fid = -1;
884 var ftype = Thrift.Type.STOP;
885
886 //get a fieldId
887 for (var f in (this.rstack[this.rstack.length - 1])) {
888 if (f === null) {
889 continue;
890 }
891
892 fid = parseInt(f, 10);
893 this.rpos.push(this.rstack.length);
894
895 var field = this.rstack[this.rstack.length - 1][fid];
896
897 //remove so we don't see it again
898 delete this.rstack[this.rstack.length - 1][fid];
899
900 this.rstack.push(field);
901
902 break;
903 }
904
905 if (fid != -1) {
906
907 //should only be 1 of these but this is the only
908 //way to match a key
909 for (var i in (this.rstack[this.rstack.length - 1])) {
910 if (Thrift.Protocol.RType[i] === null) {
911 continue;
912 }
913
914 ftype = Thrift.Protocol.RType[i];
915 this.rstack[this.rstack.length - 1] =
916 this.rstack[this.rstack.length - 1][i];
917 }
918 }
919
920 r.fname = '';
921 r.ftype = ftype;
922 r.fid = fid;
923
924 return r;
925 },
926
927 /** Deserializes the end of a field. */
928 readFieldEnd: function() {
929 var pos = this.rpos.pop();
930
931 //get back to the right place in the stack
932 while (this.rstack.length > pos) {
933 this.rstack.pop();
934 }
935
936 },
937
938 /**
939 @class
940 @name AnonReadMapBeginReturn
941 @property {Thrift.Type} ktype - The data type of the key.
942 @property {Thrift.Type} vtype - The data type of the value.
943 @property {number} size - The number of elements in the map.
944 */
945 /**
946 * Deserializes the beginning of a map.
947 * @returns {AnonReadMapBeginReturn}
948 */
949 readMapBegin: function() {
950 var map = this.rstack.pop();
951
952 var r = {};
953 r.ktype = Thrift.Protocol.RType[map.shift()];
954 r.vtype = Thrift.Protocol.RType[map.shift()];
955 r.size = map.shift();
956
957
958 this.rpos.push(this.rstack.length);
959 this.rstack.push(map.shift());
960
961 return r;
962 },
963
964 /** Deserializes the end of a map. */
965 readMapEnd: function() {
966 this.readFieldEnd();
967 },
968
969 /**
970 @class
971 @name AnonReadColBeginReturn
972 @property {Thrift.Type} etype - The data type of the element.
973 @property {number} size - The number of elements in the collection.
974 */
975 /**
976 * Deserializes the beginning of a list.
977 * @returns {AnonReadColBeginReturn}
978 */
979 readListBegin: function() {
980 var list = this.rstack[this.rstack.length - 1];
981
982 var r = {};
983 r.etype = Thrift.Protocol.RType[list.shift()];
984 r.size = list.shift();
985
986 this.rpos.push(this.rstack.length);
987 this.rstack.push(list);
988
989 return r;
990 },
991
992 /** Deserializes the end of a list. */
993 readListEnd: function() {
994 this.readFieldEnd();
995 },
996
997 /**
998 * Deserializes the beginning of a set.
999 * @returns {AnonReadColBeginReturn}
1000 */
1001 readSetBegin: function(elemType, size) {
1002 return this.readListBegin(elemType, size);
1003 },
1004
1005 /** Deserializes the end of a set. */
1006 readSetEnd: function() {
1007 return this.readListEnd();
1008 },
1009
1010 /** Returns an object with a value property set to
1011 * False unless the next number in the protocol buffer
1012 * is 1, in which case teh value property is True */
1013 readBool: function() {
1014 var r = this.readI32();
1015
1016 if (r !== null && r.value == '1') {
1017 r.value = true;
1018 } else {
1019 r.value = false;
1020 }
1021
1022 return r;
1023 },
1024
1025 /** Returns the an object with a value property set to the
1026 next value found in the protocol buffer */
1027 readByte: function() {
1028 return this.readI32();
1029 },
1030
1031 /** Returns the an object with a value property set to the
1032 next value found in the protocol buffer */
1033 readI16: function() {
1034 return this.readI32();
1035 },
1036
1037 /** Returns the an object with a value property set to the
1038 next value found in the protocol buffer */
1039 readI32: function(f) {
1040 if (f === undefined) {
1041 f = this.rstack[this.rstack.length - 1];
1042 }
1043
1044 var r = {};
1045
1046 if (f instanceof Array) {
1047 if (f.length === 0) {
1048 r.value = undefined;
1049 } else {
1050 r.value = f.shift();
1051 }
1052 } else if (f instanceof Object) {
1053 for (var i in f) {
1054 if (i === null) {
1055 continue;
1056 }
1057 this.rstack.push(f[i]);
1058 delete f[i];
1059
1060 r.value = i;
1061 break;
1062 }
1063 } else {
1064 r.value = f;
1065 this.rstack.pop();
1066 }
1067
1068 return r;
1069 },
1070
1071 /** Returns the an object with a value property set to the
1072 next value found in the protocol buffer */
1073 readI64: function() {
1074 return this.readI32();
1075 },
1076
1077 /** Returns the an object with a value property set to the
1078 next value found in the protocol buffer */
1079 readDouble: function() {
1080 return this.readI32();
1081 },
1082
1083 /** Returns the an object with a value property set to the
1084 next value found in the protocol buffer */
1085 readString: function() {
1086 var r = this.readI32();
1087 return r;
1088 },
1089
1090 /** Returns the an object with a value property set to the
1091 next value found in the protocol buffer */
1092 readBinary: function() {
1093 return this.readString();
1094 },
1095
1096 /**
1097 * Method to arbitrarily skip over data (not implemented).
1098 * @throws {string} this method is not implemented and always throws.
1099 */
1100 skip: function(type) {
1101 throw 'skip not supported yet';
1102 }
1103};