diff --git a/src/base/api/interfaces/IApiRequest.h b/src/base/api/interfaces/IApiRequest.h index 8e65a921..4f74581c 100644 --- a/src/base/api/interfaces/IApiRequest.h +++ b/src/base/api/interfaces/IApiRequest.h @@ -54,16 +54,28 @@ public: enum RequestType { REQ_UNKNOWN, - REQ_SUMMARY + REQ_SUMMARY, + REQ_JSON_RPC + }; + + + enum ErrorCode : int { + RPC_PARSE_ERROR = -32700, + RPC_INVALID_REQUEST = -32600, + RPC_METHOD_NOT_FOUND = -32601, + RPC_INVALID_PARAMS = -32602 }; virtual ~IApiRequest() = default; + virtual bool accept() = 0; + virtual bool hasParseError() const = 0; virtual bool isDone() const = 0; virtual bool isNew() const = 0; virtual bool isRestricted() const = 0; virtual const rapidjson::Value &json() const = 0; + virtual const String &rpcMethod() const = 0; virtual const String &url() const = 0; virtual int version() const = 0; virtual Method method() const = 0; @@ -71,7 +83,6 @@ public: virtual rapidjson::Value &reply() = 0; virtual RequestType type() const = 0; virtual Source source() const = 0; - virtual void accept() = 0; virtual void done(int status) = 0; }; diff --git a/src/base/api/requests/ApiRequest.h b/src/base/api/requests/ApiRequest.h index 1fd721f1..ad4b0c35 100644 --- a/src/base/api/requests/ApiRequest.h +++ b/src/base/api/requests/ApiRequest.h @@ -28,6 +28,7 @@ #include "base/api/interfaces/IApiRequest.h" +#include "base/tools/String.h" namespace xmrig { @@ -40,28 +41,30 @@ public: ~ApiRequest() override; protected: - inline bool isDone() const override { return m_state == STATE_DONE; } - inline bool isNew() const override { return m_state == STATE_NEW; } - inline bool isRestricted() const override { return m_restricted; } - inline int version() const override { return m_version; } - inline RequestType type() const override { return m_type; } - inline Source source() const override { return m_source; } - inline void accept() override { m_state = STATE_ACCEPTED; } - inline void done(int) override { m_state = STATE_DONE; } - - int m_version = 1; - RequestType m_type = REQ_UNKNOWN; - -private: enum State { STATE_NEW, STATE_ACCEPTED, STATE_DONE }; + inline bool accept() override { m_state = STATE_ACCEPTED; return true; } + inline bool isDone() const override { return m_state == STATE_DONE; } + inline bool isNew() const override { return m_state == STATE_NEW; } + inline bool isRestricted() const override { return m_restricted; } + inline const String &rpcMethod() const override { return m_rpcMethod; } + inline int version() const override { return m_version; } + inline RequestType type() const override { return m_type; } + inline Source source() const override { return m_source; } + inline void done(int) override { m_state = STATE_DONE; } + + int m_version = 1; + RequestType m_type = REQ_UNKNOWN; + State m_state = STATE_NEW; + String m_rpcMethod; + +private: bool m_restricted; Source m_source; - State m_state = STATE_NEW; }; diff --git a/src/base/api/requests/HttpApiRequest.cpp b/src/base/api/requests/HttpApiRequest.cpp index c3c2dac4..ed13e47d 100644 --- a/src/base/api/requests/HttpApiRequest.cpp +++ b/src/base/api/requests/HttpApiRequest.cpp @@ -23,14 +23,45 @@ */ +#include "3rdparty/http-parser/http_parser.h" #include "base/api/requests/HttpApiRequest.h" +#include "base/io/json/Json.h" #include "base/net/http/HttpData.h" #include "rapidjson/error/en.h" +namespace xmrig { + + +static const char *kError = "error"; +static const char *kId = "id"; +static const char *kResult = "result"; + + +static inline const char *rpcError(int code) { + switch (code) { + case IApiRequest::RPC_PARSE_ERROR: + return "Parse error"; + + case IApiRequest::RPC_INVALID_REQUEST: + return "Invalid Request"; + + case IApiRequest::RPC_METHOD_NOT_FOUND: + return "Method not found"; + + case IApiRequest::RPC_INVALID_PARAMS: + return "Invalid params"; + } + + return "Internal error"; +} + + +} // namespace xmrig + + xmrig::HttpApiRequest::HttpApiRequest(const HttpData &req, bool restricted) : ApiRequest(SOURCE_HTTP, restricted), - m_parsed(false), m_req(req), m_res(req.id()), m_url(req.url.c_str()) @@ -41,6 +72,28 @@ xmrig::HttpApiRequest::HttpApiRequest(const HttpData &req, bool restricted) : } } + if (method() == METHOD_POST && url() == "/json_rpc") { + m_type = REQ_JSON_RPC; + accept(); + + if (hasParseError()) { + done(RPC_PARSE_ERROR); + + return; + } + + m_rpcMethod = Json::getString(json(), "method"); + if (m_rpcMethod.isEmpty()) { + done(RPC_INVALID_REQUEST); + + return; + } + + m_state = STATE_NEW; + + return; + } + if (url().size() > 4) { if (memcmp(url().data(), "/2/", 3) == 0) { m_version = 2; @@ -49,6 +102,31 @@ xmrig::HttpApiRequest::HttpApiRequest(const HttpData &req, bool restricted) : } +bool xmrig::HttpApiRequest::accept() +{ + using namespace rapidjson; + + ApiRequest::accept(); + + if (m_parsed == 0 && !m_req.body.empty()) { + m_body.Parse(m_req.body.c_str()); + m_parsed = m_body.HasParseError() ? 2 : 1; + + if (!hasParseError()) { + return true; + } + + if (type() != REQ_JSON_RPC) { + reply().AddMember(StringRef(kError), StringRef(GetParseError_En(m_body.GetParseError())), doc().GetAllocator()); + } + + return false; + } + + return hasParseError(); +} + + const rapidjson::Value &xmrig::HttpApiRequest::json() const { return m_body; @@ -61,27 +139,40 @@ xmrig::IApiRequest::Method xmrig::HttpApiRequest::method() const } -void xmrig::HttpApiRequest::accept() -{ - using namespace rapidjson; - - ApiRequest::accept(); - - if (!m_parsed && !m_req.body.empty()) { - m_parsed = true; - m_body.Parse(m_req.body.c_str()); - - if (m_body.HasParseError()) { - reply().AddMember("error", StringRef(GetParseError_En(m_body.GetParseError())), doc().GetAllocator());; - } - } -} - - void xmrig::HttpApiRequest::done(int status) { ApiRequest::done(status); - m_res.setStatus(status); + if (type() == REQ_JSON_RPC) { + using namespace rapidjson; + auto &allocator = doc().GetAllocator(); + + m_res.setStatus(HTTP_STATUS_OK); + + if (status != HTTP_STATUS_OK) { + if (status == HTTP_STATUS_NOT_FOUND) { + status = RPC_METHOD_NOT_FOUND; + } + + Value error(kObjectType); + error.AddMember("code", status, allocator); + error.AddMember("message", StringRef(rpcError(status)), allocator); + + reply().AddMember(StringRef(kError), error, allocator); + } + else if (!reply().HasMember(kResult)) { + Value result(kObjectType); + result.AddMember("status", "OK", allocator); + + reply().AddMember(StringRef(kResult), result, allocator); + } + + reply().AddMember("jsonrpc", "2.0", allocator); + reply().AddMember(StringRef(kId), Value().CopyFrom(Json::getValue(json(), kId), allocator), allocator); + } + else { + m_res.setStatus(status); + } + m_res.end(); } diff --git a/src/base/api/requests/HttpApiRequest.h b/src/base/api/requests/HttpApiRequest.h index dc3eb037..309b5a6b 100644 --- a/src/base/api/requests/HttpApiRequest.h +++ b/src/base/api/requests/HttpApiRequest.h @@ -44,19 +44,20 @@ public: HttpApiRequest(const HttpData &req, bool restricted); protected: + inline bool hasParseError() const override { return m_parsed == 2; } + inline const String &url() const override { return m_url; } inline rapidjson::Document &doc() override { return m_res.doc(); } inline rapidjson::Value &reply() override { return m_res.doc(); } - inline const String &url() const override { return m_url; } + bool accept() override; const rapidjson::Value &json() const override; Method method() const override; - void accept() override; void done(int status) override; private: - bool m_parsed; const HttpData &m_req; HttpApiResponse m_res; + int m_parsed = 0; rapidjson::Document m_body; String m_url; }; diff --git a/src/core/Miner.cpp b/src/core/Miner.cpp index 801b27c7..26a5d7e7 100644 --- a/src/core/Miner.cpp +++ b/src/core/Miner.cpp @@ -477,5 +477,17 @@ void xmrig::Miner::onRequest(IApiRequest &request) d_ptr->getBackends(request.reply(), request.doc()); } } + else if (request.type() == IApiRequest::REQ_JSON_RPC) { + if (request.rpcMethod() == "pause") { + request.accept(); + + setEnabled(false); + } + else if (request.rpcMethod() == "resume") { + request.accept(); + + setEnabled(true); + } + } } #endif