summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2011-03-09 23:07:27 +0900
committerTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>2011-03-09 23:07:27 +0900
commit7338a2503547ae1310d018d0fc2903fb85b309ea (patch)
treec7111eb641f6bf1b661c03270a8f4407d2fada21 /src
parentd541b350d21cd7747479f21b7c4b6d363248a450 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/GZipEncoder.cc6
-rw-r--r--src/GZipEncoder.h4
-rw-r--r--src/HttpServerBodyCommand.cc56
-rw-r--r--src/Makefile.am3
-rw-r--r--src/ValueBase.cc99
-rw-r--r--src/ValueBase.h71
-rw-r--r--src/XmlRpcMethod.cc11
-rw-r--r--src/XmlRpcMethod.h10
-rw-r--r--src/XmlRpcMethodImpl.cc13
-rw-r--r--src/XmlRpcRequest.cc8
-rw-r--r--src/XmlRpcRequest.h5
-rw-r--r--src/XmlRpcResponse.cc61
-rw-r--r--src/XmlRpcResponse.h11
-rw-r--r--src/bencode2.cc3
-rw-r--r--src/bittorrent_helper.cc2
-rw-r--r--src/bittorrent_helper.h2
-rw-r--r--src/error_code.h3
-rw-r--r--src/json.cc612
-rw-r--r--src/json.h141
19 files changed, 1080 insertions, 41 deletions
diff --git a/src/GZipEncoder.cc b/src/GZipEncoder.cc
index def1c281..c272ce97 100644
--- a/src/GZipEncoder.cc
+++ b/src/GZipEncoder.cc
@@ -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
diff --git a/src/GZipEncoder.h b/src/GZipEncoder.h
index b5cf66de..4ba83ac2 100644
--- a/src/GZipEncoder.h
+++ b/src/GZipEncoder.h
@@ -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.
diff --git a/src/HttpServerBodyCommand.cc b/src/HttpServerBodyCommand.cc
index e49c253c..3d39e111 100644
--- a/src/HttpServerBodyCommand.cc
+++ b/src/HttpServerBodyCommand.cc
@@ -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;
}
diff --git a/src/Makefile.am b/src/Makefile.am
index 05cb27c4..8a3b3d79 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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\
diff --git a/src/ValueBase.cc b/src/ValueBase.cc
index 5048970d..ab388c36 100644
--- a/src/ValueBase.cc
+++ b/src/ValueBase.cc
@@ -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;
}
diff --git a/src/ValueBase.h b/src/ValueBase.h
index a262b853..e13d12d2 100644
--- a/src/ValueBase.h
+++ b/src/ValueBase.h
@@ -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);
diff --git a/src/XmlRpcMethod.cc b/src/XmlRpcMethod.cc
index 75b7ebca..7621b0f2 100644
--- a/src/XmlRpcMethod.cc
+++ b/src/XmlRpcMethod.cc
@@ -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);
}
}
diff --git a/src/XmlRpcMethod.h b/src/XmlRpcMethod.h
index cbdf65c1..80a49fe5 100644
--- a/src/XmlRpcMethod.h
+++ b/src/XmlRpcMethod.h
@@ -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
diff --git a/src/XmlRpcMethodImpl.cc b/src/XmlRpcMethodImpl.cc
index 4e2a9007..1b9edb74 100644
--- a/src/XmlRpcMethodImpl.cc
+++ b/src/XmlRpcMethodImpl.cc
@@ -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));
diff --git a/src/XmlRpcRequest.cc b/src/XmlRpcRequest.cc
index 08d58b68..0e361499 100644
--- a/src/XmlRpcRequest.cc
+++ b/src/XmlRpcRequest.cc
@@ -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() {}
diff --git a/src/XmlRpcRequest.h b/src/XmlRpcRequest.h
index 5b654e03..327b2904 100644
--- a/src/XmlRpcRequest.h
+++ b/src/XmlRpcRequest.h
@@ -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);
diff --git a/src/XmlRpcResponse.cc b/src/XmlRpcResponse.cc
index 6cd47afc..107efa01 100644
--- a/src/XmlRpcResponse.cc
+++ b/src/XmlRpcResponse.cc
@@ -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
diff --git a/src/XmlRpcResponse.h b/src/XmlRpcResponse.h
index eea95e0d..8a978a3d 100644
--- a/src/XmlRpcResponse.h
+++ b/src/XmlRpcResponse.h
@@ -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
diff --git a/src/bencode2.cc b/src/bencode2.cc
index 10b39cda..0e24bfcf 100644
--- a/src/bencode2.cc
+++ b/src/bencode2.cc
@@ -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";
diff --git a/src/bittorrent_helper.cc b/src/bittorrent_helper.cc
index bae8abd9..c1efcfb7 100644
--- a/src/bittorrent_helper.cc
+++ b/src/bittorrent_helper.cc
@@ -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)
{
diff --git a/src/bittorrent_helper.h b/src/bittorrent_helper.h
index 1dffe960..f9463a92 100644
--- a/src/bittorrent_helper.h
+++ b/src/bittorrent_helper.h
@@ -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)
{
diff --git a/src/error_code.h b/src/error_code.h
index 18abc6f0..a721a3cc 100644
--- a/src/error_code.h
+++ b/src/error_code.h
@@ -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
diff --git a/src/json.cc b/src/json.cc
new file mode 100644
index 00000000..f389d854
--- /dev/null
+++ b/src/json.cc
@@ -0,0 +1,612 @@
+/* <!-- 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
diff --git a/src/json.h b/src/json.h
new file mode 100644
index 00000000..186f3a4c
--- /dev/null
+++ b/src/json.h
@@ -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