Преглед на файлове

Added JSONP support. Callback query parameter is "jsoncallback".

Tatsuhiro Tsujikawa преди 14 години
родител
ревизия
bf01bb84b6
променени са 6 файла, в които са добавени 135 реда и са изтрити 24 реда
  1. 5 0
      src/HttpServer.cc
  2. 2 0
      src/HttpServer.h
  3. 7 3
      src/HttpServerBodyCommand.cc
  4. 49 0
      src/json.cc
  5. 41 21
      src/json.h
  6. 31 0
      test/JsonTest.cc

+ 5 - 0
src/HttpServer.cc

@@ -137,6 +137,11 @@ std::string HttpServer::getBody() const
   return lastBody_.str();
 }
 
+const std::string& HttpServer::getMethod() const
+{
+  return lastRequestHeader_->getMethod();
+}
+
 const std::string& HttpServer::getRequestPath() const
 {
   return lastRequestHeader_->getRequestPath();

+ 2 - 0
src/HttpServer.h

@@ -79,6 +79,8 @@ public:
 
   std::string getBody() const;
 
+  const std::string& getMethod() const;
+
   const std::string& getRequestPath() const;
 
   void feedResponse(const std::string& text, const std::string& contentType);

+ 7 - 3
src/HttpServerBodyCommand.cc

@@ -213,12 +213,16 @@ bool HttpServerBodyCommand::execute()
           addHttpServerResponseCommand();
           return true;
         } else if(reqPath == "/jsonrpc") {
-          // TODO handle query parameter
           std::string callback;
-
           SharedHandle<ValueBase> json;
           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) {
             A2_LOG_INFO_EX
               (fmt("CUID#%lld - Failed to parse JSON-RPC request",

+ 49 - 0
src/json.cc

@@ -42,6 +42,7 @@
 #include "a2functional.h"
 #include "util.h"
 #include "fmt.h"
+#include "Base64.h"
 
 namespace aria2 {
 
@@ -607,6 +608,54 @@ std::string encode(const SharedHandle<ValueBase>& json)
   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 aria2

+ 41 - 21
src/json.h

@@ -58,9 +58,9 @@ OutputStream& encode(OutputStream& out, const ValueBase* vlb)
     {
       const std::string& s = string.s();
       std::string t = jsonEscape(s);
-      out_ << '"';
+      out_ << "\"";
       out_.write(t.data(), t.size());
-      out_ << '"';
+      out_ << "\"";
     }
 
     virtual void visit(const Integer& integer)
@@ -80,40 +80,40 @@ OutputStream& encode(OutputStream& out, const ValueBase* vlb)
 
     virtual void visit(const List& list)
     {
-      out_ << '[';
+      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);
+        }
       }
-      ++i;
-      for(List::ValueType::const_iterator eoi = list.end(); i != eoi; ++i){
-        out_ << ',';
-        (*i)->accept(*this);
-      }
-      out_ << ']';
+      out_ << "]";
     }
 
     virtual void visit(const Dict& dict)
     {
-      out_ << '{';
+      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_ << "\"";
         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_ << '}';
+      out_ << "}";
     }
   private:
     OutputStream& out_;
@@ -133,6 +133,26 @@ OutputStream& encode(OutputStream& out, const SharedHandle<ValueBase>& vlb)
 std::string encode(const 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&params=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 aria2

+ 31 - 0
test/JsonTest.cc

@@ -5,6 +5,7 @@
 #include "RecoverableException.h"
 #include "util.h"
 #include "array_fun.h"
+#include "Base64.h"
 
 namespace aria2 {
 
@@ -14,6 +15,7 @@ class JsonTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testDecode);
   CPPUNIT_TEST(testDecode_error);
   CPPUNIT_TEST(testEncode);
+  CPPUNIT_TEST(testDecodeGetParams);
   CPPUNIT_TEST_SUITE_END();
 private:
 
@@ -21,6 +23,7 @@ public:
   void testDecode();
   void testDecode_error();
   void testEncode();
+  void testDecodeGetParams();
 };
 
 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