mirror of https://github.com/aria2/aria2
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
parent
d541b350d2
commit
7338a25035
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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\
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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\
|
||||
|
|
Loading…
Reference in New Issue