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.
pull/1/head
Tatsuhiro Tsujikawa 2011-03-09 23:07:27 +09:00
parent d541b350d2
commit 7338a25035
21 changed files with 1528 additions and 42 deletions

View File

@ -143,4 +143,10 @@ GZipEncoder& GZipEncoder::operator<<(int64_t i)
return *this;
}
GZipEncoder& GZipEncoder::write(const char* s, size_t length)
{
internalBuf_ += encode(reinterpret_cast<const unsigned char*>(s), length);
return *this;
}
} // namespace aria2

View File

@ -68,6 +68,10 @@ public:
// in this class.
GZipEncoder& operator<<(const std::string& s);
// Feeds binary data in s with size length to deflater. The
// deflated result is kept in this class.
GZipEncoder& write(const char* s, size_t length);
// Feeds integer to deflator. Before passed to deflator, i is
// converted to std::string using util::itos(). The deflated result
// is kept in this class.

View File

@ -54,6 +54,8 @@
#include "util.h"
#include "fmt.h"
#include "SocketRecvBuffer.h"
#include "json.h"
#include "DlAbortEx.h"
namespace aria2 {
@ -92,8 +94,16 @@ bool HttpServerBodyCommand::execute()
timeoutTimer_ = global::wallclock;
if(httpServer_->receiveBody()) {
A2_LOG_DEBUG(fmt("%s", httpServer_->getBody().c_str()));
std::string reqPath = httpServer_->getRequestPath();
reqPath.erase(std::find(reqPath.begin(), reqPath.end(), '#'),
reqPath.end());
std::string query(std::find(reqPath.begin(), reqPath.end(), '?'),
reqPath.end());
reqPath.erase(reqPath.size()-query.size(), query.size());
// Do something for requestpath and body
if(httpServer_->getRequestPath() == "/rpc") {
if(reqPath == "/rpc") {
xmlrpc::XmlRpcRequest req =
xmlrpc::XmlRpcRequestProcessor().parseMemory(httpServer_->getBody());
@ -108,6 +118,50 @@ bool HttpServerBodyCommand::execute()
e_->addCommand(command);
e_->setNoWait(true);
return true;
} else if(reqPath == "/jsonrpc") {
// TODO handle query parameter
std::string callback;// = "callback";
SharedHandle<ValueBase> json = json::decode(httpServer_->getBody());
const Dict* jsondict = asDict(json);
if(!jsondict) {
// TODO code: -32600, Invalid Request
throw DL_ABORT_EX("JSON-RPC Invalid Request");
}
const String* methodName = asString(jsondict->get("method"));
if(!methodName) {
// TODO Batch request does not have method
throw DL_ABORT_EX("JSON-RPC No Method Found");
}
SharedHandle<List> params;
const SharedHandle<ValueBase>& tempParams = jsondict->get("params");
if(asList(tempParams)) {
params = static_pointer_cast<List>(tempParams);
} else if(!tempParams) {
params = List::g();
} else {
// TODO No support for Named params
throw DL_ABORT_EX("JSON-RPC Named params are not supported");
}
SharedHandle<ValueBase> id = jsondict->get("id");
if(!id) {
// TODO Batch request does not have id
throw DL_ABORT_EX("JSON-RPC NO id found");
}
xmlrpc::XmlRpcRequest req(methodName->s(), params, id);
SharedHandle<xmlrpc::XmlRpcMethod> method =
xmlrpc::XmlRpcMethodFactory::create(req.methodName);
method->setJsonRpc(true);
xmlrpc::XmlRpcResponse res = method->execute(req, e_);
bool gzip = httpServer_->supportsGZip();
std::string responseData = res.toJson(callback, gzip);
httpServer_->feedResponse(responseData, "application/json-rpc");
Command* command =
new HttpServerResponseCommand(getCuid(), httpServer_, e_, socket_);
e_->addCommand(command);
e_->setNoWait(true);
return true;
} else {
return true;
}

View File

@ -210,7 +210,8 @@ SRCS = Socket.h\
NullSinkStreamFilter.cc NullSinkStreamFilter.h\
uri.cc uri.h\
Triplet.h\
cookie_helper.cc cookie_helper.h
cookie_helper.cc cookie_helper.h\
json.cc json.h
if ENABLE_XML_RPC
SRCS += XmlRpcRequestParserController.cc XmlRpcRequestParserController.h\

View File

@ -97,6 +97,45 @@ void Integer::accept(ValueBaseVisitor& v) const
v.visit(*this);
}
const SharedHandle<Bool> Bool::trueValue_(new Bool(true));
const SharedHandle<Bool> Bool::falseValue_(new Bool(false));
SharedHandle<Bool> Bool::gTrue()
{
return trueValue_;
}
SharedHandle<Bool> Bool::gFalse()
{
return falseValue_;
}
bool Bool::val() const
{
return val_;
}
void Bool::accept(ValueBaseVisitor& v) const
{
v.visit(*this);
}
Bool::Bool(bool val):val_(val) {}
const SharedHandle<Null> Null::nullValue_(new Null());
SharedHandle<Null> Null::g()
{
return nullValue_;
}
void Null::accept(ValueBaseVisitor& v) const
{
v.visit(*this);
}
Null::Null() {}
List::List() {}
List::~List() {}
@ -262,7 +301,7 @@ void Dict::accept(ValueBaseVisitor& v) const
const String* asString(const ValueBase* v)
{
if(v) {
return downcast<String, Integer, List, Dict>(v);
return downcast<String>(v);
} else {
return 0;
}
@ -271,7 +310,7 @@ const String* asString(const ValueBase* v)
String* asString(ValueBase* v)
{
if(v) {
return const_cast<String*>(downcast<String, Integer, List, Dict>(v));
return const_cast<String*>(downcast<String>(v));
} else {
return 0;
}
@ -280,7 +319,7 @@ String* asString(ValueBase* v)
String* asString(const SharedHandle<ValueBase>& v)
{
if(v.get()) {
return const_cast<String*>(downcast<String, Integer, List, Dict>(v));
return const_cast<String*>(downcast<String>(v));
} else {
return 0;
}
@ -289,7 +328,7 @@ String* asString(const SharedHandle<ValueBase>& v)
const Integer* asInteger(const ValueBase* v)
{
if(v) {
return downcast<Integer, String, List, Dict>(v);
return downcast<Integer>(v);
} else {
return 0;
}
@ -298,7 +337,7 @@ const Integer* asInteger(const ValueBase* v)
Integer* asInteger(ValueBase* v)
{
if(v) {
return const_cast<Integer*>(downcast<Integer, String, List, Dict>(v));
return const_cast<Integer*>(downcast<Integer>(v));
} else {
return 0;
}
@ -307,7 +346,43 @@ Integer* asInteger(ValueBase* v)
Integer* asInteger(const SharedHandle<ValueBase>& v)
{
if(v.get()) {
return const_cast<Integer*>(downcast<Integer, String, List, Dict>(v));
return const_cast<Integer*>(downcast<Integer>(v));
} else {
return 0;
}
}
const Bool* asBool(const ValueBase* v)
{
if(v) {
return downcast<Bool>(v);
} else {
return 0;
}
}
Bool* asBool(const SharedHandle<ValueBase>& v)
{
if(v.get()) {
return const_cast<Bool*>(downcast<Bool>(v));
} else {
return 0;
}
}
const Null* asNull(const ValueBase* v)
{
if(v) {
return downcast<Null>(v);
} else {
return 0;
}
}
Null* asNull(const SharedHandle<ValueBase>& v)
{
if(v) {
return const_cast<Null*>(downcast<Null>(v));
} else {
return 0;
}
@ -316,7 +391,7 @@ Integer* asInteger(const SharedHandle<ValueBase>& v)
const List* asList(const ValueBase* v)
{
if(v) {
return downcast<List, String, Integer, Dict>(v);
return downcast<List>(v);
} else {
return 0;
}
@ -325,7 +400,7 @@ const List* asList(const ValueBase* v)
List* asList(ValueBase* v)
{
if(v) {
return const_cast<List*>(downcast<List, String, Integer, Dict>(v));
return const_cast<List*>(downcast<List>(v));
} else {
return 0;
}
@ -334,7 +409,7 @@ List* asList(ValueBase* v)
List* asList(const SharedHandle<ValueBase>& v)
{
if(v.get()) {
return const_cast<List*>(downcast<List, String, Integer, Dict>(v));
return const_cast<List*>(downcast<List>(v));
} else {
return 0;
}
@ -343,7 +418,7 @@ List* asList(const SharedHandle<ValueBase>& v)
const Dict* asDict(const ValueBase* v)
{
if(v) {
return downcast<Dict, String, Integer, List>(v);
return downcast<Dict>(v);
} else {
return 0;
}
@ -352,7 +427,7 @@ const Dict* asDict(const ValueBase* v)
Dict* asDict(ValueBase* v)
{
if(v) {
return const_cast<Dict*>(downcast<Dict, String, Integer, List>(v));
return const_cast<Dict*>(downcast<Dict>(v));
} else {
return 0;
}
@ -361,7 +436,7 @@ Dict* asDict(ValueBase* v)
Dict* asDict(const SharedHandle<ValueBase>& v)
{
if(v.get()) {
return const_cast<Dict*>(downcast<Dict, String, Integer, List>(v));
return const_cast<Dict*>(downcast<Dict>(v));
} else {
return 0;
}

View File

@ -58,19 +58,19 @@ public:
class String;
class Integer;
class Bool;
class Null;
class List;
class Dict;
class ValueBaseVisitor {
public:
virtual ~ValueBaseVisitor() {}
virtual void visit(const String& string) = 0;
virtual void visit(const Integer& integer) = 0;
virtual void visit(const Bool& boolValue) = 0;
virtual void visit(const Null& nullValue) = 0;
virtual void visit(const List& list) = 0;
virtual void visit(const Dict& dict) = 0;
};
@ -133,6 +133,34 @@ private:
ValueType integer_;
};
class Bool:public ValueBase {
public:
static SharedHandle<Bool> gTrue();
static SharedHandle<Bool> gFalse();
bool val() const;
virtual void accept(ValueBaseVisitor& visitor) const;
private:
Bool(bool val);
// Don't allow copying
Bool(const Bool&);
Bool& operator=(const Bool&);
bool val_;
static const SharedHandle<Bool> trueValue_;
static const SharedHandle<Bool> falseValue_;
};
class Null:public ValueBase {
public:
static SharedHandle<Null> g();
virtual void accept(ValueBaseVisitor& visitor) const;
private:
Null();
// Don't allow copying
Null(const Null&);
Null& operator=(const Null&);
static const SharedHandle<Null> nullValue_;
};
class List:public ValueBase {
public:
typedef std::vector<SharedHandle<ValueBase> > ValueType;
@ -256,10 +284,19 @@ private:
ValueType dict_;
};
template<typename T, typename T1, typename T2, typename T3>
class DowncastValueBaseVisitor:public ValueBaseVisitor {
private:
const T* result_;
class EmptyDowncastValueBaseVisitor:public ValueBaseVisitor {
public:
EmptyDowncastValueBaseVisitor() {}
virtual void visit(const String& v) {}
virtual void visit(const Integer& v) {}
virtual void visit(const Bool& v) {}
virtual void visit(const Null& v) {}
virtual void visit(const List& v) {}
virtual void visit(const Dict& v) {}
};
template<typename T>
class DowncastValueBaseVisitor:public EmptyDowncastValueBaseVisitor {
public:
DowncastValueBaseVisitor():result_(0) {}
@ -268,10 +305,6 @@ public:
result_ = &t;
}
virtual void visit(const T1& t1) {}
virtual void visit(const T2& t2) {}
virtual void visit(const T3& t3) {}
const T* getResult() const
{
return result_;
@ -281,12 +314,14 @@ public:
{
result_ = r;
}
private:
const T* result_;
};
template<typename T, typename T1, typename T2, typename T3, typename VPtr>
template<typename T, typename VPtr>
const T* downcast(const VPtr& v)
{
DowncastValueBaseVisitor<T, T1, T2, T3> visitor;
DowncastValueBaseVisitor<T> visitor;
v->accept(visitor);
return visitor.getResult();
}
@ -303,6 +338,14 @@ Integer* asInteger(ValueBase* v);
Integer* asInteger(const SharedHandle<ValueBase>& v);
const Bool* asBool(const ValueBase* v);
Bool* asBool(const SharedHandle<ValueBase>& v);
const Null* asNull(const ValueBase* v);
Null* asNull(const SharedHandle<ValueBase>& v);
const List* asList(const ValueBase* v);
List* asList(ValueBase* v);

View File

@ -53,7 +53,8 @@ namespace aria2 {
namespace xmlrpc {
XmlRpcMethod::XmlRpcMethod()
: optionParser_(OptionParser::getInstance())
: optionParser_(OptionParser::getInstance()),
jsonRpc_(false)
{}
XmlRpcMethod::~XmlRpcMethod() {}
@ -62,8 +63,8 @@ SharedHandle<ValueBase> XmlRpcMethod::createErrorResponse
(const Exception& e)
{
SharedHandle<Dict> params = Dict::g();
params->put("faultCode", Integer::g(1));
params->put("faultString", std::string(e.what()));
params->put((jsonRpc_ ? "code" : "faultCode"), Integer::g(1));
params->put((jsonRpc_ ? "message" : "faultString"), std::string(e.what()));
return params;
}
@ -71,10 +72,10 @@ XmlRpcResponse XmlRpcMethod::execute
(const XmlRpcRequest& req, DownloadEngine* e)
{
try {
return XmlRpcResponse(0, process(req, e));
return XmlRpcResponse(0, process(req, e), req.id);
} catch(RecoverableException& e) {
A2_LOG_DEBUG_EX(EX_EXCEPTION_CAUGHT, e);
return XmlRpcResponse(1, createErrorResponse(e));
return XmlRpcResponse(1, createErrorResponse(e), req.id);
}
}

View File

@ -64,6 +64,7 @@ struct XmlRpcResponse;
class XmlRpcMethod {
private:
SharedHandle<OptionParser> optionParser_;
bool jsonRpc_;
protected:
// Subclass must implement this function to fulfil XmlRpcRequest
// req. The return value of this method is used as a return value
@ -102,6 +103,15 @@ public:
// Do work to fulfill XmlRpcRequest req and returns its result as
// XmlRpcResponse. This method delegates to process() method.
XmlRpcResponse execute(const XmlRpcRequest& req, DownloadEngine* e);
// Set whether JSON-RPC style parameter handling.
void setJsonRpc(bool f)
{
jsonRpc_ = f;
}
bool getJsonRpc() const
{
return jsonRpc_;
}
};
} // namespace xmlrpc

View File

@ -63,6 +63,7 @@
#include "SegmentMan.h"
#include "TimedHaltCommand.h"
#include "PeerStat.h"
#include "Base64.h"
#ifdef ENABLE_BITTORRENT
# include "bittorrent_helper.h"
# include "BtRegistry.h"
@ -266,7 +267,11 @@ SharedHandle<ValueBase> AddTorrentXmlRpcMethod::process
if(!torrentParam) {
throw DL_ABORT_EX("Torrent data is not provided.");
}
SharedHandle<String> tempTorrentParam;
if(getJsonRpc()) {
tempTorrentParam = String::g(Base64::decode(torrentParam->s()));
torrentParam = tempTorrentParam.get();
}
std::vector<std::string> uris;
extractUris(std::back_inserter(uris), req.getListParam(1));
@ -309,7 +314,11 @@ SharedHandle<ValueBase> AddMetalinkXmlRpcMethod::process
if(!metalinkParam) {
throw DL_ABORT_EX("Metalink data is not provided.");
}
SharedHandle<String> tempMetalinkParam;
if(getJsonRpc()) {
tempMetalinkParam = String::g(Base64::decode(metalinkParam->s()));
metalinkParam = tempMetalinkParam.get();
}
SharedHandle<Option> requestOption(new Option(*e->getOption()));
gatherRequestOption(requestOption, req.getDictParam(1));

View File

@ -43,8 +43,14 @@ XmlRpcRequest::XmlRpcRequest(const std::string& methodName,
: methodName(methodName), params(params)
{}
XmlRpcRequest::XmlRpcRequest(const std::string& methodName,
const SharedHandle<List>& params,
const SharedHandle<ValueBase>& id)
: methodName(methodName), params(params), id(id)
{}
XmlRpcRequest::XmlRpcRequest(const XmlRpcRequest& c)
: methodName(c.methodName), params(c.params)
: methodName(c.methodName), params(c.params), id(c.id)
{}
XmlRpcRequest::~XmlRpcRequest() {}

View File

@ -48,10 +48,15 @@ namespace xmlrpc {
struct XmlRpcRequest {
std::string methodName;
SharedHandle<List> params;
SharedHandle<ValueBase> id;
XmlRpcRequest(const std::string& methodName,
const SharedHandle<List>& params);
XmlRpcRequest(const std::string& methodName,
const SharedHandle<List>& params,
const SharedHandle<ValueBase>& id);
~XmlRpcRequest();
XmlRpcRequest(const XmlRpcRequest& c);

View File

@ -38,6 +38,7 @@
#include <sstream>
#include "util.h"
#include "json.h"
#ifdef HAVE_ZLIB
# include "GZipEncoder.h"
#endif // HAVE_ZLIB
@ -68,6 +69,10 @@ void encodeValue(const SharedHandle<ValueBase>& value, OutputStream& o)
o_ << "<value><int>" << v.i() << "</int></value>";
}
virtual void visit(const Bool& boolValue) {}
virtual void visit(const Null& nullValue) {}
virtual void visit(const List& v)
{
o_ << "<value><array><data>";
@ -117,13 +122,16 @@ std::string encodeAll
} // namespace
XmlRpcResponse::XmlRpcResponse
(int code, const SharedHandle<ValueBase>& param)
: code(code), param(param)
(int code,
const SharedHandle<ValueBase>& param,
const SharedHandle<ValueBase>& id)
: code(code), param(param), id(id)
{}
XmlRpcResponse::XmlRpcResponse(const XmlRpcResponse& c)
: code(c.code),
param(c.param)
param(c.param),
id(c.id)
{}
XmlRpcResponse::~XmlRpcResponse() {}
@ -153,6 +161,53 @@ std::string XmlRpcResponse::toXml(bool gzip) const
}
}
namespace {
template<typename OutputStream>
OutputStream& encodeJsonAll
(OutputStream& o,
int code,
const SharedHandle<ValueBase>& param,
const SharedHandle<ValueBase>& id,
const std::string& callback)
{
if(!callback.empty()) {
o << callback << '(';
}
SharedHandle<Dict> dict = Dict::g();
dict->put("jsonrpc", "2.0");
// TODO id may be null?
if(id) {
dict->put("id", id);
}
if(code == 0) {
dict->put("result", param);
} else {
dict->put("error", param);
}
json::encode(o, dict).str();
if(!callback.empty()) {
o << ')';
}
return o;
}
} // namespace
std::string XmlRpcResponse::toJson(const std::string& callback, bool gzip) const
{
if(gzip) {
#ifdef HAVE_ZLIB
GZipEncoder o;
o.init();
return encodeJsonAll(o, code, param, id, callback).str();
#else // !HAVE_ZLIB
abort();
#endif // !HAVE_ZLIB
} else {
std::stringstream o;
return encodeJsonAll(o, code, param, id, callback).str();
}
}
} // namespace xmlrpc
} // namespace aria2

View File

@ -51,7 +51,12 @@ struct XmlRpcResponse {
SharedHandle<ValueBase> param;
XmlRpcResponse(int code, const SharedHandle<ValueBase>& param);
SharedHandle<ValueBase> id;
XmlRpcResponse
(int code,
const SharedHandle<ValueBase>& param,
const SharedHandle<ValueBase>& id);
XmlRpcResponse(const XmlRpcResponse& c);
@ -60,6 +65,10 @@ struct XmlRpcResponse {
XmlRpcResponse& operator=(const XmlRpcResponse& c);
std::string toXml(bool gzip = false) const;
// Encodes RPC response in JSON. If callback is not empty, the
// resulting string is JSONP.
std::string toJson(const std::string& callback, bool gzip = false) const;
};
} // namespace xmlrpc

View File

@ -249,6 +249,9 @@ std::string encode(const ValueBase* vlb)
out_ << "i" << integer.i() << "e";
}
virtual void visit(const Bool& v) {}
virtual void visit(const Null& v) {}
virtual void visit(const List& list)
{
out_ << "l";

View File

@ -150,6 +150,8 @@ void extractUrlList
}
virtual void visit(const Integer& v) {}
virtual void visit(const Bool& v) {}
virtual void visit(const Null& v) {}
virtual void visit(const List& v)
{

View File

@ -283,6 +283,8 @@ void extractPeer(const ValueBase* peerData, int family, OutputIterator dest)
}
virtual void visit(const Integer& v) {}
virtual void visit(const Bool& v) {}
virtual void visit(const Null& v) {}
virtual void visit(const List& peerData)
{

View File

@ -72,7 +72,8 @@ enum Value {
BITTORRENT_PARSE_ERROR = 26,
MAGNET_PARSE_ERROR = 27,
OPTION_ERROR = 28,
HTTP_SERVICE_UNAVAILABLE = 29
HTTP_SERVICE_UNAVAILABLE = 29,
JSON_PARSE_ERROR = 30
};
} // namespace error_code

612
src/json.cc Normal file
View File

@ -0,0 +1,612 @@
/* <!-- copyright */
/*
* aria2 - The high speed download utility
*
* Copyright (C) 2011 Tatsuhiro Tsujikawa
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
* You must obey the GNU General Public License in all respects
* for all of the code used other than OpenSSL. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you
* do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source
* files in the program, then also delete it here.
*/
/* copyright --> */
#include "json.h"
#include <sstream>
#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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, 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<char>(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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, std::string::const_iterator>
decodeArray
(std::string::const_iterator first,
std::string::const_iterator last,
size_t depth)
{
checkDepth(depth);
SharedHandle<List> list = List::g();
// Consume first char, assuming it is '['.
++first;
first = skipWs(first, last);
checkEof(first, last);
if(*first != ']') {
while(1) {
std::pair<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, std::string::const_iterator>
decodeObject
(std::string::const_iterator first,
std::string::const_iterator last,
size_t depth)
{
checkDepth(depth);
SharedHandle<Dict> dict = Dict::g();
// Consume first char, assuming it is '{'
++first;
first = skipWs(first, last);
checkEof(first, last);
if(*first != '}') {
while(1) {
std::pair<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, 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<SharedHandle<ValueBase>, 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<ValueBase> 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<SharedHandle<ValueBase>, 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<unsigned char>(*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<ValueBase>& json)
{
std::ostringstream out;
return encode(out, json.get()).str();
}
} // namespace json
} // namespace aria2

141
src/json.h Normal file
View File

@ -0,0 +1,141 @@
/* <!-- copyright */
/*
* aria2 - The high speed download utility
*
* Copyright (C) 2011 Tatsuhiro Tsujikawa
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the
* OpenSSL library under certain conditions as described in each
* individual source file, and distribute linked combinations
* including the two.
* You must obey the GNU General Public License in all respects
* for all of the code used other than OpenSSL. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you
* do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source
* files in the program, then also delete it here.
*/
/* copyright --> */
#ifndef D_JSON_H
#define D_JSON_H
#include "common.h"
#include "ValueBase.h"
namespace aria2 {
namespace json {
// Parses JSON text defined in RFC4627.
SharedHandle<ValueBase> decode(const std::string& json);
std::string jsonEscape(const std::string& s);
template<typename OutputStream>
OutputStream& encode(OutputStream& out, const ValueBase* vlb)
{
class JsonValueBaseVisitor:public ValueBaseVisitor {
public:
JsonValueBaseVisitor(OutputStream& out):out_(out) {}
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_ << '}';
}
private:
OutputStream& out_;
};
JsonValueBaseVisitor visitor(out);
vlb->accept(visitor);
return out;
}
template<typename OutputStream>
OutputStream& encode(OutputStream& out, const SharedHandle<ValueBase>& vlb)
{
return encode(out, vlb.get());
}
// Serializes JSON object or array.
std::string encode(const ValueBase* json);
std::string encode(const SharedHandle<ValueBase>& json);
} // namespace json
} // namespace aria2
#endif // D_JSON_H

446
test/JsonTest.cc Normal file
View File

@ -0,0 +1,446 @@
#include "json.h"
#include <cppunit/extensions/HelperMacros.h>
#include "RecoverableException.h"
#include "util.h"
#include "array_fun.h"
namespace aria2 {
class JsonTest:public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(JsonTest);
CPPUNIT_TEST(testDecode);
CPPUNIT_TEST(testDecode_error);
CPPUNIT_TEST(testEncode);
CPPUNIT_TEST_SUITE_END();
private:
public:
void testDecode();
void testDecode_error();
void testEncode();
};
CPPUNIT_TEST_SUITE_REGISTRATION( JsonTest );
void JsonTest::testDecode()
{
{
// empty object
SharedHandle<ValueBase> r = json::decode("{}");
const Dict* dict = asDict(r);
CPPUNIT_ASSERT(dict);
}
{
// empty object
SharedHandle<ValueBase> r = json::decode("{ }");
const Dict* dict = asDict(r);
CPPUNIT_ASSERT(dict);
}
{
// empty array
SharedHandle<ValueBase> r = json::decode("[]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
}
{
// empty array
SharedHandle<ValueBase> r = json::decode("[ ]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
}
{
// empty string
SharedHandle<ValueBase> r = json::decode("[\"\"]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
const String* s = asString(list->get(0));
CPPUNIT_ASSERT_EQUAL(std::string(), s->s());
}
{
// string
SharedHandle<ValueBase> r = json::decode("[\"foobar\"]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
const String* s = asString(list->get(0));
CPPUNIT_ASSERT_EQUAL(std::string("foobar"), s->s());
}
{
// string with escape
SharedHandle<ValueBase> r = json::decode("[\"\\\\foo\\\"\\\"bar\"]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
const String* s = asString(list->get(0));
CPPUNIT_ASSERT_EQUAL(std::string("\\foo\"\"bar"), s->s());
}
{
// string with escape
SharedHandle<ValueBase> r = json::decode("[\"foo\\\"\"]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
const String* s = asString(list->get(0));
CPPUNIT_ASSERT_EQUAL(std::string("foo\""), s->s());
}
{
// string: utf-8 1 to 3 bytes.
SharedHandle<ValueBase> r = json::decode("[\"\\u0024\\u00A2\\u20AC\"]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
const String* s = asString(list->get(0));
CPPUNIT_ASSERT_EQUAL(std::string("$¢€"), s->s());
}
{
// string: utf-8 4 bytes
SharedHandle<ValueBase> r = json::decode("[\"\\uD852\\uDF62\"]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
const String* s = asString(list->get(0));
const char arr[] = { 0xF0u, 0xA4u, 0xADu, 0xA2u };
CPPUNIT_ASSERT_EQUAL(std::string(vbegin(arr), vend(arr)), s->s());
}
{
// null
SharedHandle<ValueBase> r = json::decode("[null]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
const Null* s = asNull(list->get(0));
CPPUNIT_ASSERT(s);
}
{
// true, false
SharedHandle<ValueBase> r = json::decode("[true, false]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
const Bool* trueValue = asBool(list->get(0));
CPPUNIT_ASSERT(trueValue);
CPPUNIT_ASSERT(trueValue->val());
const Bool* falseValue = asBool(list->get(1));
CPPUNIT_ASSERT(falseValue);
CPPUNIT_ASSERT(!falseValue->val());
}
{
// object: 1 member
SharedHandle<ValueBase> r = json::decode("{\"foo\":[\"bar\"]}");
const Dict* dict = asDict(r);
CPPUNIT_ASSERT(dict);
const List* list = asList(dict->get("foo"));
CPPUNIT_ASSERT(list);
const String* s = asString(list->get(0));
CPPUNIT_ASSERT_EQUAL(std::string("bar"), s->s());
}
{
// object: 2 members
SharedHandle<ValueBase> r = json::decode("{\"\":[\"bar\"], "
"\"alpha\" : \"bravo\"}");
const Dict* dict = asDict(r);
CPPUNIT_ASSERT(dict);
const List* list = asList(dict->get(""));
CPPUNIT_ASSERT(list);
const String* s = asString(list->get(0));
CPPUNIT_ASSERT_EQUAL(std::string("bar"), s->s());
const String* str = asString(dict->get("alpha"));
CPPUNIT_ASSERT_EQUAL(std::string("bravo"), str->s());
}
{
// array: 2 values
SharedHandle<ValueBase> r = json::decode("[\"foo\", {}]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
const String* s = asString(list->get(0));
CPPUNIT_ASSERT_EQUAL(std::string("foo"), s->s());
const Dict* dict = asDict(list->get(1));
CPPUNIT_ASSERT(dict);
}
{
// Number: currently we handle floating point number as string
SharedHandle<ValueBase> r = json::decode("[0,-1,1.2,-1.2e-10,-1e10]");
const List* list = asList(r);
CPPUNIT_ASSERT(list);
const Integer* i = asInteger(list->get(0));
CPPUNIT_ASSERT_EQUAL((Integer::ValueType)0, i->i());
const Integer* i1 = asInteger(list->get(1));
CPPUNIT_ASSERT_EQUAL((Integer::ValueType)-1, i1->i());
const String* s2 = asString(list->get(2));
CPPUNIT_ASSERT_EQUAL(std::string("1.2"), s2->s());
const String* s3 = asString(list->get(3));
CPPUNIT_ASSERT_EQUAL(std::string("-1.2e-10"), s3->s());
const String* s4 = asString(list->get(4));
CPPUNIT_ASSERT_EQUAL(std::string("-1e10"), s4->s());
}
}
void JsonTest::testDecode_error()
{
{
try {
// object
SharedHandle<ValueBase> r = json::decode("{");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// object
SharedHandle<ValueBase> r = json::decode("}");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// object
SharedHandle<ValueBase> r = json::decode("{\"\":");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// object
SharedHandle<ValueBase> r = json::decode("{\"\":\"\",");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// array
SharedHandle<ValueBase> r = json::decode("[");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// array
SharedHandle<ValueBase> r = json::decode("]");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// array
SharedHandle<ValueBase> r = json::decode("[\"\"");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// array
SharedHandle<ValueBase> r = json::decode("[\"\",");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"foo]");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"\\u\"]");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"\\u");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"\\u000\"]");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"\\u000");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"\\uD852foo\"]");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"\\uD852");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"\\uD852\\u\"]");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"\\uD852\\u");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"\\uD852\\u0000\"]");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// string
SharedHandle<ValueBase> r = json::decode("[\"\\uD852\\uDF62");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// object
SharedHandle<ValueBase> r = json::decode("{:\"\"}");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// object
SharedHandle<ValueBase> r = json::decode("{\"foo\":}");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// number
SharedHandle<ValueBase> r = json::decode("{00}");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// number
SharedHandle<ValueBase> r = json::decode("{1.}");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// number
SharedHandle<ValueBase> r = json::decode("{1.1e}");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
{
try {
// bool
SharedHandle<ValueBase> r = json::decode("{t");
CPPUNIT_FAIL("exception must be thrown.");
} catch(RecoverableException& e) {
// success
}
}
}
void JsonTest::testEncode()
{
{
Dict dict;
dict["name"] = String::g("aria2");
dict["loc"] = Integer::g(80000);
SharedHandle<List> files = List::g();
files->append(String::g("aria2c"));
dict["files"] = files;
SharedHandle<Dict> attrs = Dict::g();
attrs->put("license", String::g("GPL"));
dict["attrs"] = attrs;
CPPUNIT_ASSERT_EQUAL(std::string("{\"attrs\":{\"license\":\"GPL\"},"
"\"files\":[\"aria2c\"],"
"\"loc\":80000,"
"\"name\":\"aria2\"}"),
json::encode(&dict));
}
{
List list;
list.append("\"\\/\b\f\n\r\t");
CPPUNIT_ASSERT_EQUAL(std::string("[\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"]"),
json::encode(&list));
}
{
List list;
std::string s;
s += 0x1Fu;
list.append(s);
CPPUNIT_ASSERT_EQUAL(std::string("[\"\\u001F\"]"),
json::encode(&list));
}
{
List list;
list.append(Bool::gTrue());
list.append(Bool::gFalse());
list.append(Null::g());
CPPUNIT_ASSERT_EQUAL(std::string("[true,false,null]"),
json::encode(&list));
}
}
} // namespace aria2

View File

@ -76,7 +76,8 @@ aria2c_SOURCES = AllTest.cc\
UriTest.cc\
MockSegment.h\
TripletTest.cc\
CookieHelperTest.cc
CookieHelperTest.cc\
JsonTest.cc
if ENABLE_XML_RPC
aria2c_SOURCES += XmlRpcRequestParserControllerTest.cc\