From 7338a2503547ae1310d018d0fc2903fb85b309ea Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 9 Mar 2011 23:07:27 +0900 Subject: Added initial JSON-RPC support. JSON-RPC is enabled using --enable-xml-rpc. We are implementing JSON-RPC based on JSON-RPC 2.0 draft spec. --- src/json.cc | 612 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 612 insertions(+) create mode 100644 src/json.cc (limited to 'src/json.cc') diff --git a/src/json.cc b/src/json.cc new file mode 100644 index 00000000..f389d854 --- /dev/null +++ b/src/json.cc @@ -0,0 +1,612 @@ +/* */ +#include "json.h" + +#include + +#include "array_fun.h" +#include "DlAbortEx.h" +#include "error_code.h" +#include "a2functional.h" +#include "util.h" +#include "fmt.h" + +namespace aria2 { + +namespace json { + +// Function prototype declaration +namespace { +std::pair, std::string::const_iterator> +decode +(std::string::const_iterator first, + std::string::const_iterator last, + size_t depth); +} // namespace + +namespace { +const char WS[] = { 0x20, 0x09, 0x0a, 0x0d }; +const char ESCAPE_CHARS[] = { '"', '\\', '/', '\b', '\f', '\n', '\r', '\t' }; +const size_t MAX_STRUCTURE_DEPTH = 100; +} // namespace + +namespace { +std::string::const_iterator skipWs +(std::string::const_iterator first, + std::string::const_iterator last) +{ + while(first != last && std::find(vbegin(WS), vend(WS), *first) != vend(WS)) { + ++first; + } + return first; +} +} // namespace + +namespace { +void checkEof +(std::string::const_iterator first, + std::string::const_iterator last) +{ + if(first == last) { + throw DL_ABORT_EX2("JSON decoding failed: unexpected EOF", + error_code::JSON_PARSE_ERROR); + } +} +} // namespace + +namespace { +std::string::const_iterator +decodeKeyword +(std::string::const_iterator first, + std::string::const_iterator last, + const std::string& keyword) +{ + size_t len = keyword.size(); + for(size_t i = 0; i < len; ++i) { + checkEof(first, last); + if(*first != keyword[i]) { + throw DL_ABORT_EX2(fmt("JSON decoding failed: %s not found.", + keyword.c_str()), + error_code::JSON_PARSE_ERROR); + } + ++first; + } + return first; +} +} // namespace + +namespace { +std::pair, std::string::const_iterator> +decodeTrue +(std::string::const_iterator first, + std::string::const_iterator last) +{ + first = decodeKeyword(first, last, "true"); + return std::make_pair(Bool::gTrue(), first); +} +} // namespace + +namespace { +std::pair, std::string::const_iterator> +decodeFalse +(std::string::const_iterator first, + std::string::const_iterator last) +{ + first = decodeKeyword(first, last, "false"); + return std::make_pair(Bool::gFalse(), first); +} +} // namespace + +namespace { +std::pair, std::string::const_iterator> +decodeNull +(std::string::const_iterator first, + std::string::const_iterator last) +{ + first = decodeKeyword(first, last, "null"); + return std::make_pair(Null::g(), first); +} +} // namespace + +namespace { +std::pair, std::string::const_iterator> +decodeString +(std::string::const_iterator first, + std::string::const_iterator last) +{ + // Consume first char, assuming it is '"'. + ++first; + std::string s; + std::string::const_iterator offset = first; + while(first != last) { + if(*first == '"') { + break; + } + if(*first == '\\') { + s += std::string(offset, first); + ++first; + checkEof(first, last); + if(*first == 'u') { + ++first; + std::string uchars; + for(int i = 0; i < 4; ++i, ++first) { + checkEof(first, last); + uchars += *first; + } + checkEof(first, last); + uint16_t codepoint = util::parseUInt(uchars, 16); + if(codepoint <= 0x007fu) { + s += static_cast(codepoint); + } else if(codepoint <= 0x07ffu) { + unsigned char c2 = 0x80u | (codepoint & 0x003fu); + unsigned char c1 = 0xC0u | (codepoint >> 6); + s += c1; + s += c2; + } else if(in(codepoint, 0xD800u, 0xDBFFu)) { + // surrogate pair + if(*first != '\\' || first+1 == last || + *(first+1) != 'u') { + throw DL_ABORT_EX2("JSON decoding failed: bad UTF-8 sequence.", + error_code::JSON_PARSE_ERROR); + } + first += 2; + std::string uchars; + for(int i = 0; i < 4; ++i, ++first) { + checkEof(first, last); + uchars += *first; + } + checkEof(first, last); + uint16_t codepoint2 = util::parseUInt(uchars, 16); + if(!in(codepoint2, 0xDC00u, 0xDFFFu)) { + throw DL_ABORT_EX2("JSON decoding failed: bad UTF-8 sequence.", + error_code::JSON_PARSE_ERROR); + } + uint32_t fullcodepoint = 0x010000u; + fullcodepoint += (codepoint & 0x03FFu) << 10; + fullcodepoint += (codepoint2 & 0x03FFu); + unsigned char c4 = 0x80u | (fullcodepoint & 0x003Fu); + unsigned char c3 = 0x80u | ((fullcodepoint >> 6) & 0x003Fu); + unsigned char c2 = 0x80u | ((fullcodepoint >> 12) & 0x003Fu); + unsigned char c1 = 0xf0u | (fullcodepoint >> 18); + s += c1; + s += c2; + s += c3; + s += c4; + } else if(codepoint <= 0xffffu) { + unsigned char c3 = 0x80u | (codepoint & 0x003Fu); + unsigned char c2 = 0x80u | ((codepoint >> 6) & 0x003Fu); + unsigned char c1 = 0xE0u | (codepoint >> 12); + s += c1; + s += c2; + s += c3; + } + offset = first; + } else { + if(*first == 'b') { + s += '\b'; + } else if(*first == 'f') { + s += '\f'; + } else if(*first == 'n') { + s += '\n'; + } else if(*first == 'r') { + s += '\r'; + } else if(*first == 't') { + s += '\t'; + } else { + s += *first; + } + ++first; + offset = first; + } + } else { + ++first; + } + } + checkEof(first, last); + if(std::distance(offset, first) > 0) { + s += std::string(offset, first); + } + if(!util::isUtf8(s)) { + throw DL_ABORT_EX2("JSON decoding failed: Non UTF-8 string.", + error_code::JSON_PARSE_ERROR); + } + ++first; + return std::make_pair(String::g(s), first); +} +} // namespace + +namespace { +void checkEmptyDigit +(std::string::const_iterator first, + std::string::const_iterator last) +{ + if(std::distance(first, last) == 0) { + throw DL_ABORT_EX2("JSON decoding failed: zero DIGIT.", + error_code::JSON_PARSE_ERROR); + } +} +} // namespace + +namespace { +void checkLeadingZero +(std::string::const_iterator first, + std::string::const_iterator last) +{ + if(std::distance(first, last) > 2 && *first == '0') { + throw DL_ABORT_EX2("JSON decoding failed: leading zero.", + error_code::JSON_PARSE_ERROR); + } +} +} // namespace + +namespace { +std::pair, std::string::const_iterator> +decodeNumber +(std::string::const_iterator first, + std::string::const_iterator last) +{ + std::string s; + if(*first == '-') { + s += *first; + ++first; + } + std::string::const_iterator offset = first; + while(first != last && in(*first, '0', '9')) { + ++first; + } + checkEof(first, last); + checkEmptyDigit(offset, first); + checkLeadingZero(offset, first); + s += std::string(offset, first); + bool fp = false; + if(*first == '.') { + fp = true; + s += *first; + ++first; + offset = first; + while(first != last && in(*first, '0', '9')) { + ++first; + } + checkEof(first, last); + checkEmptyDigit(offset, first); + s += std::string(offset, first); + } + if(*first == 'e') { + fp = true; + s += *first; + ++first; + checkEof(first, last); + if(*first == '+' || *first == '-') { + s += *first; + ++first; + } + offset = first; + while(first != last && in(*first, '0', '9')) { + ++first; + } + checkEof(first, last); + checkEmptyDigit(offset, first); + s += std::string(offset, first); + } + if(fp) { + // Since we don't have floating point coutner part in ValueBase, + // we just treat it as string. + return std::make_pair(String::g(s), first); + } else { + Integer::ValueType val = util::parseLLInt(s); + return std::make_pair(Integer::g(val), first); + } +} +} // namespace + +namespace { +void checkDepth(size_t depth) +{ + if(depth >= MAX_STRUCTURE_DEPTH) { + throw DL_ABORT_EX2("JSON decoding failed: Structure is too deep.", + error_code::JSON_PARSE_ERROR); + } +} +} // namespace + +namespace { +std::pair, std::string::const_iterator> +decodeArray +(std::string::const_iterator first, + std::string::const_iterator last, + size_t depth) +{ + checkDepth(depth); + SharedHandle list = List::g(); + // Consume first char, assuming it is '['. + ++first; + first = skipWs(first, last); + checkEof(first, last); + if(*first != ']') { + while(1) { + std::pair, std::string::const_iterator> + r = decode(first, last, depth); + list->append(r.first); + first = r.second; + first = skipWs(first, last); + if(first == last || (*first != ',' && *first != ']')) { + throw DL_ABORT_EX2("JSON decoding failed:" + " value-separator ',' or ']' is not found.", + error_code::JSON_PARSE_ERROR); + } + if(*first == ']') { + break; + } + ++first; + } + } + ++first; + return std::make_pair(list, first); +} +} // namespace + +namespace { +std::pair, std::string::const_iterator> +decodeObject +(std::string::const_iterator first, + std::string::const_iterator last, + size_t depth) +{ + checkDepth(depth); + SharedHandle dict = Dict::g(); + // Consume first char, assuming it is '{' + ++first; + first = skipWs(first, last); + checkEof(first, last); + if(*first != '}') { + while(1) { + std::pair, std::string::const_iterator> + keyRet = decodeString(first, last); + first = keyRet.second; + first = skipWs(first, last); + if(first == last || *first != ':') { + throw DL_ABORT_EX2("JSON decoding failed:" + " name-separator ':' is not found.", + error_code::JSON_PARSE_ERROR); + } + ++first; + std::pair, std::string::const_iterator> + valueRet = decode(first, last, depth); + dict->put(asString(keyRet.first)->s(), valueRet.first); + first = valueRet.second; + first = skipWs(first, last); + if(first == last || (*first != ',' && *first != '}')) { + throw DL_ABORT_EX2("JSON decoding failed:" + " value-separator ',' or '}' is not found.", + error_code::JSON_PARSE_ERROR); + } + if(*first == '}') { + break; + } + ++first; + first = skipWs(first, last); + checkEof(first, last); + } + } + ++first; + return std::make_pair(dict, first); +} +} // namespace + +namespace { +std::pair, std::string::const_iterator> +decode +(std::string::const_iterator first, + std::string::const_iterator last, + size_t depth) +{ + first = skipWs(first, last); + if(first == last) { + throw DL_ABORT_EX2("JSON decoding failed:" + " Unexpected EOF in term context.", + error_code::JSON_PARSE_ERROR); + } + if(*first == '[') { + return decodeArray(first, last, depth+1); + } else if(*first == '{') { + return decodeObject(first, last, depth+1); + } else if(*first == '"') { + return decodeString(first, last); + } else if(*first == '-' || in(*first, '0', '9')) { + return decodeNumber(first, last); + } else if(*first == 't') { + return decodeTrue(first, last); + } else if(*first == 'f') { + return decodeFalse(first, last); + } else if(*first == 'n') { + return decodeNull(first, last); + } else { + throw DL_ABORT_EX2("JSON decoding failed:" + " Unexpected EOF in term context.", + error_code::JSON_PARSE_ERROR); + } +} +} // namespace + +SharedHandle decode(const std::string& json) +{ + std::string::const_iterator first = json.begin(); + std::string::const_iterator last = json.end(); + first = skipWs(first, last); + if(first == last) { + throw DL_ABORT_EX2("JSON decoding failed:" + " Unexpected EOF in term context.", + error_code::JSON_PARSE_ERROR); + } + std::pair, std::string::const_iterator> r; + if(*first == '[') { + r = decodeArray(first, last, 1); + } else if(*first == '{') { + r = decodeObject(first, last, 1); + } else { + throw DL_ABORT_EX2("JSON decoding failed:" + " Unexpected EOF in term context.", + error_code::JSON_PARSE_ERROR); + } + return r.first; +} + +std::string jsonEscape(const std::string& s) +{ + std::string t; + for(std::string::const_iterator i = s.begin(), eoi = s.end(); i != eoi; + ++i) { + if(*i == '"' || *i == '\\' || *i == '/') { + t += '\\'; + t += *i; + } else if(*i == '\b') { + t += "\\b"; + } else if(*i == '\f') { + t += "\\f"; + } else if(*i == '\n') { + t += "\\n"; + } else if(*i == '\r') { + t += "\\r"; + } else if(*i == '\t') { + t += "\\t"; + } else if(in(static_cast(*i), 0x00u, 0x1Fu)) { + t += "\\u00"; + char temp[3]; + temp[2] = '\0'; + temp[0] = (*i >> 4); + temp[1] = (*i)&0x0Fu; + for(int j = 0; j < 2; ++j) { + if(temp[j] < 10) { + temp[j] += '0'; + } else { + temp[j] += 'A'-10; + } + } + t += temp; + } else { + t += *i; + } + } + return t; +} + +std::string encode(const ValueBase* vlb) +{ + class JsonValueBaseVisitor:public ValueBaseVisitor { + private: + std::ostringstream out_; + public: + virtual void visit(const String& string) + { + const std::string& s = string.s(); + std::string t = jsonEscape(s); + out_ << '"'; + out_.write(t.data(), t.size()); + out_ << '"'; + } + + virtual void visit(const Integer& integer) + { + out_ << integer.i(); + } + + virtual void visit(const Bool& boolValue) + { + out_ << (boolValue.val() ? "true" : "false"); + } + + virtual void visit(const Null& nullValue) + { + out_ << "null"; + } + + virtual void visit(const List& list) + { + out_ << '['; + List::ValueType::const_iterator i = list.begin(); + if(!list.empty()) { + (*i)->accept(*this); + } + ++i; + for(List::ValueType::const_iterator eoi = list.end(); i != eoi; ++i){ + out_ << ','; + (*i)->accept(*this); + } + out_ << ']'; + } + + virtual void visit(const Dict& dict) + { + out_ << '{'; + Dict::ValueType::const_iterator i = dict.begin(); + if(!dict.empty()) { + std::string key = jsonEscape((*i).first); + out_ << '"'; + out_.write(key.data(), key.size()); + out_ << "\":"; + (*i).second->accept(*this); + } + ++i; + for(Dict::ValueType::const_iterator eoi = dict.end(); i != eoi; ++i){ + out_ << ','; + std::string key = jsonEscape((*i).first); + out_ << '"'; + out_.write(key.data(), key.size()); + out_ << "\":"; + (*i).second->accept(*this); + } + out_ << '}'; + } + + std::string getResult() const + { + return out_.str(); + } + }; + JsonValueBaseVisitor visitor; + vlb->accept(visitor); + return visitor.getResult(); +} + +// Serializes JSON object or array. +std::string encode(const SharedHandle& json) +{ + std::ostringstream out; + return encode(out, json.get()).str(); +} + +} // namespace json + +} // namespace aria2 -- cgit