mirror of https://github.com/aria2/aria2
parent
24ae459523
commit
8f2af33b6d
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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\
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -64,6 +64,11 @@ public:
|
|||
|
||||
virtual bool execute() CXX11_OVERRIDE;
|
||||
|
||||
std::shared_ptr<WebSocketSession>& getSession()
|
||||
{
|
||||
return wsSession_;
|
||||
}
|
||||
|
||||
void updateWriteCheck();
|
||||
};
|
||||
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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\"?>"
|
||||
|
|
Loading…
Reference in New Issue