summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/DelayedCommand.h75
-rw-r--r--src/DownloadEngine.cc78
-rw-r--r--src/DownloadEngine.h3
-rw-r--r--src/HttpServerBodyCommand.cc33
-rw-r--r--src/HttpServerBodyCommand.h2
-rw-r--r--src/Makefile.am1
-rw-r--r--src/RpcMethod.cc7
-rw-r--r--src/RpcMethod.h2
-rw-r--r--src/RpcMethodImpl.cc111
-rw-r--r--src/RpcMethodImpl.h9
-rw-r--r--src/RpcRequest.cc2
-rw-r--r--src/RpcRequest.h6
-rw-r--r--src/RpcResponse.cc3
-rw-r--r--src/RpcResponse.h20
-rw-r--r--src/WebSocketInteractionCommand.h5
-rw-r--r--src/WebSocketSession.cc37
-rw-r--r--src/WebSocketSession.h2
-rw-r--r--src/WebSocketSessionMan.cc2
-rw-r--r--src/rpc_helper.cc5
-rw-r--r--src/rpc_helper.h2
-rw-r--r--test/RpcResponseTest.cc7
21 files changed, 250 insertions, 162 deletions
diff --git a/src/DelayedCommand.h b/src/DelayedCommand.h
new file mode 100644
index 00000000..b02bffa0
--- /dev/null
+++ b/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
diff --git a/src/DownloadEngine.cc b/src/DownloadEngine.cc
index 93a1c5d1..1b8029f3 100644
--- a/src/DownloadEngine.cc
+++ b/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
diff --git a/src/DownloadEngine.h b/src/DownloadEngine.h
index eadb2f46..7ccf61de 100644
--- a/src/DownloadEngine.h
+++ b/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);
diff --git a/src/HttpServerBodyCommand.cc b/src/HttpServerBodyCommand.cc
index 8479f3f8..33ad1abe 100644
--- a/src/HttpServerBodyCommand.cc
+++ b/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 {
diff --git a/src/HttpServerBodyCommand.h b/src/HttpServerBodyCommand.h
index 7817c048..25982e4e 100644
--- a/src/HttpServerBodyCommand.h
+++ b/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,
diff --git a/src/Makefile.am b/src/Makefile.am
index 89e442e7..bcf25948 100644
--- a/src/Makefile.am
+++ b/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\
diff --git a/src/RpcMethod.cc b/src/RpcMethod.cc
index ec424300..ad3a1c28 100644
--- a/src/RpcMethod.cc
+++ b/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));
}
}
diff --git a/src/RpcMethod.h b/src/RpcMethod.h
index f5fa2024..7eadefc2 100644
--- a/src/RpcMethod.h
+++ b/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
diff --git a/src/RpcMethodImpl.cc b/src/RpcMethodImpl.cc
index fd4c3d49..eb1a094f 100644
--- a/src/RpcMethodImpl.cc
+++ b/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
diff --git a/src/RpcMethodImpl.h b/src/RpcMethodImpl.h
index 6d691611..442303fe 100644
--- a/src/RpcMethodImpl.h
+++ b/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()
{
diff --git a/src/RpcRequest.cc b/src/RpcRequest.cc
index 62dae2c7..e49e99ec 100644
--- a/src/RpcRequest.cc
+++ b/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}
diff --git a/src/RpcRequest.h b/src/RpcRequest.h
index c06e4424..604dcde5 100644
--- a/src/RpcRequest.h
+++ b/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);
};
diff --git a/src/RpcResponse.cc b/src/RpcResponse.cc
index e944e823..c374cee7 100644
--- a/src/RpcResponse.cc
+++ b/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)
diff --git a/src/RpcResponse.h b/src/RpcResponse.h
index 1d90d0e7..e2d2bb3a 100644
--- a/src/RpcResponse.h
+++ b/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
diff --git a/src/WebSocketInteractionCommand.h b/src/WebSocketInteractionCommand.h
index ce7f7723..5db4acde 100644
--- a/src/WebSocketInteractionCommand.h
+++ b/src/WebSocketInteractionCommand.h
@@ -64,6 +64,11 @@ public:
virtual bool execute() CXX11_OVERRIDE;
+ std::shared_ptr<WebSocketSession>& getSession()
+ {
+ return wsSession_;
+ }
+
void updateWriteCheck();
};
diff --git a/src/WebSocketSession.cc b/src/WebSocketSession.cc
index aced5622..b035eb18 100644
--- a/src/WebSocketSession.cc
+++ b/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 = {
diff --git a/src/WebSocketSession.h b/src/WebSocketSession.h
index 05957460..59c4ef4d 100644
--- a/src/WebSocketSession.h
+++ b/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.
diff --git a/src/WebSocketSessionMan.cc b/src/WebSocketSessionMan.cc
index 3062a821..ee9dfabd 100644
--- a/src/WebSocketSessionMan.cc
+++ b/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();
}
}
diff --git a/src/rpc_helper.cc b/src/rpc_helper.cc
index a4c840e3..21e492ef 100644
--- a/src/rpc_helper.cc
+++ b/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) {
diff --git a/src/rpc_helper.h b/src/rpc_helper.h
index 1b7f036f..10bdec32 100644
--- a/src/rpc_helper.h
+++ b/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
diff --git a/test/RpcResponseTest.cc b/test/RpcResponseTest.cc
index 7838d075..9083892e 100644
--- a/test/RpcResponseTest.cc
+++ b/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\"?>"