mirror of https://github.com/aria2/aria2
Added JSONP support. Callback query parameter is "jsoncallback".
parent
feb4e2e53a
commit
bf01bb84b6
|
@ -137,6 +137,11 @@ std::string HttpServer::getBody() const
|
||||||
return lastBody_.str();
|
return lastBody_.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::string& HttpServer::getMethod() const
|
||||||
|
{
|
||||||
|
return lastRequestHeader_->getMethod();
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& HttpServer::getRequestPath() const
|
const std::string& HttpServer::getRequestPath() const
|
||||||
{
|
{
|
||||||
return lastRequestHeader_->getRequestPath();
|
return lastRequestHeader_->getRequestPath();
|
||||||
|
|
|
@ -79,6 +79,8 @@ public:
|
||||||
|
|
||||||
std::string getBody() const;
|
std::string getBody() const;
|
||||||
|
|
||||||
|
const std::string& getMethod() const;
|
||||||
|
|
||||||
const std::string& getRequestPath() const;
|
const std::string& getRequestPath() const;
|
||||||
|
|
||||||
void feedResponse(const std::string& text, const std::string& contentType);
|
void feedResponse(const std::string& text, const std::string& contentType);
|
||||||
|
|
|
@ -213,12 +213,16 @@ bool HttpServerBodyCommand::execute()
|
||||||
addHttpServerResponseCommand();
|
addHttpServerResponseCommand();
|
||||||
return true;
|
return true;
|
||||||
} else if(reqPath == "/jsonrpc") {
|
} else if(reqPath == "/jsonrpc") {
|
||||||
// TODO handle query parameter
|
|
||||||
std::string callback;
|
std::string callback;
|
||||||
|
|
||||||
SharedHandle<ValueBase> json;
|
SharedHandle<ValueBase> json;
|
||||||
try {
|
try {
|
||||||
json = json::decode(httpServer_->getBody());
|
if(httpServer_->getMethod() == "GET") {
|
||||||
|
json::JsonGetParam param = json::decodeGetParams(query);
|
||||||
|
callback = param.callback;
|
||||||
|
json = json::decode(param.request);
|
||||||
|
} else {
|
||||||
|
json = json::decode(httpServer_->getBody());
|
||||||
|
}
|
||||||
} catch(RecoverableException& e) {
|
} catch(RecoverableException& e) {
|
||||||
A2_LOG_INFO_EX
|
A2_LOG_INFO_EX
|
||||||
(fmt("CUID#%lld - Failed to parse JSON-RPC request",
|
(fmt("CUID#%lld - Failed to parse JSON-RPC request",
|
||||||
|
|
49
src/json.cc
49
src/json.cc
|
@ -42,6 +42,7 @@
|
||||||
#include "a2functional.h"
|
#include "a2functional.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "fmt.h"
|
#include "fmt.h"
|
||||||
|
#include "Base64.h"
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
|
@ -607,6 +608,54 @@ std::string encode(const SharedHandle<ValueBase>& json)
|
||||||
return encode(out, json.get()).str();
|
return encode(out, json.get()).str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonGetParam::JsonGetParam
|
||||||
|
(const std::string& request, const std::string& callback)
|
||||||
|
: request(request), callback(callback)
|
||||||
|
{}
|
||||||
|
|
||||||
|
JsonGetParam
|
||||||
|
decodeGetParams(const std::string& query)
|
||||||
|
{
|
||||||
|
std::string jsonRequest;
|
||||||
|
std::string callback;
|
||||||
|
if(!query.empty() && query[0] == '?') {
|
||||||
|
std::string method;
|
||||||
|
std::string id;
|
||||||
|
std::string params;
|
||||||
|
std::vector<std::string> getParams;
|
||||||
|
util::split(query.substr(1), std::back_inserter(getParams), "&");
|
||||||
|
for(std::vector<std::string>::const_iterator i =
|
||||||
|
getParams.begin(), eoi = getParams.end(); i != eoi; ++i) {
|
||||||
|
if(util::startsWith(*i, "method=")) {
|
||||||
|
method = (*i).substr(7);
|
||||||
|
} else if(util::startsWith(*i, "id=")) {
|
||||||
|
id = (*i).substr(3);
|
||||||
|
} else if(util::startsWith(*i, "params=")) {
|
||||||
|
params = (*i).substr(7);
|
||||||
|
} else if(util::startsWith(*i, "jsoncallback=")) {
|
||||||
|
callback = (*i).substr(13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string jsonParam =
|
||||||
|
Base64::decode(util::percentDecode(params));
|
||||||
|
if(method.empty() && id.empty()) {
|
||||||
|
// Assume batch call.
|
||||||
|
jsonRequest = jsonParam;
|
||||||
|
} else {
|
||||||
|
jsonRequest = '{';
|
||||||
|
if(!method.empty()) {
|
||||||
|
strappend(jsonRequest, "\"method\":\"", method, "\",");
|
||||||
|
}
|
||||||
|
if(!id.empty()) {
|
||||||
|
strappend(jsonRequest, "\"id\":\"", id, "\",");
|
||||||
|
}
|
||||||
|
strappend(jsonRequest, "\"params\":", jsonParam);
|
||||||
|
jsonRequest += '}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JsonGetParam(jsonRequest, callback);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace json
|
} // namespace json
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
62
src/json.h
62
src/json.h
|
@ -58,9 +58,9 @@ OutputStream& encode(OutputStream& out, const ValueBase* vlb)
|
||||||
{
|
{
|
||||||
const std::string& s = string.s();
|
const std::string& s = string.s();
|
||||||
std::string t = jsonEscape(s);
|
std::string t = jsonEscape(s);
|
||||||
out_ << '"';
|
out_ << "\"";
|
||||||
out_.write(t.data(), t.size());
|
out_.write(t.data(), t.size());
|
||||||
out_ << '"';
|
out_ << "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(const Integer& integer)
|
virtual void visit(const Integer& integer)
|
||||||
|
@ -80,40 +80,40 @@ OutputStream& encode(OutputStream& out, const ValueBase* vlb)
|
||||||
|
|
||||||
virtual void visit(const List& list)
|
virtual void visit(const List& list)
|
||||||
{
|
{
|
||||||
out_ << '[';
|
out_ << "[";
|
||||||
List::ValueType::const_iterator i = list.begin();
|
List::ValueType::const_iterator i = list.begin();
|
||||||
if(!list.empty()) {
|
if(!list.empty()) {
|
||||||
(*i)->accept(*this);
|
(*i)->accept(*this);
|
||||||
|
++i;
|
||||||
|
for(List::ValueType::const_iterator eoi = list.end(); i != eoi; ++i){
|
||||||
|
out_ << ",";
|
||||||
|
(*i)->accept(*this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
++i;
|
out_ << "]";
|
||||||
for(List::ValueType::const_iterator eoi = list.end(); i != eoi; ++i){
|
|
||||||
out_ << ',';
|
|
||||||
(*i)->accept(*this);
|
|
||||||
}
|
|
||||||
out_ << ']';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(const Dict& dict)
|
virtual void visit(const Dict& dict)
|
||||||
{
|
{
|
||||||
out_ << '{';
|
out_ << "{";
|
||||||
Dict::ValueType::const_iterator i = dict.begin();
|
Dict::ValueType::const_iterator i = dict.begin();
|
||||||
if(!dict.empty()) {
|
if(!dict.empty()) {
|
||||||
std::string key = jsonEscape((*i).first);
|
std::string key = jsonEscape((*i).first);
|
||||||
out_ << '"';
|
out_ << "\"";
|
||||||
out_.write(key.data(), key.size());
|
out_.write(key.data(), key.size());
|
||||||
out_ << "\":";
|
out_ << "\":";
|
||||||
(*i).second->accept(*this);
|
(*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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
++i;
|
out_ << "}";
|
||||||
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:
|
private:
|
||||||
OutputStream& out_;
|
OutputStream& out_;
|
||||||
|
@ -133,6 +133,26 @@ OutputStream& encode(OutputStream& out, const SharedHandle<ValueBase>& vlb)
|
||||||
std::string encode(const ValueBase* json);
|
std::string encode(const ValueBase* json);
|
||||||
std::string encode(const SharedHandle<ValueBase>& json);
|
std::string encode(const SharedHandle<ValueBase>& json);
|
||||||
|
|
||||||
|
struct JsonGetParam {
|
||||||
|
std::string request;
|
||||||
|
std::string callback;
|
||||||
|
JsonGetParam(const std::string& request, const std::string& callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Decodes JSON-RPC request from GET query parameter query. query must
|
||||||
|
// starts with "?". This function identifies method name, id,
|
||||||
|
// parameters and jsonp callback. For method name, it searches
|
||||||
|
// "method" query parameter. For id, it searches "id" query
|
||||||
|
// parameter. The id is always treated as string. For parameters, it
|
||||||
|
// searches "params" query parameter. The params is Base64 encoded
|
||||||
|
// JSON string normally associated "params" key in POST request. For
|
||||||
|
// jsonp callback, it searches "jsoncallback". For example, calling
|
||||||
|
// remote method, sum([1,2,3]) with id=300 looks like this:
|
||||||
|
// ?method=sum&id=300¶ms=WzEsMiwzXQ%3D%3D
|
||||||
|
//
|
||||||
|
// If both method and id are missing, params is treated as batch call.
|
||||||
|
JsonGetParam decodeGetParams(const std::string& query);
|
||||||
|
|
||||||
} // namespace json
|
} // namespace json
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "RecoverableException.h"
|
#include "RecoverableException.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "array_fun.h"
|
#include "array_fun.h"
|
||||||
|
#include "Base64.h"
|
||||||
|
|
||||||
namespace aria2 {
|
namespace aria2 {
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ class JsonTest:public CppUnit::TestFixture {
|
||||||
CPPUNIT_TEST(testDecode);
|
CPPUNIT_TEST(testDecode);
|
||||||
CPPUNIT_TEST(testDecode_error);
|
CPPUNIT_TEST(testDecode_error);
|
||||||
CPPUNIT_TEST(testEncode);
|
CPPUNIT_TEST(testEncode);
|
||||||
|
CPPUNIT_TEST(testDecodeGetParams);
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -21,6 +23,7 @@ public:
|
||||||
void testDecode();
|
void testDecode();
|
||||||
void testDecode_error();
|
void testDecode_error();
|
||||||
void testEncode();
|
void testEncode();
|
||||||
|
void testDecodeGetParams();
|
||||||
};
|
};
|
||||||
|
|
||||||
CPPUNIT_TEST_SUITE_REGISTRATION( JsonTest );
|
CPPUNIT_TEST_SUITE_REGISTRATION( JsonTest );
|
||||||
|
@ -443,4 +446,32 @@ void JsonTest::testEncode()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonTest::testDecodeGetParams()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::string param = util::percentEncode(Base64::encode("[1,2,3]"));
|
||||||
|
std::string query = "?params=";
|
||||||
|
query += param;
|
||||||
|
query += '&';
|
||||||
|
query += "method=sum&";
|
||||||
|
query += "id=300&";
|
||||||
|
query += "jsoncallback=cb";
|
||||||
|
json::JsonGetParam gparam = json::decodeGetParams(query);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("{\"method\":\"sum\","
|
||||||
|
"\"id\":\"300\","
|
||||||
|
"\"params\":[1,2,3]}"),
|
||||||
|
gparam.request);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("cb"), gparam.callback);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::string query = "?params=";
|
||||||
|
query += util::percentEncode(Base64::encode("[{}]"));
|
||||||
|
query += '&';
|
||||||
|
query += "jsoncallback=cb";
|
||||||
|
json::JsonGetParam gparam = json::decodeGetParams(query);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("[{}]"), gparam.request);
|
||||||
|
CPPUNIT_ASSERT_EQUAL(std::string("cb"), gparam.callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace aria2
|
} // namespace aria2
|
||||||
|
|
Loading…
Reference in New Issue