]>
git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/nodejs/lib/thrift/json_protocol.js
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
20 var Int64
= require('node-int64');
21 var Thrift
= require('./thrift');
22 var Type
= Thrift
.Type
;
23 var util
= require("util");
25 var Int64Util
= require('./int64_util');
26 var json_parse
= require('./json_parse');
28 var InputBufferUnderrunError
= require('./input_buffer_underrun_error');
30 module
.exports
= TJSONProtocol
;
33 * Initializes a Thrift JSON protocol instance.
35 * @param {Thrift.Transport} trans - The transport to serialize to/from.
36 * @classdesc Apache Thrift Protocols perform serialization which enables cross
37 * language RPC. The Protocol type is the JavaScript browser implementation
38 * of the Apache Thrift TJSONProtocol.
40 * var protocol = new Thrift.Protocol(transport);
42 function TJSONProtocol(trans
) {
49 * Thrift IDL type Id to string mapping.
51 * @see {@link Thrift.Type}
53 TJSONProtocol
.Type
= {};
54 TJSONProtocol
.Type
[Type
.BOOL
] = '"tf"';
55 TJSONProtocol
.Type
[Type
.BYTE
] = '"i8"';
56 TJSONProtocol
.Type
[Type
.I16
] = '"i16"';
57 TJSONProtocol
.Type
[Type
.I32
] = '"i32"';
58 TJSONProtocol
.Type
[Type
.I64
] = '"i64"';
59 TJSONProtocol
.Type
[Type
.DOUBLE
] = '"dbl"';
60 TJSONProtocol
.Type
[Type
.STRUCT
] = '"rec"';
61 TJSONProtocol
.Type
[Type
.STRING
] = '"str"';
62 TJSONProtocol
.Type
[Type
.MAP
] = '"map"';
63 TJSONProtocol
.Type
[Type
.LIST
] = '"lst"';
64 TJSONProtocol
.Type
[Type
.SET
] = '"set"';
67 * Thrift IDL type string to Id mapping.
69 * @see {@link Thrift.Type}
71 TJSONProtocol
.RType
= {};
72 TJSONProtocol
.RType
.tf
= Type
.BOOL
;
73 TJSONProtocol
.RType
.i8
= Type
.BYTE
;
74 TJSONProtocol
.RType
.i16
= Type
.I16
;
75 TJSONProtocol
.RType
.i32
= Type
.I32
;
76 TJSONProtocol
.RType
.i64
= Type
.I64
;
77 TJSONProtocol
.RType
.dbl
= Type
.DOUBLE
;
78 TJSONProtocol
.RType
.rec
= Type
.STRUCT
;
79 TJSONProtocol
.RType
.str
= Type
.STRING
;
80 TJSONProtocol
.RType
.map
= Type
.MAP
;
81 TJSONProtocol
.RType
.lst
= Type
.LIST
;
82 TJSONProtocol
.RType
.set = Type
.SET
;
85 * The TJSONProtocol version number.
87 * @const {number} Version
88 * @memberof Thrift.Protocol
90 TJSONProtocol
.Version
= 1;
92 TJSONProtocol
.prototype.flush = function() {
93 this.writeToTransportIfStackIsFlushable();
94 return this.trans
.flush();
97 TJSONProtocol
.prototype.writeToTransportIfStackIsFlushable = function() {
98 if (this.tstack
.length
=== 1) {
99 this.trans
.write(this.tstack
.pop());
104 * Serializes the beginning of a Thrift RPC message.
105 * @param {string} name - The service method to call.
106 * @param {Thrift.MessageType} messageType - The type of method call.
107 * @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift).
109 TJSONProtocol
.prototype.writeMessageBegin = function(name
, messageType
, seqid
) {
110 this.tstack
.push([TJSONProtocol
.Version
, '"' + name
+ '"', messageType
, seqid
]);
114 * Serializes the end of a Thrift RPC message.
116 TJSONProtocol
.prototype.writeMessageEnd = function() {
117 var obj
= this.tstack
.pop();
119 this.wobj
= this.tstack
.pop();
122 this.wbuf
= '[' + this.wobj
.join(',') + ']';
124 // we assume there is nothing more to come so we write
125 this.trans
.write(this.wbuf
);
129 * Serializes the beginning of a struct.
130 * @param {string} name - The name of the struct.
132 TJSONProtocol
.prototype.writeStructBegin = function(name
) {
133 this.tpos
.push(this.tstack
.length
);
134 this.tstack
.push({});
138 * Serializes the end of a struct.
140 TJSONProtocol
.prototype.writeStructEnd = function() {
141 var p
= this.tpos
.pop();
142 var struct
= this.tstack
[p
];
145 for (var key
in struct
) {
152 str
+= key
+ ':' + struct
[key
];
156 this.tstack
[p
] = str
;
158 this.writeToTransportIfStackIsFlushable();
162 * Serializes the beginning of a struct field.
163 * @param {string} name - The name of the field.
164 * @param {Thrift.Protocol.Type} fieldType - The data type of the field.
165 * @param {number} fieldId - The field's unique identifier.
167 TJSONProtocol
.prototype.writeFieldBegin = function(name
, fieldType
, fieldId
) {
168 this.tpos
.push(this.tstack
.length
);
169 this.tstack
.push({ 'fieldId': '"' +
170 fieldId
+ '"', 'fieldType': TJSONProtocol
.Type
[fieldType
]
175 * Serializes the end of a field.
177 TJSONProtocol
.prototype.writeFieldEnd = function() {
178 var value
= this.tstack
.pop();
179 var fieldInfo
= this.tstack
.pop();
181 if (':' + value
=== ":[object Object]") {
182 this.tstack
[this.tstack
.length
- 1][fieldInfo
.fieldId
] = '{' +
183 fieldInfo
.fieldType
+ ':' + JSON
.stringify(value
) + '}';
185 this.tstack
[this.tstack
.length
- 1][fieldInfo
.fieldId
] = '{' +
186 fieldInfo
.fieldType
+ ':' + value
+ '}';
190 this.writeToTransportIfStackIsFlushable();
194 * Serializes the end of the set of fields for a struct.
196 TJSONProtocol
.prototype.writeFieldStop = function() {
200 * Serializes the beginning of a map collection.
201 * @param {Thrift.Type} keyType - The data type of the key.
202 * @param {Thrift.Type} valType - The data type of the value.
203 * @param {number} [size] - The number of elements in the map (ignored).
205 TJSONProtocol
.prototype.writeMapBegin = function(keyType
, valType
, size
) {
206 //size is invalid, we'll set it on end.
207 this.tpos
.push(this.tstack
.length
);
208 this.tstack
.push([TJSONProtocol
.Type
[keyType
], TJSONProtocol
.Type
[valType
], 0]);
212 * Serializes the end of a map.
214 TJSONProtocol
.prototype.writeMapEnd = function() {
215 var p
= this.tpos
.pop();
217 if (p
== this.tstack
.length
) {
221 if ((this.tstack
.length
- p
- 1) % 2 !== 0) {
222 this.tstack
.push('');
225 var size
= (this.tstack
.length
- p
- 1) / 2;
227 this.tstack
[p
][this.tstack
[p
].length
- 1] = size
;
231 while (this.tstack
.length
> p
+ 1) {
232 var v
= this.tstack
.pop();
233 var k
= this.tstack
.pop();
240 if (! isNaN(k
)) { k
= '"' + k
+ '"'; } //json "keys" need to be strings
241 map
= k
+ ':' + v
+ map
;
245 this.tstack
[p
].push(map
);
246 this.tstack
[p
] = '[' + this.tstack
[p
].join(',') + ']';
248 this.writeToTransportIfStackIsFlushable();
252 * Serializes the beginning of a list collection.
253 * @param {Thrift.Type} elemType - The data type of the elements.
254 * @param {number} size - The number of elements in the list.
256 TJSONProtocol
.prototype.writeListBegin = function(elemType
, size
) {
257 this.tpos
.push(this.tstack
.length
);
258 this.tstack
.push([TJSONProtocol
.Type
[elemType
], size
]);
262 * Serializes the end of a list.
264 TJSONProtocol
.prototype.writeListEnd = function() {
265 var p
= this.tpos
.pop();
267 while (this.tstack
.length
> p
+ 1) {
268 var tmpVal
= this.tstack
[p
+ 1];
269 this.tstack
.splice(p
+ 1, 1);
270 this.tstack
[p
].push(tmpVal
);
273 this.tstack
[p
] = '[' + this.tstack
[p
].join(',') + ']';
275 this.writeToTransportIfStackIsFlushable();
279 * Serializes the beginning of a set collection.
280 * @param {Thrift.Type} elemType - The data type of the elements.
281 * @param {number} size - The number of elements in the list.
283 TJSONProtocol
.prototype.writeSetBegin = function(elemType
, size
) {
284 this.tpos
.push(this.tstack
.length
);
285 this.tstack
.push([TJSONProtocol
.Type
[elemType
], size
]);
289 * Serializes the end of a set.
291 TJSONProtocol
.prototype.writeSetEnd = function() {
292 var p
= this.tpos
.pop();
294 while (this.tstack
.length
> p
+ 1) {
295 var tmpVal
= this.tstack
[p
+ 1];
296 this.tstack
.splice(p
+ 1, 1);
297 this.tstack
[p
].push(tmpVal
);
300 this.tstack
[p
] = '[' + this.tstack
[p
].join(',') + ']';
302 this.writeToTransportIfStackIsFlushable();
305 /** Serializes a boolean */
306 TJSONProtocol
.prototype.writeBool = function(bool
) {
307 this.tstack
.push(bool
? 1 : 0);
310 /** Serializes a number */
311 TJSONProtocol
.prototype.writeByte = function(byte) {
312 this.tstack
.push(byte);
315 /** Serializes a number */
316 TJSONProtocol
.prototype.writeI16 = function(i16
) {
317 this.tstack
.push(i16
);
320 /** Serializes a number */
321 TJSONProtocol
.prototype.writeI32 = function(i32
) {
322 this.tstack
.push(i32
);
325 /** Serializes a number */
326 TJSONProtocol
.prototype.writeI64 = function(i64
) {
327 if (i64
instanceof Int64
) {
328 this.tstack
.push(Int64Util
.toDecimalString(i64
));
330 this.tstack
.push(i64
);
334 /** Serializes a number */
335 TJSONProtocol
.prototype.writeDouble = function(dub
) {
336 this.tstack
.push(dub
);
339 /** Serializes a string */
340 TJSONProtocol
.prototype.writeString = function(arg
) {
341 // We do not encode uri components for wire transfer:
343 this.tstack
.push(null);
345 if (typeof arg
=== 'string') {
347 } else if (arg
instanceof Buffer
) {
348 var str
= arg
.toString('utf8');
350 throw new Error('writeString called without a string/Buffer argument: ' + arg
);
353 // concat may be slower than building a byte buffer
354 var escapedString
= '';
355 for (var i
= 0; i
< str
.length
; i
++) {
356 var ch
= str
.charAt(i
); // a single double quote: "
358 escapedString
+= '\\\"'; // write out as: \"
359 } else if (ch
=== '\\') { // a single backslash: \
360 escapedString
+= '\\\\'; // write out as: \\
361 /* Currently escaped forward slashes break TJSONProtocol.
362 * As it stands, we can simply pass forward slashes into
363 * our strings across the wire without being escaped.
364 * I think this is the protocol's bug, not thrift.js
365 * } else if(ch === '/') { // a single forward slash: /
366 * escapedString += '\\/'; // write out as \/
369 } else if (ch
=== '\b') { // a single backspace: invisible
370 escapedString
+= '\\b'; // write out as: \b"
371 } else if (ch
=== '\f') { // a single formfeed: invisible
372 escapedString
+= '\\f'; // write out as: \f"
373 } else if (ch
=== '\n') { // a single newline: invisible
374 escapedString
+= '\\n'; // write out as: \n"
375 } else if (ch
=== '\r') { // a single return: invisible
376 escapedString
+= '\\r'; // write out as: \r"
377 } else if (ch
=== '\t') { // a single tab: invisible
378 escapedString
+= '\\t'; // write out as: \t"
380 escapedString
+= ch
; // Else it need not be escaped
383 this.tstack
.push('"' + escapedString
+ '"');
387 /** Serializes a string */
388 TJSONProtocol
.prototype.writeBinary = function(arg
) {
389 if (typeof arg
=== 'string') {
390 var buf
= new Buffer(arg
, 'binary');
391 } else if (arg
instanceof Buffer
||
392 Object
.prototype.toString
.call(arg
) == '[object Uint8Array]') {
395 throw new Error('writeBinary called without a string/Buffer argument: ' + arg
);
397 this.tstack
.push('"' + buf
.toString('base64') + '"');
402 * @name AnonReadMessageBeginReturn
403 * @property {string} fname - The name of the service method.
404 * @property {Thrift.MessageType} mtype - The type of message call.
405 * @property {number} rseqid - The sequence number of the message (0 in Thrift RPC).
408 * Deserializes the beginning of a message.
409 * @returns {AnonReadMessageBeginReturn}
411 TJSONProtocol
.prototype.readMessageBegin = function() {
415 //Borrow the inbound transport buffer and ensure data is present/consistent
416 var transBuf
= this.trans
.borrow();
417 if (transBuf
.readIndex
>= transBuf
.writeIndex
) {
418 throw new InputBufferUnderrunError();
420 var cursor
= transBuf
.readIndex
;
422 if (transBuf
.buf
[cursor
] !== 0x5B) { //[
423 throw new Error("Malformed JSON input, no opening bracket");
426 //Parse a single message (there may be several in the buffer)
427 // TODO: Handle characters using multiple code units
429 var openBracketCount
= 1;
430 var inString
= false;
431 for (; cursor
< transBuf
.writeIndex
; cursor
++) {
432 var chr
= transBuf
.buf
[cursor
];
433 //we use hexa charcode here because data[i] returns an int and not a char
435 if (chr
=== 0x22) { //"
437 } else if (chr
=== 0x5C) { //\
438 //escaped character, skip
442 if (chr
=== 0x5B) { //[
443 openBracketCount
+= 1;
444 } else if (chr
=== 0x5D) { //]
445 openBracketCount
-= 1;
446 if (openBracketCount
=== 0) {
447 //end of json message detected
450 } else if (chr
=== 0x22) { //"
456 if (openBracketCount
!== 0) {
457 // Missing closing bracket. Can be buffer underrun.
458 throw new InputBufferUnderrunError();
461 //Reconstitute the JSON object and conume the necessary bytes
462 this.robj
= json_parse(transBuf
.buf
.slice(transBuf
.readIndex
, cursor
+1).toString());
463 this.trans
.consume(cursor
+ 1 - transBuf
.readIndex
);
465 //Verify the protocol version
466 var version
= this.robj
.shift();
467 if (version
!= TJSONProtocol
.Version
) {
468 throw new Error('Wrong thrift protocol version: ' + version
);
471 //Objectify the thrift message {name/type/sequence-number} for return
472 // and then save the JSON object in rstack
474 r
.fname
= this.robj
.shift();
475 r
.mtype
= this.robj
.shift();
476 r
.rseqid
= this.robj
.shift();
477 this.rstack
.push(this.robj
.shift());
481 /** Deserializes the end of a message. */
482 TJSONProtocol
.prototype.readMessageEnd = function() {
486 * Deserializes the beginning of a struct.
487 * @param {string} [name] - The name of the struct (ignored)
488 * @returns {object} - An object with an empty string fname property
490 TJSONProtocol
.prototype.readStructBegin = function() {
494 //incase this is an array of structs
495 if (this.rstack
[this.rstack
.length
- 1] instanceof Array
) {
496 this.rstack
.push(this.rstack
[this.rstack
.length
- 1].shift());
502 /** Deserializes the end of a struct. */
503 TJSONProtocol
.prototype.readStructEnd = function() {
509 * @name AnonReadFieldBeginReturn
510 * @property {string} fname - The name of the field (always '').
511 * @property {Thrift.Type} ftype - The data type of the field.
512 * @property {number} fid - The unique identifier of the field.
515 * Deserializes the beginning of a field.
516 * @returns {AnonReadFieldBeginReturn}
518 TJSONProtocol
.prototype.readFieldBegin = function() {
522 var ftype
= Type
.STOP
;
525 for (var f
in (this.rstack
[this.rstack
.length
- 1])) {
530 fid
= parseInt(f
, 10);
531 this.rpos
.push(this.rstack
.length
);
533 var field
= this.rstack
[this.rstack
.length
- 1][fid
];
535 //remove so we don't see it again
536 delete this.rstack
[this.rstack
.length
- 1][fid
];
538 this.rstack
.push(field
);
544 //should only be 1 of these but this is the only
546 for (var i
in (this.rstack
[this.rstack
.length
- 1])) {
547 if (TJSONProtocol
.RType
[i
] === null) {
551 ftype
= TJSONProtocol
.RType
[i
];
552 this.rstack
[this.rstack
.length
- 1] = this.rstack
[this.rstack
.length
- 1][i
];
563 /** Deserializes the end of a field. */
564 TJSONProtocol
.prototype.readFieldEnd = function() {
565 var pos
= this.rpos
.pop();
567 //get back to the right place in the stack
568 while (this.rstack
.length
> pos
) {
575 * @name AnonReadMapBeginReturn
576 * @property {Thrift.Type} ktype - The data type of the key.
577 * @property {Thrift.Type} vtype - The data type of the value.
578 * @property {number} size - The number of elements in the map.
581 * Deserializes the beginning of a map.
582 * @returns {AnonReadMapBeginReturn}
584 TJSONProtocol
.prototype.readMapBegin = function() {
585 var map
= this.rstack
.pop();
586 var first
= map
.shift();
587 if (first
instanceof Array
) {
588 this.rstack
.push(map
);
594 r
.ktype
= TJSONProtocol
.RType
[first
];
595 r
.vtype
= TJSONProtocol
.RType
[map
.shift()];
596 r
.size
= map
.shift();
599 this.rpos
.push(this.rstack
.length
);
600 this.rstack
.push(map
.shift());
605 /** Deserializes the end of a map. */
606 TJSONProtocol
.prototype.readMapEnd = function() {
612 * @name AnonReadColBeginReturn
613 * @property {Thrift.Type} etype - The data type of the element.
614 * @property {number} size - The number of elements in the collection.
617 * Deserializes the beginning of a list.
618 * @returns {AnonReadColBeginReturn}
620 TJSONProtocol
.prototype.readListBegin = function() {
621 var list
= this.rstack
[this.rstack
.length
- 1];
624 r
.etype
= TJSONProtocol
.RType
[list
.shift()];
625 r
.size
= list
.shift();
627 this.rpos
.push(this.rstack
.length
);
628 this.rstack
.push(list
.shift());
633 /** Deserializes the end of a list. */
634 TJSONProtocol
.prototype.readListEnd = function() {
635 var pos
= this.rpos
.pop() - 2;
636 var st
= this.rstack
;
638 if (st
instanceof Array
&& st
.length
> pos
&& st
[pos
].length
> 0) {
639 st
.push(st
[pos
].shift());
644 * Deserializes the beginning of a set.
645 * @returns {AnonReadColBeginReturn}
647 TJSONProtocol
.prototype.readSetBegin = function() {
648 return this.readListBegin();
651 /** Deserializes the end of a set. */
652 TJSONProtocol
.prototype.readSetEnd = function() {
653 return this.readListEnd();
656 TJSONProtocol
.prototype.readBool = function() {
657 return this.readValue() == '1';
660 TJSONProtocol
.prototype.readByte = function() {
661 return this.readI32();
664 TJSONProtocol
.prototype.readI16 = function() {
665 return this.readI32();
668 TJSONProtocol
.prototype.readI32 = function(f
) {
669 return +this.readValue();
672 /** Returns the next value found in the protocol buffer */
673 TJSONProtocol
.prototype.readValue = function(f
) {
674 if (f
=== undefined) {
675 f
= this.rstack
[this.rstack
.length
- 1];
680 if (f
instanceof Array
) {
681 if (f
.length
=== 0) {
686 } else if (!(f
instanceof Int64
) && f
instanceof Object
) {
691 this.rstack
.push(f
[i
]);
705 TJSONProtocol
.prototype.readI64 = function() {
706 var n
= this.readValue()
707 if (typeof n
=== 'string') {
708 // Assuming no one is sending in 1.11111e+33 format
709 return Int64Util
.fromDecimalString(n
);
715 TJSONProtocol
.prototype.readDouble = function() {
716 return this.readI32();
719 TJSONProtocol
.prototype.readBinary = function() {
720 return new Buffer(this.readValue(), 'base64');
723 TJSONProtocol
.prototype.readString = function() {
724 return this.readValue();
728 * Returns the underlying transport.
730 * @returns {Thrift.Transport} The underlying transport.
732 TJSONProtocol
.prototype.getTransport = function() {
737 * Method to arbitrarily skip over data
739 TJSONProtocol
.prototype.skip = function(type
) {
763 this.readStructBegin();
765 var r
= this.readFieldBegin();
766 if (r
.ftype
=== Type
.STOP
) {
772 this.readStructEnd();
775 var mapBegin
= this.readMapBegin();
776 for (var i
= 0; i
< mapBegin
.size
; ++i
) {
777 this.skip(mapBegin
.ktype
);
778 this.skip(mapBegin
.vtype
);
783 var setBegin
= this.readSetBegin();
784 for (var i2
= 0; i2
< setBegin
.size
; ++i2
) {
785 this.skip(setBegin
.etype
);
790 var listBegin
= this.readListBegin();
791 for (var i3
= 0; i3
< listBegin
.size
; ++i3
) {
792 this.skip(listBegin
.etype
);
797 throw new Error("Invalid type: " + type
);