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