1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <thrift/lib/cpp2/protocol/JSONProtocolCommon.h>
18 
19 #include <type_traits>
20 
21 #include <fmt/core.h>
22 
23 namespace {
24 
25 class WrappedIOBufQueueAppender {
26  public:
WrappedIOBufQueueAppender(folly::io::QueueAppender & out)27   explicit WrappedIOBufQueueAppender(folly::io::QueueAppender& out)
28       : out_(out) {}
29 
append(const char * s,const size_t n)30   void append(const char* s, const size_t n) {
31     if (n == 0)
32       return;
33     out_.push(reinterpret_cast<const uint8_t*>(CHECK_NOTNULL(s)), n);
34     length_ += n;
35   }
36 
push_back(const char c)37   void push_back(const char c) { append(&c, 1); }
38 
operator +=(const char c)39   WrappedIOBufQueueAppender& operator+=(const char c) {
40     push_back(c);
41     return *this;
42   }
43 
size() const44   size_t size() const { return length_; }
45 
46  private:
47   folly::io::QueueAppender& out_;
48   size_t length_ = 0;
49 };
50 
51 } // namespace
52 
53 namespace folly {
54 
55 template <>
56 struct IsSomeString<WrappedIOBufQueueAppender> : std::true_type {};
57 
58 } // namespace folly
59 
60 namespace apache {
61 namespace thrift {
62 
63 // This table describes the handling for the first 0x30 characters
64 //  0 : escape using "\u00xx" notation
65 //  1 : just output index
66 // <other> : escape using "\<other>" notation
67 const uint8_t JSONProtocolWriterCommon::kJSONCharTable[0x30] = {
68     //  0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
69     0, 0, 0,   0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0, // 0
70     0, 0, 0,   0, 0, 0, 0, 0, 0,   0,   0,   0, 0,   0,   0, 0, // 1
71     1, 1, '"', 1, 1, 1, 1, 1, 1,   1,   1,   1, 1,   1,   1, 1, // 2
72 };
73 
74 // The elements of this array must match up with the sequence of characters in
75 // kEscapeChars
76 const uint8_t JSONProtocolReaderCommon::kEscapeCharVals[8] = {
77     '"',
78     '\\',
79     '/',
80     '\b',
81     '\f',
82     '\n',
83     '\r',
84     '\t',
85 };
86 
writeJSONDoubleInternal(double dbl)87 uint32_t JSONProtocolWriterCommon::writeJSONDoubleInternal(double dbl) {
88   WrappedIOBufQueueAppender appender(out_);
89   folly::toAppend(dbl, &appender);
90   return appender.size();
91 }
92 
writeJSONDoubleInternal(float flt)93 uint32_t JSONProtocolWriterCommon::writeJSONDoubleInternal(float flt) {
94   WrappedIOBufQueueAppender appender(out_);
95   folly::toAppend(
96       flt,
97       &appender,
98       double_conversion::DoubleToStringConverter::SHORTEST_SINGLE,
99       0);
100   return appender.size();
101 }
102 
writeJSONIntInternal(int64_t num)103 uint32_t JSONProtocolWriterCommon::writeJSONIntInternal(int64_t num) {
104   WrappedIOBufQueueAppender appender(out_);
105   if (!context.empty() && context.back().type == ContextType::MAP &&
106       context.back().meta % 2 == 1) {
107     folly::toAppend('"', num, '"', &appender);
108   } else {
109     folly::toAppend(num, &appender);
110   }
111   return appender.size();
112 }
113 
sp(char const & ch)114 static inline folly::StringPiece sp(char const& ch) {
115   return {&ch, 1};
116 }
117 
throwBadVersion()118 [[noreturn]] void JSONProtocolReaderCommon::throwBadVersion() {
119   throw TProtocolException(
120       TProtocolException::BAD_VERSION, "Message contained bad version.");
121 }
122 
throwUnrecognizableAsBoolean(std::string const & s)123 [[noreturn]] void JSONProtocolReaderCommon::throwUnrecognizableAsBoolean(
124     std::string const& s) {
125   throw TProtocolException(
126       TProtocolException::INVALID_DATA, s + " is not a valid bool");
127 }
128 
throwUnrecognizableAsIntegral(folly::StringPiece s,folly::StringPiece typeName)129 [[noreturn]] void JSONProtocolReaderCommon::throwUnrecognizableAsIntegral(
130     folly::StringPiece s, folly::StringPiece typeName) {
131   throw TProtocolException(
132       TProtocolException::INVALID_DATA,
133       folly::to<std::string>(s, " is not a valid ", typeName));
134 }
135 
throwUnrecognizableAsFloatingPoint(std::string const & s)136 [[noreturn]] void JSONProtocolReaderCommon::throwUnrecognizableAsFloatingPoint(
137     std::string const& s) {
138   throw TProtocolException(
139       TProtocolException::INVALID_DATA, s + " is not a valid float/double");
140 }
141 
throwUnrecognizableAsString(std::string const & s,std::exception const & e)142 [[noreturn]] void JSONProtocolReaderCommon::throwUnrecognizableAsString(
143     std::string const& s, std::exception const& e) {
144   throw TProtocolException(
145       TProtocolException::INVALID_DATA,
146       s + " is not a valid JSON string: " + e.what());
147 }
148 
throwUnrecognizableAsAny(std::string const & s)149 [[noreturn]] void JSONProtocolReaderCommon::throwUnrecognizableAsAny(
150     std::string const& s) {
151   throw TProtocolException(
152       TProtocolException::INVALID_DATA, s + " is not valid JSON");
153 }
154 
throwInvalidFieldStart(char const ch)155 [[noreturn]] void JSONProtocolReaderCommon::throwInvalidFieldStart(
156     char const ch) {
157   throw TProtocolException(
158       TProtocolException::INVALID_DATA,
159       std::string(1, ch) + " is not a valid start to a JSON field");
160 }
161 
throwUnexpectedChar(char const ch,char const expected)162 [[noreturn]] void JSONProtocolReaderCommon::throwUnexpectedChar(
163     char const ch, char const expected) {
164   auto const msg = fmt::format(
165       "expected '{0}' (hex {0:#02x}), read '{1}' (hex {1:#02x})", expected, ch);
166   throw TProtocolException(TProtocolException::INVALID_DATA, msg);
167 }
168 
throwInvalidEscapeChar(char const ch)169 [[noreturn]] void JSONProtocolReaderCommon::throwInvalidEscapeChar(
170     char const ch) {
171   throw TProtocolException(
172       TProtocolException::INVALID_DATA,
173       folly::to<std::string>("Expected control char, got '", sp(ch), "'."));
174 }
175 
throwInvalidHexChar(char const ch)176 [[noreturn]] void JSONProtocolReaderCommon::throwInvalidHexChar(char const ch) {
177   throw TProtocolException(
178       TProtocolException::INVALID_DATA,
179       folly::to<std::string>(
180           "Expected hex val ([0-9a-f]); got \'", sp(ch), "\'."));
181 }
182 } // namespace thrift
183 } // namespace apache
184