Explorar o código

Delay auth failures instead of PBKDF2

Closes GH-256
Nils Maier %!s(int64=11) %!d(string=hai) anos
pai
achega
8f2af33b6d

+ 75 - 0
src/DelayedCommand.h

@@ -0,0 +1,75 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2014 Nils Maier
+ *
+ * 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_DELAYED_COMMAND_H
+#define D_DELAYED_COMMAND_H
+
+#include "TimeBasedCommand.h"
+
+namespace aria2
+{
+
+class DelayedCommand : public TimeBasedCommand
+{
+private:
+  std::unique_ptr<Command> command_;
+  bool noWait_;
+
+public:
+  virtual void process() CXX11_OVERRIDE
+  {
+    auto e = getDownloadEngine();
+    e->addCommand(std::move(command_));
+    if (noWait_) {
+      e->setNoWait(true);
+    }
+    enableExit();
+  }
+
+public:
+  DelayedCommand(cuid_t cuid, DownloadEngine* e, time_t delay,
+                 std::unique_ptr<Command> command, bool noWait)
+    : TimeBasedCommand(cuid, e, delay),
+      command_{std::move(command)},
+      noWait_{noWait}
+  {
+  }
+
+  virtual ~DelayedCommand() {}
+};
+
+} // namespace aria2
+
+#endif // D_DELAYED_COMMAND_H

+ 5 - 73
src/DownloadEngine.cc

@@ -76,13 +76,6 @@
 #include "Option.h"
 #include "util_security.h"
 
-// Lower time limit for PBKDF2 operations in validateToken.
-static const double kTokenTimeLower = 0.025;
-// Upper time limit for PBKDF2 operations in validateToken.
-static const double kTokenTimeUpper = 0.5;
-// Sweet spot time for PBKDF2 operations in validateToken.
-static const double kTokenTimeSweetspot = 0.035;
-
 namespace aria2 {
 
 namespace global {
@@ -111,9 +104,7 @@ DownloadEngine::DownloadEngine(std::unique_ptr<EventPoll> eventPoll)
     asyncDNSServers_(nullptr),
 #endif // HAVE_ARES_ADDR_NODE
     dnsCache_(make_unique<DNSCache>()),
-    option_(nullptr),
-    tokenIterations_(5000),
-    tokenAverageDuration_(0.0)
+    option_(nullptr)
 {
   unsigned char sessionId[20];
   util::generateRandomKey(sessionId);
@@ -650,76 +641,17 @@ bool DownloadEngine::validateToken(const std::string& token)
     return true;
   }
 
-  if (!tokenHMAC_ || tokenAverageDuration_ > kTokenTimeUpper ||
-      tokenAverageDuration_ < kTokenTimeLower) {
-
-    // Setup our stuff.
-    if (tokenHMAC_) {
-      A2_LOG_INFO(fmt("Recalculating iterations because avg. duration is %.4f",
-                      tokenAverageDuration_));
-    }
-
+  if (!tokenHMAC_) {
     tokenHMAC_ = HMAC::createRandom();
     if (!tokenHMAC_) {
       A2_LOG_ERROR("Failed to create HMAC");
       return false;
     }
-
-    // This should still be pretty fast on a modern system... Well, too fast
-    // with the initial 5000 iterations, and that is why we adjust it.
-    // XXX We should run this setup high priorty, so that other processes on the
-    // system don't mess up our results and let us underestimate the iterations.
-    std::deque<double> mm;
-    for (auto i = 0; i < 10; ++i) {
-      auto c = std::clock();
-      tokenExpected_ = make_unique<HMACResult>
-        (PBKDF2(tokenHMAC_.get(), option_->get(PREF_RPC_SECRET),
-                tokenIterations_));
-      mm.push_back((std::clock() - c) / (double)CLOCKS_PER_SEC);
-    }
-    std::sort(mm.begin(), mm.end());
-    // Pop outliers.
-    mm.pop_front();
-    mm.pop_back();
-    mm.pop_back();
-    auto duration = std::accumulate(mm.begin(), mm.end(), 0.0) / mm.size();
-
-    A2_LOG_INFO(fmt("Took us %.4f secs on average to perform PBKDF2 with %zu "
-                    "iterations during setup",
-                    duration, tokenIterations_));
-
-    // Adjust iterations so that an op takes about |kTokenTimeSpeetspot| sec,
-    // which would allow for a couple attempts per second (instead of
-    // potentially thousands without PBKDF2).
-    // We might overestimate the performance a bit, but should not perform
-    // worse than |kTokenTimeUpper| secs per attempt on a normally loaded system
-    // and no better than |kTokenTimeLower|. If this does not hold true anymore,
-    // the |tokenAverageDuration_| checks will force a re-calcuation.
-    tokenIterations_ *= kTokenTimeSweetspot / duration;
-
-    auto c = std::clock();
-    tokenExpected_ = make_unique<HMACResult>
-      (PBKDF2(tokenHMAC_.get(), option_->get(PREF_RPC_SECRET),
-              tokenIterations_));
-    duration = (std::clock() - c) / (double)CLOCKS_PER_SEC;
-    A2_LOG_INFO(fmt("Took us %.4f secs to perform PBKDF2 with %zu iterations",
-                    duration, tokenIterations_));
-
-    // Seed average duration.
-    tokenAverageDuration_ = duration;
+    tokenExpected_ = make_unique<HMACResult>(tokenHMAC_->getResult(
+          option_->get(PREF_RPC_SECRET)));
   }
 
-  auto c = std::clock();
-  bool rv = *tokenExpected_ == PBKDF2(tokenHMAC_.get(), token,
-                                      tokenIterations_);
-  auto duration = (std::clock() - c) / (double)CLOCKS_PER_SEC;
-  A2_LOG_DEBUG(fmt("Took us %.4f secs to perform token compare with %zu "
-                   "iterations",
-                   duration, tokenIterations_));
-
-  // Update rolling hash.
-  tokenAverageDuration_ = tokenAverageDuration_ * 0.9 + duration * 0.1;
-  return rv;
+  return *tokenExpected_ == tokenHMAC_->getResult(token);
 }
 
 } // namespace aria2

+ 0 - 3
src/DownloadEngine.h

@@ -183,9 +183,6 @@ private:
 
   std::unique_ptr<util::security::HMAC> tokenHMAC_;
   std::unique_ptr<util::security::HMACResult> tokenExpected_;
-  size_t tokenIterations_;
-
-  double tokenAverageDuration_;
 
 public:
   DownloadEngine(std::unique_ptr<EventPoll> eventPoll);

+ 21 - 12
src/HttpServerBodyCommand.cc

@@ -43,6 +43,7 @@
 #include "RequestGroupMan.h"
 #include "RecoverableException.h"
 #include "HttpServerResponseCommand.h"
+#include "DelayedCommand.h"
 #include "OptionParser.h"
 #include "OptionHandler.h"
 #include "wallclock.h"
@@ -104,6 +105,7 @@ void HttpServerBodyCommand::sendJsonRpcResponse
 (const rpc::RpcResponse& res,
  const std::string& callback)
 {
+  bool notauthorized = rpc::not_authorized(res);
   bool gzip = httpServer_->supportsGZip();
   std::string responseData = rpc::toJson(res, callback, gzip);
   if(res.code == 0) {
@@ -126,24 +128,32 @@ void HttpServerBodyCommand::sendJsonRpcResponse
                               std::move(responseData),
                               getJsonRpcContentType(!callback.empty()));
   }
-  addHttpServerResponseCommand();
+  addHttpServerResponseCommand(notauthorized);
 }
 
 void HttpServerBodyCommand::sendJsonRpcBatchResponse
 (const std::vector<rpc::RpcResponse>& results,
  const std::string& callback)
 {
+  bool notauthorized = rpc::any_not_authorized(results.begin(), results.end());
   bool gzip = httpServer_->supportsGZip();
   std::string responseData = rpc::toJsonBatch(results, callback, gzip);
   httpServer_->feedResponse(std::move(responseData),
                             getJsonRpcContentType(!callback.empty()));
-  addHttpServerResponseCommand();
+  addHttpServerResponseCommand(notauthorized);
 }
 
-void HttpServerBodyCommand::addHttpServerResponseCommand()
+void HttpServerBodyCommand::addHttpServerResponseCommand(bool delayed)
 {
-  e_->addCommand(make_unique<HttpServerResponseCommand>
-                 (getCuid(), httpServer_, e_, socket_));
+  auto resp =
+    make_unique<HttpServerResponseCommand>(getCuid(), httpServer_, e_, socket_);
+  if (delayed) {
+    e_->addCommand(
+        make_unique<DelayedCommand>(getCuid(), e_, 1, std::move(resp), true));
+    return;
+  }
+
+  e_->addCommand(std::move(resp));
   e_->setNoWait(true);
 }
 
@@ -201,7 +211,7 @@ bool HttpServerBodyCommand::execute()
             }
           }
           httpServer_->feedResponse(200, accessControlHeaders);
-          addHttpServerResponseCommand();
+          addHttpServerResponseCommand(false);
           return true;
         }
 
@@ -223,7 +233,7 @@ bool HttpServerBodyCommand::execute()
               (fmt("CUID#%" PRId64 " - Failed to parse XML-RPC request",
                    getCuid()));
             httpServer_->feedResponse(400);
-            addHttpServerResponseCommand();
+            addHttpServerResponseCommand(false);
             return true;
           }
           A2_LOG_INFO(fmt("Executing RPC method %s", req.methodName.c_str()));
@@ -232,10 +242,10 @@ bool HttpServerBodyCommand::execute()
           bool gzip = httpServer_->supportsGZip();
           std::string responseData = rpc::toXml(res, gzip);
           httpServer_->feedResponse(std::move(responseData), "text/xml");
-          addHttpServerResponseCommand();
+          addHttpServerResponseCommand(false);
 #else // !ENABLE_XML_RPC
           httpServer_->feedResponse(404);
-          addHttpServerResponseCommand();
+          addHttpServerResponseCommand(false);
 #endif // !ENABLE_XML_RPC
           return true;
         }
@@ -274,8 +284,7 @@ bool HttpServerBodyCommand::execute()
           }
           Dict* jsondict = downcast<Dict>(json);
           if(jsondict) {
-            rpc::RpcResponse res =
-              rpc::processJsonRpcRequest(jsondict, e_, preauthorized);
+            auto res = rpc::processJsonRpcRequest(jsondict, e_, preauthorized);
             sendJsonRpcResponse(res, callback);
           } else {
             List* jsonlist = downcast<List>(json);
@@ -306,7 +315,7 @@ bool HttpServerBodyCommand::execute()
         }
         default:
           httpServer_->feedResponse(404);
-          addHttpServerResponseCommand();
+          addHttpServerResponseCommand(false);
           return true;
         }
       } else {

+ 1 - 1
src/HttpServerBodyCommand.h

@@ -63,7 +63,7 @@ private:
   void sendJsonRpcBatchResponse
   (const std::vector<rpc::RpcResponse>& results,
    const std::string& callback);
-  void addHttpServerResponseCommand();
+  void addHttpServerResponseCommand(bool delayed);
   void updateWriteCheck();
 public:
   HttpServerBodyCommand(cuid_t cuid,

+ 1 - 0
src/Makefile.am

@@ -66,6 +66,7 @@ SRCS =  \
 	DefaultDiskWriterFactory.cc DefaultDiskWriterFactory.h\
 	DefaultPieceStorage.cc DefaultPieceStorage.h\
 	DefaultStreamPieceSelector.cc DefaultStreamPieceSelector.h\
+	DelayedCommand.h\
 	Dependency.h\
 	DirectDiskAdaptor.cc DirectDiskAdaptor.h\
 	DiskAdaptor.cc DiskAdaptor.h\

+ 5 - 2
src/RpcMethod.cc

@@ -94,13 +94,16 @@ void RpcMethod::authorize(RpcRequest& req, DownloadEngine* e)
 
 RpcResponse RpcMethod::execute(RpcRequest req, DownloadEngine* e)
 {
+  auto authorized = RpcResponse::NOTAUTHORIZED;
   try {
     authorize(req, e);
+    authorized = RpcResponse::AUTHORIZED;
     auto r = process(req, e);
-    return RpcResponse(0, std::move(r), std::move(req.id));
+    return RpcResponse(0, authorized, std::move(r), std::move(req.id));
   } catch(RecoverableException& ex) {
     A2_LOG_DEBUG_EX(EX_EXCEPTION_CAUGHT, ex);
-    return RpcResponse(1, createErrorResponse(ex, req), std::move(req.id));
+    return RpcResponse(1, authorized, createErrorResponse(ex, req),
+                       std::move(req.id));
   }
 }
 

+ 1 - 1
src/RpcMethod.h

@@ -97,7 +97,7 @@ public:
 
   // Do work to fulfill RpcRequest req and returns its result as
   // RpcResponse. This method delegates to process() method.
-  RpcResponse execute(RpcRequest req, DownloadEngine* e);
+  virtual RpcResponse execute(RpcRequest req, DownloadEngine* e);
 };
 
 } // namespace rpc

+ 65 - 46
src/RpcMethodImpl.cc

@@ -1360,54 +1360,73 @@ std::unique_ptr<ValueBase> SaveSessionRpcMethod::process
 std::unique_ptr<ValueBase> SystemMulticallRpcMethod::process
 (const RpcRequest& req, DownloadEngine* e)
 {
-  const List* methodSpecs = checkRequiredParam<List>(req, 0);
-  auto list = List::g();
-  auto auth = RpcRequest::MUST_AUTHORIZE;
-  for(auto & methodSpec : *methodSpecs) {
-    Dict* methodDict = downcast<Dict>(methodSpec);
-    if(!methodDict) {
-      list->append(createErrorResponse
-                   (DL_ABORT_EX("system.multicall expected struct."), req));
-      continue;
-    }
-    const String* methodName = downcast<String>(methodDict->get(KEY_METHOD_NAME));
-    if(!methodName) {
-      list->append(createErrorResponse
-                   (DL_ABORT_EX("Missing methodName."), req));
-      continue;
-    }
-    if(methodName->s() == getMethodName()) {
-      list->append(createErrorResponse
-                   (DL_ABORT_EX("Recursive system.multicall forbidden."), req));
-      continue;
-    }
-    // TODO what if params missing?
-    auto tempParamsList = methodDict->get(KEY_PARAMS);
-    std::unique_ptr<List> paramsList;
-    if(downcast<List>(tempParamsList)) {
-      paramsList.reset(static_cast<List*>(methodDict->popValue(KEY_PARAMS)
-                                          .release()));
-    } else {
-      paramsList = List::g();
-    }
-    RpcRequest r = {
-      methodName->s(),
-      std::move(paramsList),
-      nullptr,
-      auth,
-      req.jsonRpc
-    };
-    RpcResponse res = getMethod(methodName->s())->execute(std::move(r), e);
-    if(res.code == 0) {
-      auto l = List::g();
-      l->append(std::move(res.param));
-      list->append(std::move(l));
-      auth = RpcRequest::PREAUTHORIZED;
-    } else {
-      list->append(std::move(res.param));
+  // Should never get here, since SystemMulticallRpcMethod overrides execute().
+  assert(false);
+  return nullptr;
+}
+
+RpcResponse SystemMulticallRpcMethod::execute(RpcRequest req, DownloadEngine *e)
+{
+  auto preauthorized = RpcRequest::MUST_AUTHORIZE;
+  auto authorized = RpcResponse::AUTHORIZED;
+  try {
+    const List* methodSpecs = checkRequiredParam<List>(req, 0);
+    auto list = List::g();
+    for(auto & methodSpec : *methodSpecs) {
+      Dict* methodDict = downcast<Dict>(methodSpec);
+      if(!methodDict) {
+        list->append(createErrorResponse
+                      (DL_ABORT_EX("system.multicall expected struct."), req));
+        continue;
+      }
+      const String* methodName = downcast<String>(methodDict->get(KEY_METHOD_NAME));
+      if(!methodName) {
+        list->append(createErrorResponse
+                      (DL_ABORT_EX("Missing methodName."), req));
+        continue;
+      }
+      if(methodName->s() == getMethodName()) {
+        list->append(createErrorResponse
+                      (DL_ABORT_EX("Recursive system.multicall forbidden."), req));
+        continue;
+      }
+      // TODO what if params missing?
+      auto tempParamsList = methodDict->get(KEY_PARAMS);
+      std::unique_ptr<List> paramsList;
+      if(downcast<List>(tempParamsList)) {
+        paramsList.reset(static_cast<List*>(methodDict->popValue(KEY_PARAMS)
+                                            .release()));
+      } else {
+        paramsList = List::g();
+      }
+      RpcRequest r = {
+        methodName->s(),
+        std::move(paramsList),
+        nullptr,
+        preauthorized,
+        req.jsonRpc
+      };
+      RpcResponse res = getMethod(methodName->s())->execute(std::move(r), e);
+      if(rpc::not_authorized(res)) {
+        authorized = RpcResponse::NOTAUTHORIZED;
+      } else {
+        preauthorized = RpcRequest::PREAUTHORIZED;
+      }
+      if(res.code == 0) {
+        auto l = List::g();
+        l->append(std::move(res.param));
+        list->append(std::move(l));
+      } else {
+        list->append(std::move(res.param));
+      }
     }
+    return RpcResponse(0, authorized, std::move(list), std::move(req.id));
+
+  } catch(RecoverableException& ex) {
+    A2_LOG_DEBUG_EX(EX_EXCEPTION_CAUGHT, ex);
+    return RpcResponse(1, authorized, createErrorResponse(ex, req),
+                       std::move(req.id));
   }
-  return std::move(list);
 }
 
 std::unique_ptr<ValueBase> NoSuchMethodRpcMethod::process

+ 2 - 7
src/RpcMethodImpl.h

@@ -586,14 +586,9 @@ class SystemMulticallRpcMethod:public RpcMethod {
 protected:
   virtual std::unique_ptr<ValueBase> process
   (const RpcRequest& req, DownloadEngine* e) CXX11_OVERRIDE;
+
 public:
-  virtual void authorize(RpcRequest& req, DownloadEngine* e) CXX11_OVERRIDE
-  {
-    // Batch calls (e.g., system.multicall) authorizes only nested
-    // methods. This is because XML-RPC system.multicall only accpets
-    // methods array and there is no room for us to insert token
-    // parameter.
-  }
+  virtual RpcResponse execute(RpcRequest req, DownloadEngine* e) CXX11_OVERRIDE;
 
   static const char* getMethodName()
   {

+ 1 - 1
src/RpcRequest.cc

@@ -51,7 +51,7 @@ RpcRequest::RpcRequest(std::string methodName,
 RpcRequest::RpcRequest(std::string methodName,
                        std::unique_ptr<List> params,
                        std::unique_ptr<ValueBase> id,
-                       RpcRequest::authorization_t authorization,
+                       RpcRequest::preauthorization_t authorization,
                        bool jsonRpc)
   : methodName{std::move(methodName)}, params{std::move(params)},
     id{std::move(id)}, authorization{authorization}, jsonRpc{jsonRpc}

+ 3 - 3
src/RpcRequest.h

@@ -46,7 +46,7 @@ namespace aria2 {
 namespace rpc {
 
 struct RpcRequest {
-  enum authorization_t {
+  enum preauthorization_t {
     MUST_AUTHORIZE,
     PREAUTHORIZED
   };
@@ -54,7 +54,7 @@ struct RpcRequest {
   std::string methodName;
   std::unique_ptr<List> params;
   std::unique_ptr<ValueBase> id;
-  authorization_t authorization;
+  preauthorization_t authorization;
   bool jsonRpc;
 
   RpcRequest();
@@ -65,7 +65,7 @@ struct RpcRequest {
   RpcRequest(std::string methodName,
              std::unique_ptr<List> params,
              std::unique_ptr<ValueBase> id,
-             authorization_t authorization,
+             preauthorization_t authorization,
              bool jsonRpc = false);
 };
 

+ 2 - 1
src/RpcResponse.cc

@@ -121,9 +121,10 @@ std::string encodeAll
 
 RpcResponse::RpcResponse
 (int code,
+ RpcResponse::authorization_t authorized,
  std::unique_ptr<ValueBase> param,
  std::unique_ptr<ValueBase> id)
-  : code{code}, param{std::move(param)}, id{std::move(id)}
+  : param{std::move(param)}, id{std::move(id)}, code{code}, authorized{authorized}
 {}
 
 std::string toXml(const RpcResponse& res, bool gzip)

+ 19 - 1
src/RpcResponse.h

@@ -47,17 +47,35 @@ namespace aria2 {
 namespace rpc {
 
 struct RpcResponse {
+  enum authorization_t {
+    NOTAUTHORIZED,
+    AUTHORIZED
+  };
+
   // 0 for success, non-zero for error
-  int code;
   std::unique_ptr<ValueBase> param;
   std::unique_ptr<ValueBase> id;
+  int code;
+  authorization_t authorized;
 
   RpcResponse
   (int code,
+   authorization_t authorized,
    std::unique_ptr<ValueBase> param,
    std::unique_ptr<ValueBase> id);
 };
 
+inline
+bool not_authorized(const rpc::RpcResponse& res)
+{
+  return res.authorized != rpc::RpcResponse::AUTHORIZED;
+}
+
+template<typename InputIterator>
+bool any_not_authorized(const InputIterator begin, const InputIterator end) {
+  return std::any_of(begin, end, not_authorized);
+}
+
 std::string toXml(const RpcResponse& response, bool gzip = false);
 
 // Encodes RPC response in JSON. If callback is not empty, the

+ 5 - 0
src/WebSocketInteractionCommand.h

@@ -64,6 +64,11 @@ public:
 
   virtual bool execute() CXX11_OVERRIDE;
 
+  std::shared_ptr<WebSocketSession>& getSession()
+  {
+    return wsSession_;
+  }
+
   void updateWriteCheck();
 };
 

+ 34 - 3
src/WebSocketSession.cc

@@ -43,6 +43,8 @@
 #include "RecoverableException.h"
 #include "message.h"
 #include "DownloadEngine.h"
+#include "DelayedCommand.h"
+#include "WebSocketInteractionCommand.h"
 #include "rpc_helper.h"
 #include "RpcResponse.h"
 #include "json.h"
@@ -111,8 +113,9 @@ ssize_t recvCallback(wslay_event_context_ptr wsctx,
 namespace {
 void addResponse(WebSocketSession* wsSession, const RpcResponse& res)
 {
+  bool notauthorized = rpc::not_authorized(res);
   std::string response = toJson(res, "", false);
-  wsSession->addTextMessage(response);
+  wsSession->addTextMessage(response, notauthorized);
 }
 } // namespace
 
@@ -120,8 +123,9 @@ namespace {
 void addResponse(WebSocketSession* wsSession,
                  const std::vector<RpcResponse>& results)
 {
+  bool notauthorized = rpc::any_not_authorized(results.begin(), results.end());
   std::string response = toJsonBatch(results, "", false);
-  wsSession->addTextMessage(response);
+  wsSession->addTextMessage(response, notauthorized);
 }
 } // namespace
 
@@ -264,8 +268,35 @@ int WebSocketSession::onWriteEvent()
   }
 }
 
-void WebSocketSession::addTextMessage(const std::string& msg)
+namespace {
+class TextMessageCommand : public Command
 {
+private:
+  std::shared_ptr<WebSocketSession> session_;
+  const std::string msg_;
+public:
+  TextMessageCommand(cuid_t cuid, std::shared_ptr<WebSocketSession> session,
+                            const std::string& msg)
+    : Command(cuid), session_{std::move(session)}, msg_{msg}
+  {}
+  virtual bool execute() CXX11_OVERRIDE
+  {
+    session_->addTextMessage(msg_, false);
+    return true;
+  }
+};
+} // namespace
+
+void WebSocketSession::addTextMessage(const std::string& msg, bool delayed)
+{
+  if (delayed) {
+    auto e = getDownloadEngine();
+    auto cuid = command_->getCuid();
+    auto c = make_unique<TextMessageCommand>(cuid, command_->getSession(), msg);
+    e->addCommand(make_unique<DelayedCommand>(cuid, e, 1, std::move(c), false));
+    return;
+  }
+
   // TODO Don't add text message if the size of outbound queue in
   // wsctx_ exceeds certain limit.
   wslay_event_msg arg = {

+ 1 - 1
src/WebSocketSession.h

@@ -74,7 +74,7 @@ public:
   int onWriteEvent();
   // Adds text message |msg|. The message is queued and will be sent
   // in onWriteEvent().
-  void addTextMessage(const std::string& msg);
+  void addTextMessage(const std::string& msg, bool delayed);
   // Returns true if the close frame is received.
   bool closeReceived();
   // Returns true if the close frame is sent.

+ 1 - 1
src/WebSocketSessionMan.cc

@@ -78,7 +78,7 @@ void WebSocketSessionMan::addNotification
   dict->put("params", std::move(params));
   std::string msg = json::encode(dict.get());
   for(auto& session : sessions_) {
-    session->addTextMessage(msg);
+    session->addTextMessage(msg, false);
     session->getCommand()->updateWriteCheck();
   }
 }

+ 3 - 2
src/rpc_helper.cc

@@ -73,11 +73,12 @@ RpcResponse createJsonRpcErrorResponse(int code,
   auto params = Dict::g();
   params->put("code", Integer::g(code));
   params->put("message", msg);
-  return rpc::RpcResponse{code, std::move(params), std::move(id)};
+  return rpc::RpcResponse{code, rpc::RpcResponse::AUTHORIZED, std::move(params),
+                          std::move(id)};
 }
 
 RpcResponse processJsonRpcRequest(Dict* jsondict, DownloadEngine* e,
-                                  RpcRequest::authorization_t authorization)
+                                  RpcRequest::preauthorization_t authorization)
 {
   auto id = jsondict->popValue("id");
   if(!id) {

+ 1 - 1
src/rpc_helper.h

@@ -65,7 +65,7 @@ RpcResponse createJsonRpcErrorResponse(int code,
 
 // Processes JSON-RPC request |jsondict| and returns the result.
 RpcResponse processJsonRpcRequest(Dict* jsondict, DownloadEngine* e,
-                                  RpcRequest::authorization_t authorization);
+                                  RpcRequest::preauthorization_t authorization);
 
 } // namespace rpc
 

+ 4 - 3
test/RpcResponseTest.cc

@@ -28,7 +28,8 @@ void RpcResponseTest::testToJson()
   {
     auto param = List::g();
     param->append(Integer::g(1));
-    RpcResponse res(0, std::move(param), String::g("9"));
+    RpcResponse res(0, RpcResponse::AUTHORIZED, std::move(param),
+                    String::g("9"));
     results.push_back(std::move(res));
     std::string s = toJson(results.back(), "", false);
     CPPUNIT_ASSERT_EQUAL(std::string("{\"id\":\"9\","
@@ -47,7 +48,7 @@ void RpcResponseTest::testToJson()
     auto param = Dict::g();
     param->put("code", Integer::g(1));
     param->put("message", "HELLO ERROR");
-    RpcResponse res(1, std::move(param), Null::g());
+    RpcResponse res(1, RpcResponse::AUTHORIZED, std::move(param), Null::g());
     results.push_back(std::move(res));
     std::string s = toJson(results.back(), "", false);
     CPPUNIT_ASSERT_EQUAL(std::string("{\"id\":null,"
@@ -101,7 +102,7 @@ void RpcResponseTest::testToXml()
   auto param = Dict::g();
   param->put("faultCode", Integer::g(1));
   param->put("faultString", "No such method: make.hamburger");
-  RpcResponse res(1, std::move(param), Null::g());
+  RpcResponse res(1, RpcResponse::AUTHORIZED, std::move(param), Null::g());
   std::string s = toXml(res, false);
   CPPUNIT_ASSERT_EQUAL
     (std::string("<?xml version=\"1.0\"?>"