blob: a0cc8e2cae1aeaa3de0817339fe4e330b99796c8 [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +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 */
David Reissdb0ea152008-02-18 01:49:37 +000019
Roger Meier4285ba22013-06-10 21:17:23 +020020#include <thrift/protocol/TJSONProtocol.h>
David Reissdb0ea152008-02-18 01:49:37 +000021
22#include <math.h>
David Reissa1771092008-04-11 22:36:31 +000023#include <boost/lexical_cast.hpp>
Roger Meier4285ba22013-06-10 21:17:23 +020024#include <thrift/protocol/TBase64Utils.h>
Roger Meier49ff8b12012-04-13 09:12:31 +000025#include <thrift/transport/TTransportException.h>
David Reissdb0ea152008-02-18 01:49:37 +000026
T Jake Lucianib5e62212009-01-31 22:36:20 +000027using namespace apache::thrift::transport;
David Reissdb0ea152008-02-18 01:49:37 +000028
T Jake Lucianib5e62212009-01-31 22:36:20 +000029namespace apache { namespace thrift { namespace protocol {
David Reissdb0ea152008-02-18 01:49:37 +000030
31
32// Static data
33
34static const uint8_t kJSONObjectStart = '{';
35static const uint8_t kJSONObjectEnd = '}';
36static const uint8_t kJSONArrayStart = '[';
37static const uint8_t kJSONArrayEnd = ']';
38static const uint8_t kJSONNewline = '\n';
39static const uint8_t kJSONPairSeparator = ':';
40static const uint8_t kJSONElemSeparator = ',';
41static const uint8_t kJSONBackslash = '\\';
42static const uint8_t kJSONStringDelimiter = '"';
43static const uint8_t kJSONZeroChar = '0';
44static const uint8_t kJSONEscapeChar = 'u';
45
46static const std::string kJSONEscapePrefix("\\u00");
47
David Reiss6713e1b2009-01-15 23:56:19 +000048static const uint32_t kThriftVersion1 = 1;
David Reissdb0ea152008-02-18 01:49:37 +000049
50static const std::string kThriftNan("NaN");
51static const std::string kThriftInfinity("Infinity");
52static const std::string kThriftNegativeInfinity("-Infinity");
53
54static const std::string kTypeNameBool("tf");
55static const std::string kTypeNameByte("i8");
56static const std::string kTypeNameI16("i16");
57static const std::string kTypeNameI32("i32");
58static const std::string kTypeNameI64("i64");
59static const std::string kTypeNameDouble("dbl");
60static const std::string kTypeNameStruct("rec");
61static const std::string kTypeNameString("str");
62static const std::string kTypeNameMap("map");
63static const std::string kTypeNameList("lst");
64static const std::string kTypeNameSet("set");
65
66static const std::string &getTypeNameForTypeID(TType typeID) {
67 switch (typeID) {
68 case T_BOOL:
69 return kTypeNameBool;
70 case T_BYTE:
71 return kTypeNameByte;
72 case T_I16:
73 return kTypeNameI16;
74 case T_I32:
75 return kTypeNameI32;
76 case T_I64:
77 return kTypeNameI64;
78 case T_DOUBLE:
79 return kTypeNameDouble;
80 case T_STRING:
81 return kTypeNameString;
82 case T_STRUCT:
83 return kTypeNameStruct;
84 case T_MAP:
85 return kTypeNameMap;
86 case T_SET:
87 return kTypeNameSet;
88 case T_LIST:
89 return kTypeNameList;
90 default:
91 throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
92 "Unrecognized type");
93 }
94}
95
96static TType getTypeIDForTypeName(const std::string &name) {
97 TType result = T_STOP; // Sentinel value
David Reiss2c9824c2008-03-02 00:20:47 +000098 if (name.length() > 1) {
David Reiss1e62ab42008-02-21 22:37:45 +000099 switch (name[0]) {
100 case 'd':
101 result = T_DOUBLE;
David Reissdb0ea152008-02-18 01:49:37 +0000102 break;
David Reiss1e62ab42008-02-21 22:37:45 +0000103 case 'i':
104 switch (name[1]) {
105 case '8':
106 result = T_BYTE;
107 break;
108 case '1':
109 result = T_I16;
110 break;
111 case '3':
112 result = T_I32;
113 break;
114 case '6':
115 result = T_I64;
116 break;
117 }
David Reissdb0ea152008-02-18 01:49:37 +0000118 break;
David Reiss1e62ab42008-02-21 22:37:45 +0000119 case 'l':
120 result = T_LIST;
David Reissdb0ea152008-02-18 01:49:37 +0000121 break;
David Reiss1e62ab42008-02-21 22:37:45 +0000122 case 'm':
123 result = T_MAP;
124 break;
125 case 'r':
126 result = T_STRUCT;
127 break;
128 case 's':
129 if (name[1] == 't') {
130 result = T_STRING;
131 }
132 else if (name[1] == 'e') {
133 result = T_SET;
134 }
135 break;
136 case 't':
137 result = T_BOOL;
David Reissdb0ea152008-02-18 01:49:37 +0000138 break;
139 }
David Reissdb0ea152008-02-18 01:49:37 +0000140 }
141 if (result == T_STOP) {
142 throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
143 "Unrecognized type");
144 }
145 return result;
146}
147
148
149// This table describes the handling for the first 0x30 characters
150// 0 : escape using "\u00xx" notation
151// 1 : just output index
152// <other> : escape using "\<other>" notation
153static const uint8_t kJSONCharTable[0x30] = {
154// 0 1 2 3 4 5 6 7 8 9 A B C D E F
155 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
157 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
158};
159
160
161// This string's characters must match up with the elements in kEscapeCharVals.
162// I don't have '/' on this list even though it appears on www.json.org --
163// it is not in the RFC
164const static std::string kEscapeChars("\"\\bfnrt");
165
166// The elements of this array must match up with the sequence of characters in
167// kEscapeChars
168const static uint8_t kEscapeCharVals[7] = {
169 '"', '\\', '\b', '\f', '\n', '\r', '\t',
170};
171
172
173// Static helper functions
174
175// Read 1 character from the transport trans and verify that it is the
176// expected character ch.
177// Throw a protocol exception if it is not.
David Reiss1e62ab42008-02-21 22:37:45 +0000178static uint32_t readSyntaxChar(TJSONProtocol::LookaheadReader &reader,
179 uint8_t ch) {
180 uint8_t ch2 = reader.read();
181 if (ch2 != ch) {
David Reissdb0ea152008-02-18 01:49:37 +0000182 throw TProtocolException(TProtocolException::INVALID_DATA,
183 "Expected \'" + std::string((char *)&ch, 1) +
David Reiss1e62ab42008-02-21 22:37:45 +0000184 "\'; got \'" + std::string((char *)&ch2, 1) +
David Reissdb0ea152008-02-18 01:49:37 +0000185 "\'.");
186 }
187 return 1;
188}
189
David Reissdb0ea152008-02-18 01:49:37 +0000190// Return the integer value of a hex character ch.
191// Throw a protocol exception if the character is not [0-9a-f].
192static uint8_t hexVal(uint8_t ch) {
193 if ((ch >= '0') && (ch <= '9')) {
194 return ch - '0';
195 }
196 else if ((ch >= 'a') && (ch <= 'f')) {
Bryan Duxbury4a2bc1b2010-11-03 17:57:38 +0000197 return ch - 'a' + 10;
David Reissdb0ea152008-02-18 01:49:37 +0000198 }
199 else {
200 throw TProtocolException(TProtocolException::INVALID_DATA,
201 "Expected hex val ([0-9a-f]); got \'"
202 + std::string((char *)&ch, 1) + "\'.");
203 }
204}
205
206// Return the hex character representing the integer val. The value is masked
207// to make sure it is in the correct range.
208static uint8_t hexChar(uint8_t val) {
209 val &= 0x0F;
210 if (val < 10) {
211 return val + '0';
212 }
213 else {
Bryan Duxbury4a2bc1b2010-11-03 17:57:38 +0000214 return val - 10 + 'a';
David Reissdb0ea152008-02-18 01:49:37 +0000215 }
216}
217
218// Return true if the character ch is in [-+0-9.Ee]; false otherwise
219static bool isJSONNumeric(uint8_t ch) {
220 switch (ch) {
221 case '+':
222 case '-':
223 case '.':
224 case '0':
225 case '1':
226 case '2':
227 case '3':
228 case '4':
229 case '5':
230 case '6':
231 case '7':
232 case '8':
233 case '9':
234 case 'E':
235 case 'e':
236 return true;
237 }
238 return false;
239}
240
241
242/**
David Reiss1e62ab42008-02-21 22:37:45 +0000243 * Class to serve as base JSON context and as base class for other context
David Reissdb0ea152008-02-18 01:49:37 +0000244 * implementations
245 */
246class TJSONContext {
247
248 public:
249
250 TJSONContext() {};
251
252 virtual ~TJSONContext() {};
253
254 /**
255 * Write context data to the transport. Default is to do nothing.
256 */
257 virtual uint32_t write(TTransport &trans) {
Roger Meier3b771a12010-11-17 22:11:26 +0000258 (void) trans;
David Reissdb0ea152008-02-18 01:49:37 +0000259 return 0;
260 };
261
262 /**
263 * Read context data from the transport. Default is to do nothing.
264 */
David Reiss1e62ab42008-02-21 22:37:45 +0000265 virtual uint32_t read(TJSONProtocol::LookaheadReader &reader) {
Roger Meier3b771a12010-11-17 22:11:26 +0000266 (void) reader;
David Reissdb0ea152008-02-18 01:49:37 +0000267 return 0;
268 };
269
270 /**
271 * Return true if numbers need to be escaped as strings in this context.
272 * Default behavior is to return false.
273 */
274 virtual bool escapeNum() {
275 return false;
276 }
277};
278
279// Context class for object member key-value pairs
280class JSONPairContext : public TJSONContext {
281
282public:
283
284 JSONPairContext() :
285 first_(true),
286 colon_(true) {
287 }
288
289 uint32_t write(TTransport &trans) {
290 if (first_) {
291 first_ = false;
292 colon_ = true;
293 return 0;
294 }
295 else {
296 trans.write(colon_ ? &kJSONPairSeparator : &kJSONElemSeparator, 1);
297 colon_ = !colon_;
298 return 1;
299 }
300 }
301
David Reiss1e62ab42008-02-21 22:37:45 +0000302 uint32_t read(TJSONProtocol::LookaheadReader &reader) {
David Reissdb0ea152008-02-18 01:49:37 +0000303 if (first_) {
304 first_ = false;
305 colon_ = true;
306 return 0;
307 }
308 else {
309 uint8_t ch = (colon_ ? kJSONPairSeparator : kJSONElemSeparator);
310 colon_ = !colon_;
David Reiss1e62ab42008-02-21 22:37:45 +0000311 return readSyntaxChar(reader, ch);
David Reissdb0ea152008-02-18 01:49:37 +0000312 }
313 }
314
315 // Numbers must be turned into strings if they are the key part of a pair
316 virtual bool escapeNum() {
317 return colon_;
318 }
319
320 private:
321
322 bool first_;
323 bool colon_;
324};
325
326// Context class for lists
327class JSONListContext : public TJSONContext {
328
329public:
330
331 JSONListContext() :
332 first_(true) {
333 }
334
335 uint32_t write(TTransport &trans) {
336 if (first_) {
337 first_ = false;
338 return 0;
339 }
340 else {
341 trans.write(&kJSONElemSeparator, 1);
342 return 1;
343 }
344 }
345
David Reiss1e62ab42008-02-21 22:37:45 +0000346 uint32_t read(TJSONProtocol::LookaheadReader &reader) {
David Reissdb0ea152008-02-18 01:49:37 +0000347 if (first_) {
348 first_ = false;
349 return 0;
350 }
351 else {
David Reiss1e62ab42008-02-21 22:37:45 +0000352 return readSyntaxChar(reader, kJSONElemSeparator);
David Reissdb0ea152008-02-18 01:49:37 +0000353 }
354 }
355
356 private:
357 bool first_;
358};
359
360
361TJSONProtocol::TJSONProtocol(boost::shared_ptr<TTransport> ptrans) :
David Reiss6806fb82010-10-06 17:09:52 +0000362 TVirtualProtocol<TJSONProtocol>(ptrans),
David Reisse71115b2010-10-06 17:09:56 +0000363 trans_(ptrans.get()),
David Reiss1e62ab42008-02-21 22:37:45 +0000364 context_(new TJSONContext()),
365 reader_(*ptrans) {
David Reissdb0ea152008-02-18 01:49:37 +0000366}
367
368TJSONProtocol::~TJSONProtocol() {}
369
370void TJSONProtocol::pushContext(boost::shared_ptr<TJSONContext> c) {
371 contexts_.push(context_);
372 context_ = c;
373}
374
375void TJSONProtocol::popContext() {
376 context_ = contexts_.top();
377 contexts_.pop();
378}
379
380// Write the character ch as a JSON escape sequence ("\u00xx")
381uint32_t TJSONProtocol::writeJSONEscapeChar(uint8_t ch) {
382 trans_->write((const uint8_t *)kJSONEscapePrefix.c_str(),
Roger Meierb69d24d2012-10-04 18:02:15 +0000383 static_cast<uint32_t>(kJSONEscapePrefix.length()));
David Reissdb0ea152008-02-18 01:49:37 +0000384 uint8_t outCh = hexChar(ch >> 4);
385 trans_->write(&outCh, 1);
386 outCh = hexChar(ch);
387 trans_->write(&outCh, 1);
388 return 6;
389}
390
391// Write the character ch as part of a JSON string, escaping as appropriate.
392uint32_t TJSONProtocol::writeJSONChar(uint8_t ch) {
393 if (ch >= 0x30) {
394 if (ch == kJSONBackslash) { // Only special character >= 0x30 is '\'
395 trans_->write(&kJSONBackslash, 1);
396 trans_->write(&kJSONBackslash, 1);
397 return 2;
398 }
399 else {
400 trans_->write(&ch, 1);
401 return 1;
402 }
403 }
404 else {
405 uint8_t outCh = kJSONCharTable[ch];
406 // Check if regular character, backslash escaped, or JSON escaped
407 if (outCh == 1) {
408 trans_->write(&ch, 1);
409 return 1;
410 }
411 else if (outCh > 1) {
412 trans_->write(&kJSONBackslash, 1);
413 trans_->write(&outCh, 1);
414 return 2;
415 }
416 else {
417 return writeJSONEscapeChar(ch);
418 }
419 }
420}
421
422// Write out the contents of the string str as a JSON string, escaping
423// characters as appropriate.
424uint32_t TJSONProtocol::writeJSONString(const std::string &str) {
425 uint32_t result = context_->write(*trans_);
426 result += 2; // For quotes
427 trans_->write(&kJSONStringDelimiter, 1);
428 std::string::const_iterator iter(str.begin());
429 std::string::const_iterator end(str.end());
430 while (iter != end) {
431 result += writeJSONChar(*iter++);
432 }
433 trans_->write(&kJSONStringDelimiter, 1);
434 return result;
435}
436
437// Write out the contents of the string as JSON string, base64-encoding
438// the string's contents, and escaping as appropriate
439uint32_t TJSONProtocol::writeJSONBase64(const std::string &str) {
440 uint32_t result = context_->write(*trans_);
441 result += 2; // For quotes
442 trans_->write(&kJSONStringDelimiter, 1);
443 uint8_t b[4];
444 const uint8_t *bytes = (const uint8_t *)str.c_str();
Roger Meierb69d24d2012-10-04 18:02:15 +0000445 if(str.length() > (std::numeric_limits<uint32_t>::max)())
446 throw TProtocolException(TProtocolException::SIZE_LIMIT);
447 uint32_t len = static_cast<uint32_t>(str.length());
David Reissdb0ea152008-02-18 01:49:37 +0000448 while (len >= 3) {
449 // Encode 3 bytes at a time
450 base64_encode(bytes, 3, b);
451 trans_->write(b, 4);
452 result += 4;
453 bytes += 3;
454 len -=3;
455 }
456 if (len) { // Handle remainder
457 base64_encode(bytes, len, b);
458 trans_->write(b, len + 1);
459 result += len + 1;
460 }
461 trans_->write(&kJSONStringDelimiter, 1);
462 return result;
463}
464
465// Convert the given integer type to a JSON number, or a string
466// if the context requires it (eg: key in a map pair).
467template <typename NumberType>
468uint32_t TJSONProtocol::writeJSONInteger(NumberType num) {
469 uint32_t result = context_->write(*trans_);
470 std::string val(boost::lexical_cast<std::string>(num));
471 bool escapeNum = context_->escapeNum();
472 if (escapeNum) {
473 trans_->write(&kJSONStringDelimiter, 1);
474 result += 1;
475 }
Roger Meierb69d24d2012-10-04 18:02:15 +0000476 if(val.length() > (std::numeric_limits<uint32_t>::max)())
477 throw TProtocolException(TProtocolException::SIZE_LIMIT);
478 trans_->write((const uint8_t *)val.c_str(), static_cast<uint32_t>(val.length()));
479 result += static_cast<uint32_t>(val.length());
David Reissdb0ea152008-02-18 01:49:37 +0000480 if (escapeNum) {
481 trans_->write(&kJSONStringDelimiter, 1);
482 result += 1;
483 }
484 return result;
485}
486
487// Convert the given double to a JSON string, which is either the number,
488// "NaN" or "Infinity" or "-Infinity".
489uint32_t TJSONProtocol::writeJSONDouble(double num) {
490 uint32_t result = context_->write(*trans_);
491 std::string val(boost::lexical_cast<std::string>(num));
492
493 // Normalize output of boost::lexical_cast for NaNs and Infinities
494 bool special = false;
495 switch (val[0]) {
496 case 'N':
497 case 'n':
498 val = kThriftNan;
499 special = true;
500 break;
501 case 'I':
502 case 'i':
503 val = kThriftInfinity;
504 special = true;
505 break;
506 case '-':
507 if ((val[1] == 'I') || (val[1] == 'i')) {
508 val = kThriftNegativeInfinity;
509 special = true;
510 }
511 break;
512 }
513
514 bool escapeNum = special || context_->escapeNum();
515 if (escapeNum) {
516 trans_->write(&kJSONStringDelimiter, 1);
517 result += 1;
518 }
Roger Meierb69d24d2012-10-04 18:02:15 +0000519 if(val.length() > (std::numeric_limits<uint32_t>::max)())
520 throw TProtocolException(TProtocolException::SIZE_LIMIT);
521 trans_->write((const uint8_t *)val.c_str(), static_cast<uint32_t>(val.length()));
522 result += static_cast<uint32_t>(val.length());
David Reissdb0ea152008-02-18 01:49:37 +0000523 if (escapeNum) {
524 trans_->write(&kJSONStringDelimiter, 1);
525 result += 1;
526 }
527 return result;
528}
529
530uint32_t TJSONProtocol::writeJSONObjectStart() {
531 uint32_t result = context_->write(*trans_);
532 trans_->write(&kJSONObjectStart, 1);
533 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
534 return result + 1;
535}
536
537uint32_t TJSONProtocol::writeJSONObjectEnd() {
538 popContext();
539 trans_->write(&kJSONObjectEnd, 1);
540 return 1;
541}
542
543uint32_t TJSONProtocol::writeJSONArrayStart() {
544 uint32_t result = context_->write(*trans_);
545 trans_->write(&kJSONArrayStart, 1);
546 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
547 return result + 1;
548}
549
550uint32_t TJSONProtocol::writeJSONArrayEnd() {
551 popContext();
552 trans_->write(&kJSONArrayEnd, 1);
553 return 1;
554}
555
556uint32_t TJSONProtocol::writeMessageBegin(const std::string& name,
557 const TMessageType messageType,
558 const int32_t seqid) {
559 uint32_t result = writeJSONArrayStart();
560 result += writeJSONInteger(kThriftVersion1);
561 result += writeJSONString(name);
562 result += writeJSONInteger(messageType);
563 result += writeJSONInteger(seqid);
564 return result;
565}
566
567uint32_t TJSONProtocol::writeMessageEnd() {
568 return writeJSONArrayEnd();
569}
570
David Reiss64120002008-04-29 23:12:24 +0000571uint32_t TJSONProtocol::writeStructBegin(const char* name) {
Roger Meier3b771a12010-11-17 22:11:26 +0000572 (void) name;
David Reissdb0ea152008-02-18 01:49:37 +0000573 return writeJSONObjectStart();
574}
575
576uint32_t TJSONProtocol::writeStructEnd() {
577 return writeJSONObjectEnd();
578}
579
David Reiss64120002008-04-29 23:12:24 +0000580uint32_t TJSONProtocol::writeFieldBegin(const char* name,
David Reissdb0ea152008-02-18 01:49:37 +0000581 const TType fieldType,
582 const int16_t fieldId) {
Roger Meier3b771a12010-11-17 22:11:26 +0000583 (void) name;
David Reissdb0ea152008-02-18 01:49:37 +0000584 uint32_t result = writeJSONInteger(fieldId);
585 result += writeJSONObjectStart();
586 result += writeJSONString(getTypeNameForTypeID(fieldType));
587 return result;
588}
589
590uint32_t TJSONProtocol::writeFieldEnd() {
591 return writeJSONObjectEnd();
592}
593
594uint32_t TJSONProtocol::writeFieldStop() {
595 return 0;
596}
597
598uint32_t TJSONProtocol::writeMapBegin(const TType keyType,
599 const TType valType,
600 const uint32_t size) {
601 uint32_t result = writeJSONArrayStart();
602 result += writeJSONString(getTypeNameForTypeID(keyType));
603 result += writeJSONString(getTypeNameForTypeID(valType));
604 result += writeJSONInteger((int64_t)size);
605 result += writeJSONObjectStart();
606 return result;
607}
608
609uint32_t TJSONProtocol::writeMapEnd() {
610 return writeJSONObjectEnd() + writeJSONArrayEnd();
611}
612
613uint32_t TJSONProtocol::writeListBegin(const TType elemType,
614 const uint32_t size) {
615 uint32_t result = writeJSONArrayStart();
616 result += writeJSONString(getTypeNameForTypeID(elemType));
617 result += writeJSONInteger((int64_t)size);
618 return result;
619}
620
621uint32_t TJSONProtocol::writeListEnd() {
622 return writeJSONArrayEnd();
623}
624
625uint32_t TJSONProtocol::writeSetBegin(const TType elemType,
626 const uint32_t size) {
627 uint32_t result = writeJSONArrayStart();
628 result += writeJSONString(getTypeNameForTypeID(elemType));
629 result += writeJSONInteger((int64_t)size);
630 return result;
631}
632
633uint32_t TJSONProtocol::writeSetEnd() {
634 return writeJSONArrayEnd();
635}
636
637uint32_t TJSONProtocol::writeBool(const bool value) {
638 return writeJSONInteger(value);
639}
640
641uint32_t TJSONProtocol::writeByte(const int8_t byte) {
David Reiss1e62ab42008-02-21 22:37:45 +0000642 // writeByte() must be handled specially becuase boost::lexical cast sees
David Reissdb0ea152008-02-18 01:49:37 +0000643 // int8_t as a text type instead of an integer type
644 return writeJSONInteger((int16_t)byte);
645}
646
647uint32_t TJSONProtocol::writeI16(const int16_t i16) {
648 return writeJSONInteger(i16);
649}
650
651uint32_t TJSONProtocol::writeI32(const int32_t i32) {
652 return writeJSONInteger(i32);
653}
654
655uint32_t TJSONProtocol::writeI64(const int64_t i64) {
656 return writeJSONInteger(i64);
657}
658
659uint32_t TJSONProtocol::writeDouble(const double dub) {
660 return writeJSONDouble(dub);
661}
662
663uint32_t TJSONProtocol::writeString(const std::string& str) {
664 return writeJSONString(str);
665}
666
667uint32_t TJSONProtocol::writeBinary(const std::string& str) {
668 return writeJSONBase64(str);
669}
670
671 /**
672 * Reading functions
673 */
674
David Reiss1e62ab42008-02-21 22:37:45 +0000675// Reads 1 byte and verifies that it matches ch.
David Reissdb0ea152008-02-18 01:49:37 +0000676uint32_t TJSONProtocol::readJSONSyntaxChar(uint8_t ch) {
David Reiss1e62ab42008-02-21 22:37:45 +0000677 return readSyntaxChar(reader_, ch);
David Reissdb0ea152008-02-18 01:49:37 +0000678}
679
680// Decodes the four hex parts of a JSON escaped string character and returns
681// the character via out. The first two characters must be "00".
682uint32_t TJSONProtocol::readJSONEscapeChar(uint8_t *out) {
683 uint8_t b[2];
684 readJSONSyntaxChar(kJSONZeroChar);
685 readJSONSyntaxChar(kJSONZeroChar);
David Reiss1e62ab42008-02-21 22:37:45 +0000686 b[0] = reader_.read();
687 b[1] = reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000688 *out = (hexVal(b[0]) << 4) + hexVal(b[1]);
689 return 4;
690}
691
692// Decodes a JSON string, including unescaping, and returns the string via str
693uint32_t TJSONProtocol::readJSONString(std::string &str, bool skipContext) {
David Reiss1e62ab42008-02-21 22:37:45 +0000694 uint32_t result = (skipContext ? 0 : context_->read(reader_));
David Reissdb0ea152008-02-18 01:49:37 +0000695 result += readJSONSyntaxChar(kJSONStringDelimiter);
David Reiss1e62ab42008-02-21 22:37:45 +0000696 uint8_t ch;
David Reiss91630732008-02-28 21:11:39 +0000697 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000698 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000699 ch = reader_.read();
700 ++result;
701 if (ch == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000702 break;
703 }
David Reiss1e62ab42008-02-21 22:37:45 +0000704 if (ch == kJSONBackslash) {
705 ch = reader_.read();
706 ++result;
707 if (ch == kJSONEscapeChar) {
708 result += readJSONEscapeChar(&ch);
David Reissdb0ea152008-02-18 01:49:37 +0000709 }
710 else {
David Reiss1e62ab42008-02-21 22:37:45 +0000711 size_t pos = kEscapeChars.find(ch);
David Reissdb0ea152008-02-18 01:49:37 +0000712 if (pos == std::string::npos) {
713 throw TProtocolException(TProtocolException::INVALID_DATA,
714 "Expected control char, got '" +
David Reiss1e62ab42008-02-21 22:37:45 +0000715 std::string((const char *)&ch, 1) + "'.");
David Reissdb0ea152008-02-18 01:49:37 +0000716 }
David Reiss1e62ab42008-02-21 22:37:45 +0000717 ch = kEscapeCharVals[pos];
David Reissdb0ea152008-02-18 01:49:37 +0000718 }
719 }
David Reiss1e62ab42008-02-21 22:37:45 +0000720 str += ch;
David Reissdb0ea152008-02-18 01:49:37 +0000721 }
722 return result;
723}
724
725// Reads a block of base64 characters, decoding it, and returns via str
726uint32_t TJSONProtocol::readJSONBase64(std::string &str) {
727 std::string tmp;
728 uint32_t result = readJSONString(tmp);
729 uint8_t *b = (uint8_t *)tmp.c_str();
Roger Meierb69d24d2012-10-04 18:02:15 +0000730 if(tmp.length() > (std::numeric_limits<uint32_t>::max)())
731 throw TProtocolException(TProtocolException::SIZE_LIMIT);
732 uint32_t len = static_cast<uint32_t>(tmp.length());
David Reiss91630732008-02-28 21:11:39 +0000733 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000734 while (len >= 4) {
735 base64_decode(b, 4);
736 str.append((const char *)b, 3);
737 b += 4;
738 len -= 4;
739 }
740 // Don't decode if we hit the end or got a single leftover byte (invalid
741 // base64 but legal for skip of regular string type)
742 if (len > 1) {
743 base64_decode(b, len);
744 str.append((const char *)b, len - 1);
745 }
746 return result;
747}
748
749// Reads a sequence of characters, stopping at the first one that is not
750// a valid JSON numeric character.
751uint32_t TJSONProtocol::readJSONNumericChars(std::string &str) {
752 uint32_t result = 0;
David Reiss91630732008-02-28 21:11:39 +0000753 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000754 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000755 uint8_t ch = reader_.peek();
David Reissdb0ea152008-02-18 01:49:37 +0000756 if (!isJSONNumeric(ch)) {
757 break;
758 }
David Reiss1e62ab42008-02-21 22:37:45 +0000759 reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000760 str += ch;
761 ++result;
762 }
763 return result;
764}
765
766// Reads a sequence of characters and assembles them into a number,
767// returning them via num
768template <typename NumberType>
769uint32_t TJSONProtocol::readJSONInteger(NumberType &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000770 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000771 if (context_->escapeNum()) {
772 result += readJSONSyntaxChar(kJSONStringDelimiter);
773 }
774 std::string str;
775 result += readJSONNumericChars(str);
776 try {
777 num = boost::lexical_cast<NumberType>(str);
778 }
779 catch (boost::bad_lexical_cast e) {
780 throw new TProtocolException(TProtocolException::INVALID_DATA,
781 "Expected numeric value; got \"" + str +
782 "\"");
783 }
784 if (context_->escapeNum()) {
785 result += readJSONSyntaxChar(kJSONStringDelimiter);
786 }
787 return result;
788}
789
790// Reads a JSON number or string and interprets it as a double.
791uint32_t TJSONProtocol::readJSONDouble(double &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000792 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000793 std::string str;
David Reiss1e62ab42008-02-21 22:37:45 +0000794 if (reader_.peek() == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000795 result += readJSONString(str, true);
796 // Check for NaN, Infinity and -Infinity
797 if (str == kThriftNan) {
798 num = HUGE_VAL/HUGE_VAL; // generates NaN
799 }
800 else if (str == kThriftInfinity) {
801 num = HUGE_VAL;
802 }
803 else if (str == kThriftNegativeInfinity) {
804 num = -HUGE_VAL;
805 }
806 else {
807 if (!context_->escapeNum()) {
808 // Throw exception -- we should not be in a string in this case
809 throw new TProtocolException(TProtocolException::INVALID_DATA,
810 "Numeric data unexpectedly quoted");
811 }
812 try {
813 num = boost::lexical_cast<double>(str);
814 }
815 catch (boost::bad_lexical_cast e) {
816 throw new TProtocolException(TProtocolException::INVALID_DATA,
817 "Expected numeric value; got \"" + str +
818 "\"");
819 }
820 }
821 }
822 else {
823 if (context_->escapeNum()) {
824 // This will throw - we should have had a quote if escapeNum == true
825 readJSONSyntaxChar(kJSONStringDelimiter);
826 }
827 result += readJSONNumericChars(str);
828 try {
829 num = boost::lexical_cast<double>(str);
830 }
831 catch (boost::bad_lexical_cast e) {
832 throw new TProtocolException(TProtocolException::INVALID_DATA,
833 "Expected numeric value; got \"" + str +
834 "\"");
835 }
836 }
837 return result;
838}
839
840uint32_t TJSONProtocol::readJSONObjectStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000841 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000842 result += readJSONSyntaxChar(kJSONObjectStart);
843 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
844 return result;
845}
846
847uint32_t TJSONProtocol::readJSONObjectEnd() {
848 uint32_t result = readJSONSyntaxChar(kJSONObjectEnd);
849 popContext();
850 return result;
851}
852
853uint32_t TJSONProtocol::readJSONArrayStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000854 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000855 result += readJSONSyntaxChar(kJSONArrayStart);
856 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
857 return result;
858}
859
860uint32_t TJSONProtocol::readJSONArrayEnd() {
861 uint32_t result = readJSONSyntaxChar(kJSONArrayEnd);
862 popContext();
863 return result;
864}
865
866uint32_t TJSONProtocol::readMessageBegin(std::string& name,
867 TMessageType& messageType,
868 int32_t& seqid) {
869 uint32_t result = readJSONArrayStart();
David Reissdb0ea152008-02-18 01:49:37 +0000870 uint64_t tmpVal = 0;
871 result += readJSONInteger(tmpVal);
872 if (tmpVal != kThriftVersion1) {
873 throw TProtocolException(TProtocolException::BAD_VERSION,
874 "Message contained bad version.");
875 }
876 result += readJSONString(name);
877 result += readJSONInteger(tmpVal);
878 messageType = (TMessageType)tmpVal;
879 result += readJSONInteger(tmpVal);
Roger Meierb69d24d2012-10-04 18:02:15 +0000880 if(tmpVal > static_cast<uint64_t>((std::numeric_limits<int32_t>::max)()))
881 throw TProtocolException(TProtocolException::SIZE_LIMIT);
882 seqid = static_cast<int32_t>(tmpVal);
David Reissdb0ea152008-02-18 01:49:37 +0000883 return result;
884}
885
886uint32_t TJSONProtocol::readMessageEnd() {
887 return readJSONArrayEnd();
888}
889
890uint32_t TJSONProtocol::readStructBegin(std::string& name) {
Roger Meier3b771a12010-11-17 22:11:26 +0000891 (void) name;
David Reissdb0ea152008-02-18 01:49:37 +0000892 return readJSONObjectStart();
893}
894
895uint32_t TJSONProtocol::readStructEnd() {
896 return readJSONObjectEnd();
897}
898
899uint32_t TJSONProtocol::readFieldBegin(std::string& name,
900 TType& fieldType,
901 int16_t& fieldId) {
Roger Meier3b771a12010-11-17 22:11:26 +0000902 (void) name;
David Reissdb0ea152008-02-18 01:49:37 +0000903 uint32_t result = 0;
David Reiss1e62ab42008-02-21 22:37:45 +0000904 // Check if we hit the end of the list
905 uint8_t ch = reader_.peek();
906 if (ch == kJSONObjectEnd) {
T Jake Lucianib5e62212009-01-31 22:36:20 +0000907 fieldType = apache::thrift::protocol::T_STOP;
David Reissdb0ea152008-02-18 01:49:37 +0000908 }
909 else {
910 uint64_t tmpVal = 0;
911 std::string tmpStr;
912 result += readJSONInteger(tmpVal);
Roger Meierb69d24d2012-10-04 18:02:15 +0000913 if(tmpVal > static_cast<uint32_t>((std::numeric_limits<int16_t>::max)()))
914 throw TProtocolException(TProtocolException::SIZE_LIMIT);
915 fieldId = static_cast<int16_t>(tmpVal);
David Reissdb0ea152008-02-18 01:49:37 +0000916 result += readJSONObjectStart();
917 result += readJSONString(tmpStr);
918 fieldType = getTypeIDForTypeName(tmpStr);
919 }
920 return result;
921}
922
923uint32_t TJSONProtocol::readFieldEnd() {
924 return readJSONObjectEnd();
925}
926
927uint32_t TJSONProtocol::readMapBegin(TType& keyType,
928 TType& valType,
929 uint32_t& size) {
930 uint64_t tmpVal = 0;
931 std::string tmpStr;
932 uint32_t result = readJSONArrayStart();
933 result += readJSONString(tmpStr);
934 keyType = getTypeIDForTypeName(tmpStr);
935 result += readJSONString(tmpStr);
936 valType = getTypeIDForTypeName(tmpStr);
937 result += readJSONInteger(tmpVal);
Roger Meierb69d24d2012-10-04 18:02:15 +0000938 if(tmpVal > (std::numeric_limits<uint32_t>::max)())
939 throw TProtocolException(TProtocolException::SIZE_LIMIT);
940 size = static_cast<uint32_t>(tmpVal);
David Reissdb0ea152008-02-18 01:49:37 +0000941 result += readJSONObjectStart();
942 return result;
943}
944
945uint32_t TJSONProtocol::readMapEnd() {
946 return readJSONObjectEnd() + readJSONArrayEnd();
947}
948
949uint32_t TJSONProtocol::readListBegin(TType& elemType,
950 uint32_t& size) {
951 uint64_t tmpVal = 0;
952 std::string tmpStr;
953 uint32_t result = readJSONArrayStart();
954 result += readJSONString(tmpStr);
955 elemType = getTypeIDForTypeName(tmpStr);
956 result += readJSONInteger(tmpVal);
Roger Meierb69d24d2012-10-04 18:02:15 +0000957 if(tmpVal > (std::numeric_limits<uint32_t>::max)())
958 throw TProtocolException(TProtocolException::SIZE_LIMIT);
959 size = static_cast<uint32_t>(tmpVal);
David Reissdb0ea152008-02-18 01:49:37 +0000960 return result;
961}
962
963uint32_t TJSONProtocol::readListEnd() {
964 return readJSONArrayEnd();
965}
966
967uint32_t TJSONProtocol::readSetBegin(TType& elemType,
968 uint32_t& size) {
969 uint64_t tmpVal = 0;
970 std::string tmpStr;
971 uint32_t result = readJSONArrayStart();
972 result += readJSONString(tmpStr);
973 elemType = getTypeIDForTypeName(tmpStr);
974 result += readJSONInteger(tmpVal);
Roger Meierb69d24d2012-10-04 18:02:15 +0000975 if(tmpVal > (std::numeric_limits<uint32_t>::max)())
976 throw TProtocolException(TProtocolException::SIZE_LIMIT);
977 size = static_cast<uint32_t>(tmpVal);
David Reissdb0ea152008-02-18 01:49:37 +0000978 return result;
979}
980
981uint32_t TJSONProtocol::readSetEnd() {
982 return readJSONArrayEnd();
983}
984
985uint32_t TJSONProtocol::readBool(bool& value) {
986 return readJSONInteger(value);
987}
988
989// readByte() must be handled properly becuase boost::lexical cast sees int8_t
990// as a text type instead of an integer type
991uint32_t TJSONProtocol::readByte(int8_t& byte) {
992 int16_t tmp = (int16_t) byte;
993 uint32_t result = readJSONInteger(tmp);
994 assert(tmp < 256);
995 byte = (int8_t)tmp;
996 return result;
997}
998
999uint32_t TJSONProtocol::readI16(int16_t& i16) {
1000 return readJSONInteger(i16);
1001}
1002
1003uint32_t TJSONProtocol::readI32(int32_t& i32) {
1004 return readJSONInteger(i32);
1005}
1006
1007uint32_t TJSONProtocol::readI64(int64_t& i64) {
1008 return readJSONInteger(i64);
1009}
1010
1011uint32_t TJSONProtocol::readDouble(double& dub) {
1012 return readJSONDouble(dub);
1013}
1014
1015uint32_t TJSONProtocol::readString(std::string &str) {
1016 return readJSONString(str);
1017}
1018
1019uint32_t TJSONProtocol::readBinary(std::string &str) {
1020 return readJSONBase64(str);
1021}
1022
T Jake Lucianib5e62212009-01-31 22:36:20 +00001023}}} // apache::thrift::protocol