blob: d605ab7dbbc937d96c1249618af9ab9f861cd25e [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.
henriquea2de4102014-02-07 14:12:56 +0100309 * @param {object} callback - Optional async callback function, receives the call result.
Henrique Mendonça095ddb72013-09-20 19:38:03 +0200310 * @returns {undefined|string} Nothing or the current send buffer.
311 * @throws {string} If XHR fails.
312 */
henriquea2de4102014-02-07 14:12:56 +0100313 flush: function(async, callback) {
Henrique Mendonça095ddb72013-09-20 19:38:03 +0200314 //async mode
henriquea2de4102014-02-07 14:12:56 +0100315 if ((async && !callback) || this.url === undefined || this.url === '') {
Henrique Mendonça095ddb72013-09-20 19:38:03 +0200316 return this.send_buf;
317 }
318
319 var xreq = this.getXmlHttpRequestObject();
320
321 if (xreq.overrideMimeType) {
322 xreq.overrideMimeType('application/json');
323 }
324
henriquea2de4102014-02-07 14:12:56 +0100325 if (callback) {
326 xreq.onreadystatechange = callback;
327 }
328
329 xreq.open('POST', this.url, !!async);
Henrique Mendonça095ddb72013-09-20 19:38:03 +0200330 xreq.send(this.send_buf);
henriquea2de4102014-02-07 14:12:56 +0100331 if (async && callback) {
332 return;
333 }
Henrique Mendonça095ddb72013-09-20 19:38:03 +0200334
335 if (xreq.readyState != 4) {
336 throw 'encountered an unknown ajax ready state: ' + xreq.readyState;
337 }
338
339 if (xreq.status != 200) {
340 throw 'encountered a unknown request status: ' + xreq.status;
341 }
342
343 this.recv_buf = xreq.responseText;
344 this.recv_buf_sz = this.recv_buf.length;
345 this.wpos = this.recv_buf.length;
346 this.rpos = 0;
347 },
348
349 /**
350 * Creates a jQuery XHR object to be used for a Thrift server call.
351 * @param {object} client - The Thrift Service client object generated by the IDL compiler.
352 * @param {object} postData - The message to send to the server.
353 * @param {function} args - The function to call if the request suceeds.
354 * @param {function} recv_method - The Thrift Service Client receive method for the call.
355 * @returns {object} A new jQuery XHR object.
356 * @throws {string} If the jQuery version is prior to 1.5 or if jQuery is not found.
357 */
358 jqRequest: function(client, postData, args, recv_method) {
359 if (typeof jQuery === 'undefined' ||
360 typeof jQuery.Deferred === 'undefined') {
361 throw 'Thrift.js requires jQuery 1.5+ to use asynchronous requests';
362 }
363
364 var thriftTransport = this;
365
366 var jqXHR = jQuery.ajax({
367 url: this.url,
368 data: postData,
369 type: 'POST',
370 cache: false,
371 contentType: 'application/json',
372 dataType: 'text thrift',
373 converters: {
374 'text thrift' : function(responseData) {
375 thriftTransport.setRecvBuffer(responseData);
376 var value = recv_method.call(client);
377 return value;
378 }
379 },
380 context: client,
381 success: jQuery.makeArray(args).pop()
382 });
383
384 return jqXHR;
385 },
386
387 /**
388 * Sets the buffer to use when receiving server responses.
389 * @param {string} buf - The buffer to receive server responses.
390 */
391 setRecvBuffer: function(buf) {
392 this.recv_buf = buf;
393 this.recv_buf_sz = this.recv_buf.length;
394 this.wpos = this.recv_buf.length;
395 this.rpos = 0;
396 },
397
398 /**
399 * Returns true if the transport is open, in browser based JavaScript
400 * this function always returns true.
401 * @readonly
402 * @returns {boolean} Always True.
403 */
404 isOpen: function() {
405 return true;
406 },
407
408 /**
409 * Opens the transport connection, in browser based JavaScript
410 * this function is a nop.
411 */
412 open: function() {},
413
414 /**
415 * Closes the transport connection, in browser based JavaScript
416 * this function is a nop.
417 */
418 close: function() {},
419
420 /**
421 * Returns the specified number of characters from the response
422 * buffer.
423 * @param {number} len - The number of characters to return.
424 * @returns {string} Characters sent by the server.
425 */
426 read: function(len) {
427 var avail = this.wpos - this.rpos;
428
429 if (avail === 0) {
430 return '';
431 }
432
433 var give = len;
434
435 if (avail < len) {
436 give = avail;
437 }
438
439 var ret = this.read_buf.substr(this.rpos, give);
440 this.rpos += give;
441
442 //clear buf when complete?
443 return ret;
444 },
445
446 /**
447 * Returns the entire response buffer.
448 * @returns {string} Characters sent by the server.
449 */
450 readAll: function() {
451 return this.recv_buf;
452 },
453
454 /**
455 * Sets the send buffer to buf.
456 * @param {string} buf - The buffer to send.
457 */
458 write: function(buf) {
459 this.send_buf = buf;
460 },
461
462 /**
463 * Returns the send buffer.
464 * @readonly
465 * @returns {string} The send buffer.
466 */
467 getSendBuffer: function() {
468 return this.send_buf;
469 }
470
471};
472
473/**
474 * Initializes a Thrift JSON protocol instance.
475 * @constructor
476 * @param {Thrift.Transport} transport - The transport to serialize to/from.
477 * @classdesc Apache Thrift Protocols perform serialization which enables cross
478 * language RPC. The Protocol type is the JavaScript browser implementation
479 * of the Apache Thrift TJSONProtocol.
480 * @example
481 * var protocol = new Thrift.Protocol(transport);
482 */
483Thrift.Protocol = function(transport) {
484 this.transport = transport;
485};
486
487/**
488 * Thrift IDL type Id to string mapping.
489 * @readonly
490 * @see {@link Thrift.Type}
491 */
492Thrift.Protocol.Type = {};
493Thrift.Protocol.Type[Thrift.Type.BOOL] = '"tf"';
494Thrift.Protocol.Type[Thrift.Type.BYTE] = '"i8"';
495Thrift.Protocol.Type[Thrift.Type.I16] = '"i16"';
496Thrift.Protocol.Type[Thrift.Type.I32] = '"i32"';
497Thrift.Protocol.Type[Thrift.Type.I64] = '"i64"';
498Thrift.Protocol.Type[Thrift.Type.DOUBLE] = '"dbl"';
499Thrift.Protocol.Type[Thrift.Type.STRUCT] = '"rec"';
500Thrift.Protocol.Type[Thrift.Type.STRING] = '"str"';
501Thrift.Protocol.Type[Thrift.Type.MAP] = '"map"';
502Thrift.Protocol.Type[Thrift.Type.LIST] = '"lst"';
503Thrift.Protocol.Type[Thrift.Type.SET] = '"set"';
504
505/**
506 * Thrift IDL type string to Id mapping.
507 * @readonly
508 * @see {@link Thrift.Type}
509 */
510Thrift.Protocol.RType = {};
511Thrift.Protocol.RType.tf = Thrift.Type.BOOL;
512Thrift.Protocol.RType.i8 = Thrift.Type.BYTE;
513Thrift.Protocol.RType.i16 = Thrift.Type.I16;
514Thrift.Protocol.RType.i32 = Thrift.Type.I32;
515Thrift.Protocol.RType.i64 = Thrift.Type.I64;
516Thrift.Protocol.RType.dbl = Thrift.Type.DOUBLE;
517Thrift.Protocol.RType.rec = Thrift.Type.STRUCT;
518Thrift.Protocol.RType.str = Thrift.Type.STRING;
519Thrift.Protocol.RType.map = Thrift.Type.MAP;
520Thrift.Protocol.RType.lst = Thrift.Type.LIST;
521Thrift.Protocol.RType.set = Thrift.Type.SET;
522
523/**
524 * The TJSONProtocol version number.
525 * @readonly
526 * @const {number} Version
527 * @memberof Thrift.Protocol
528 */
529 Thrift.Protocol.Version = 1;
530
531Thrift.Protocol.prototype = {
532 /**
533 * Returns the underlying transport.
534 * @readonly
535 * @returns {Thrift.Transport} The underlying transport.
536 */
537 getTransport: function() {
538 return this.transport;
539 },
540
541 /**
542 * Serializes the beginning of a Thrift RPC message.
543 * @param {string} name - The service method to call.
544 * @param {Thrift.MessageType} messageType - The type of method call.
545 * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift).
546 */
547 writeMessageBegin: function(name, messageType, seqid) {
548 this.tstack = [];
549 this.tpos = [];
550
551 this.tstack.push([Thrift.Protocol.Version, '"' +
552 name + '"', messageType, seqid]);
553 },
554
555 /**
556 * Serializes the end of a Thrift RPC message.
557 */
558 writeMessageEnd: function() {
559 var obj = this.tstack.pop();
560
561 this.wobj = this.tstack.pop();
562 this.wobj.push(obj);
563
564 this.wbuf = '[' + this.wobj.join(',') + ']';
565
566 this.transport.write(this.wbuf);
567 },
568
569
570 /**
571 * Serializes the beginning of a struct.
572 * @param {string} name - The name of the struct.
573 */
574 writeStructBegin: function(name) {
575 this.tpos.push(this.tstack.length);
576 this.tstack.push({});
577 },
578
579 /**
580 * Serializes the end of a struct.
581 */
582 writeStructEnd: function() {
583
584 var p = this.tpos.pop();
585 var struct = this.tstack[p];
586 var str = '{';
587 var first = true;
588 for (var key in struct) {
589 if (first) {
590 first = false;
591 } else {
592 str += ',';
593 }
594
595 str += key + ':' + struct[key];
596 }
597
598 str += '}';
599 this.tstack[p] = str;
600 },
601
602 /**
603 * Serializes the beginning of a struct field.
604 * @param {string} name - The name of the field.
605 * @param {Thrift.Protocol.Type} fieldType - The data type of the field.
606 * @param {number} fieldId - The field's unique identifier.
607 */
608 writeFieldBegin: function(name, fieldType, fieldId) {
609 this.tpos.push(this.tstack.length);
610 this.tstack.push({ 'fieldId': '"' +
611 fieldId + '"', 'fieldType': Thrift.Protocol.Type[fieldType]
612 });
613
614 },
615
616 /**
617 * Serializes the end of a field.
618 */
619 writeFieldEnd: function() {
620 var value = this.tstack.pop();
621 var fieldInfo = this.tstack.pop();
622
623 this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
624 fieldInfo.fieldType + ':' + value + '}';
625 this.tpos.pop();
626 },
627
628 /**
629 * Serializes the end of the set of fields for a struct.
630 */
631 writeFieldStop: function() {
632 //na
633 },
634
635 /**
636 * Serializes the beginning of a map collection.
637 * @param {Thrift.Type} keyType - The data type of the key.
638 * @param {Thrift.Type} valType - The data type of the value.
639 * @param {number} [size] - The number of elements in the map (ignored).
640 */
641 writeMapBegin: function(keyType, valType, size) {
642 this.tpos.push(this.tstack.length);
643 this.tstack.push([Thrift.Protocol.Type[keyType],
644 Thrift.Protocol.Type[valType], 0]);
645 },
646
647 /**
648 * Serializes the end of a map.
649 */
650 writeMapEnd: function() {
651 var p = this.tpos.pop();
652
653 if (p == this.tstack.length) {
654 return;
655 }
656
657 if ((this.tstack.length - p - 1) % 2 !== 0) {
658 this.tstack.push('');
659 }
660
661 var size = (this.tstack.length - p - 1) / 2;
662
663 this.tstack[p][this.tstack[p].length - 1] = size;
664
665 var map = '}';
666 var first = true;
667 while (this.tstack.length > p + 1) {
668 var v = this.tstack.pop();
669 var k = this.tstack.pop();
670 if (first) {
671 first = false;
672 } else {
673 map = ',' + map;
674 }
675
676 if (! isNaN(k)) { k = '"' + k + '"'; } //json "keys" need to be strings
677 map = k + ':' + v + map;
678 }
679 map = '{' + map;
680
681 this.tstack[p].push(map);
682 this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
683 },
684
685 /**
686 * Serializes the beginning of a list collection.
687 * @param {Thrift.Type} elemType - The data type of the elements.
688 * @param {number} size - The number of elements in the list.
689 */
690 writeListBegin: function(elemType, size) {
691 this.tpos.push(this.tstack.length);
692 this.tstack.push([Thrift.Protocol.Type[elemType], size]);
693 },
694
695 /**
696 * Serializes the end of a list.
697 */
698 writeListEnd: function() {
699 var p = this.tpos.pop();
700
701 while (this.tstack.length > p + 1) {
702 var tmpVal = this.tstack[p + 1];
703 this.tstack.splice(p + 1, 1);
704 this.tstack[p].push(tmpVal);
705 }
706
707 this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
708 },
709
710 /**
711 * Serializes the beginning of a set collection.
712 * @param {Thrift.Type} elemType - The data type of the elements.
713 * @param {number} size - The number of elements in the list.
714 */
715 writeSetBegin: function(elemType, size) {
716 this.tpos.push(this.tstack.length);
717 this.tstack.push([Thrift.Protocol.Type[elemType], size]);
718 },
719
720 /**
721 * Serializes the end of a set.
722 */
723 writeSetEnd: function() {
724 var p = this.tpos.pop();
725
726 while (this.tstack.length > p + 1) {
727 var tmpVal = this.tstack[p + 1];
728 this.tstack.splice(p + 1, 1);
729 this.tstack[p].push(tmpVal);
730 }
731
732 this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
733 },
734
735 /** Serializes a boolean */
736 writeBool: function(value) {
737 this.tstack.push(value ? 1 : 0);
738 },
739
740 /** Serializes a number */
741 writeByte: function(i8) {
742 this.tstack.push(i8);
743 },
744
745 /** Serializes a number */
746 writeI16: function(i16) {
747 this.tstack.push(i16);
748 },
749
750 /** Serializes a number */
751 writeI32: function(i32) {
752 this.tstack.push(i32);
753 },
754
755 /** Serializes a number */
756 writeI64: function(i64) {
757 this.tstack.push(i64);
758 },
759
760 /** Serializes a number */
761 writeDouble: function(dbl) {
762 this.tstack.push(dbl);
763 },
764
765 /** Serializes a string */
766 writeString: function(str) {
767 // We do not encode uri components for wire transfer:
768 if (str === null) {
769 this.tstack.push(null);
770 } else {
771 // concat may be slower than building a byte buffer
772 var escapedString = '';
773 for (var i = 0; i < str.length; i++) {
774 var ch = str.charAt(i); // a single double quote: "
775 if (ch === '\"') {
776 escapedString += '\\\"'; // write out as: \"
777 } else if (ch === '\\') { // a single backslash: \
778 escapedString += '\\\\'; // write out as: \\
779 /* Currently escaped forward slashes break TJSONProtocol.
780 * As it stands, we can simply pass forward slashes into
781 * our strings across the wire without being escaped.
782 * I think this is the protocol's bug, not thrift.js
783 * } else if(ch === '/') { // a single forward slash: /
784 * escapedString += '\\/'; // write out as \/
785 * }
786 */
787 } else if (ch === '\b') { // a single backspace: invisible
788 escapedString += '\\b'; // write out as: \b"
789 } else if (ch === '\f') { // a single formfeed: invisible
790 escapedString += '\\f'; // write out as: \f"
791 } else if (ch === '\n') { // a single newline: invisible
792 escapedString += '\\n'; // write out as: \n"
793 } else if (ch === '\r') { // a single return: invisible
794 escapedString += '\\r'; // write out as: \r"
795 } else if (ch === '\t') { // a single tab: invisible
796 escapedString += '\\t'; // write out as: \t"
797 } else {
798 escapedString += ch; // Else it need not be escaped
799 }
800 }
801 this.tstack.push('"' + escapedString + '"');
802 }
803 },
804
805 /** Serializes a string */
806 writeBinary: function(str) {
807 this.writeString(str);
808 },
809
810 /**
811 @class
812 @name AnonReadMessageBeginReturn
813 @property {string} fname - The name of the service method.
814 @property {Thrift.MessageType} mtype - The type of message call.
815 @property {number} rseqid - The sequence number of the message (0 in Thrift RPC).
816 */
817 /**
818 * Deserializes the beginning of a message.
819 * @returns {AnonReadMessageBeginReturn}
820 */
821 readMessageBegin: function() {
822 this.rstack = [];
823 this.rpos = [];
824
825 if (typeof jQuery !== 'undefined') {
826 this.robj = jQuery.parseJSON(this.transport.readAll());
827 } else {
828 this.robj = eval(this.transport.readAll());
829 }
830
831 var r = {};
832 var version = this.robj.shift();
833
834 if (version != Thrift.Protocol.Version) {
835 throw 'Wrong thrift protocol version: ' + version;
836 }
837
838 r.fname = this.robj.shift();
839 r.mtype = this.robj.shift();
840 r.rseqid = this.robj.shift();
841
842
843 //get to the main obj
844 this.rstack.push(this.robj.shift());
845
846 return r;
847 },
848
849 /** Deserializes the end of a message. */
850 readMessageEnd: function() {
851 },
852
853 /**
854 * Deserializes the beginning of a struct.
855 * @param {string} [name] - The name of the struct (ignored)
856 * @returns {object} - An object with an empty string fname property
857 */
858 readStructBegin: function(name) {
859 var r = {};
860 r.fname = '';
861
862 //incase this is an array of structs
863 if (this.rstack[this.rstack.length - 1] instanceof Array) {
864 this.rstack.push(this.rstack[this.rstack.length - 1].shift());
865 }
866
867 return r;
868 },
869
870 /** Deserializes the end of a struct. */
871 readStructEnd: function() {
872 if (this.rstack[this.rstack.length - 2] instanceof Array) {
873 this.rstack.pop();
874 }
875 },
876
877 /**
878 @class
879 @name AnonReadFieldBeginReturn
880 @property {string} fname - The name of the field (always '').
881 @property {Thrift.Type} ftype - The data type of the field.
882 @property {number} fid - The unique identifier of the field.
883 */
884 /**
885 * Deserializes the beginning of a field.
886 * @returns {AnonReadFieldBeginReturn}
887 */
888 readFieldBegin: function() {
889 var r = {};
890
891 var fid = -1;
892 var ftype = Thrift.Type.STOP;
893
894 //get a fieldId
895 for (var f in (this.rstack[this.rstack.length - 1])) {
896 if (f === null) {
897 continue;
898 }
899
900 fid = parseInt(f, 10);
901 this.rpos.push(this.rstack.length);
902
903 var field = this.rstack[this.rstack.length - 1][fid];
904
905 //remove so we don't see it again
906 delete this.rstack[this.rstack.length - 1][fid];
907
908 this.rstack.push(field);
909
910 break;
911 }
912
913 if (fid != -1) {
914
915 //should only be 1 of these but this is the only
916 //way to match a key
917 for (var i in (this.rstack[this.rstack.length - 1])) {
918 if (Thrift.Protocol.RType[i] === null) {
919 continue;
920 }
921
922 ftype = Thrift.Protocol.RType[i];
923 this.rstack[this.rstack.length - 1] =
924 this.rstack[this.rstack.length - 1][i];
925 }
926 }
927
928 r.fname = '';
929 r.ftype = ftype;
930 r.fid = fid;
931
932 return r;
933 },
934
935 /** Deserializes the end of a field. */
936 readFieldEnd: function() {
937 var pos = this.rpos.pop();
938
939 //get back to the right place in the stack
940 while (this.rstack.length > pos) {
941 this.rstack.pop();
942 }
943
944 },
945
946 /**
947 @class
948 @name AnonReadMapBeginReturn
949 @property {Thrift.Type} ktype - The data type of the key.
950 @property {Thrift.Type} vtype - The data type of the value.
951 @property {number} size - The number of elements in the map.
952 */
953 /**
954 * Deserializes the beginning of a map.
955 * @returns {AnonReadMapBeginReturn}
956 */
957 readMapBegin: function() {
958 var map = this.rstack.pop();
959
960 var r = {};
961 r.ktype = Thrift.Protocol.RType[map.shift()];
962 r.vtype = Thrift.Protocol.RType[map.shift()];
963 r.size = map.shift();
964
965
966 this.rpos.push(this.rstack.length);
967 this.rstack.push(map.shift());
968
969 return r;
970 },
971
972 /** Deserializes the end of a map. */
973 readMapEnd: function() {
974 this.readFieldEnd();
975 },
976
977 /**
978 @class
979 @name AnonReadColBeginReturn
980 @property {Thrift.Type} etype - The data type of the element.
981 @property {number} size - The number of elements in the collection.
982 */
983 /**
984 * Deserializes the beginning of a list.
985 * @returns {AnonReadColBeginReturn}
986 */
987 readListBegin: function() {
988 var list = this.rstack[this.rstack.length - 1];
989
990 var r = {};
991 r.etype = Thrift.Protocol.RType[list.shift()];
992 r.size = list.shift();
993
994 this.rpos.push(this.rstack.length);
995 this.rstack.push(list);
996
997 return r;
998 },
999
1000 /** Deserializes the end of a list. */
1001 readListEnd: function() {
1002 this.readFieldEnd();
1003 },
1004
1005 /**
1006 * Deserializes the beginning of a set.
1007 * @returns {AnonReadColBeginReturn}
1008 */
1009 readSetBegin: function(elemType, size) {
1010 return this.readListBegin(elemType, size);
1011 },
1012
1013 /** Deserializes the end of a set. */
1014 readSetEnd: function() {
1015 return this.readListEnd();
1016 },
1017
1018 /** Returns an object with a value property set to
1019 * False unless the next number in the protocol buffer
1020 * is 1, in which case teh value property is True */
1021 readBool: function() {
1022 var r = this.readI32();
1023
1024 if (r !== null && r.value == '1') {
1025 r.value = true;
1026 } else {
1027 r.value = false;
1028 }
1029
1030 return r;
1031 },
1032
1033 /** Returns the an object with a value property set to the
1034 next value found in the protocol buffer */
1035 readByte: function() {
1036 return this.readI32();
1037 },
1038
1039 /** Returns the an object with a value property set to the
1040 next value found in the protocol buffer */
1041 readI16: function() {
1042 return this.readI32();
1043 },
1044
1045 /** Returns the an object with a value property set to the
1046 next value found in the protocol buffer */
1047 readI32: function(f) {
1048 if (f === undefined) {
1049 f = this.rstack[this.rstack.length - 1];
1050 }
1051
1052 var r = {};
1053
1054 if (f instanceof Array) {
1055 if (f.length === 0) {
1056 r.value = undefined;
1057 } else {
1058 r.value = f.shift();
1059 }
1060 } else if (f instanceof Object) {
1061 for (var i in f) {
1062 if (i === null) {
1063 continue;
1064 }
1065 this.rstack.push(f[i]);
1066 delete f[i];
1067
1068 r.value = i;
1069 break;
1070 }
1071 } else {
1072 r.value = f;
1073 this.rstack.pop();
1074 }
1075
1076 return r;
1077 },
1078
1079 /** Returns the an object with a value property set to the
1080 next value found in the protocol buffer */
1081 readI64: function() {
1082 return this.readI32();
1083 },
1084
1085 /** Returns the an object with a value property set to the
1086 next value found in the protocol buffer */
1087 readDouble: function() {
1088 return this.readI32();
1089 },
1090
1091 /** Returns the an object with a value property set to the
1092 next value found in the protocol buffer */
1093 readString: function() {
1094 var r = this.readI32();
1095 return r;
1096 },
1097
1098 /** Returns the an object with a value property set to the
1099 next value found in the protocol buffer */
1100 readBinary: function() {
1101 return this.readString();
1102 },
1103
1104 /**
1105 * Method to arbitrarily skip over data (not implemented).
1106 * @throws {string} this method is not implemented and always throws.
1107 */
1108 skip: function(type) {
1109 throw 'skip not supported yet';
1110 }
1111};
henrique5ba91f22013-12-20 21:13:13 +01001112
1113
1114/**
1115 * Initializes a MutilplexProtocol Implementation as a Wrapper for Thrift.Protocol
1116 * @constructor
1117 */
1118Thrift.MultiplexProtocol = function (srvName, trans, strictRead, strictWrite) {
1119 Thrift.Protocol.call(this, trans, strictRead, strictWrite);
1120 this.serviceName = srvName;
1121};
1122Thrift.inherits(Thrift.MultiplexProtocol, Thrift.Protocol, 'multiplexProtocol');
1123
1124/** Override writeMessageBegin method of prototype*/
1125Thrift.MultiplexProtocol.prototype.writeMessageBegin = function (name, type, seqid) {
1126
1127 if (type === Thrift.MessageType.CALL || type === Thrift.MessageType.ONEWAY) {
1128 Thrift.Protocol.prototype.writeMessageBegin.call(this, this.serviceName + ":" + name, type, seqid);
1129 } else {
1130 Thrift.Protocol.prototype.writeMessageBegin.call(this, name, type, seqid);
1131 }
1132};
1133
1134Thrift.Multiplexer = function () {
1135 this.seqid = 0;
1136};
1137
1138/** Instantiates a multiplexed client for a specific service
1139 * @constructor
1140 * @param {String} serviceName - The transport to serialize to/from.
1141 * @param {Thrift.ServiceClient} SCl - The Service Client Class
1142 * @param {Thrift.Transport} transport - Thrift.Transport instance which provides remote host:port
1143 * @example
1144 * var mp = new Thrift.Multiplexer();
1145 * var transport = new Thrift.Transport("http://localhost:9090/foo.thrift");
1146 * var protocol = new Thrift.Protocol(transport);
1147 * var client = mp.createClient('AuthService', AuthServiceClient, transport);
1148*/
1149Thrift.Multiplexer.prototype.createClient = function (serviceName, SCl, transport) {
1150 if (SCl.Client) {
1151 SCl = SCl.Client;
1152 }
1153 var self = this;
1154 SCl.prototype.new_seqid = function () {
1155 self.seqid += 1;
1156 return self.seqid;
1157 };
1158 var client = new SCl(new Thrift.MultiplexProtocol(serviceName, transport));
1159
1160 return client;
1161};
1162
henriquea2de4102014-02-07 14:12:56 +01001163