diff --git a/src/base/net/http/HttpClient.cpp b/src/base/net/http/HttpClient.cpp index 113e2f139..2699e6631 100644 --- a/src/base/net/http/HttpClient.cpp +++ b/src/base/net/http/HttpClient.cpp @@ -78,9 +78,7 @@ private: xmrig::HttpClient::HttpClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size) : - HttpContext(HTTP_RESPONSE, listener), - m_quiet(false), - m_port(0) + HttpContext(HTTP_RESPONSE, listener) { this->method = method; this->url = url; @@ -127,7 +125,7 @@ void xmrig::HttpClient::onResolved(const Dns &dns, int status) sockaddr *addr = dns.get().addr(m_port); - uv_connect_t *req = new uv_connect_t; + auto req = new uv_connect_t; req->data = this; uv_tcp_connect(req, m_tcp, addr, onConnect); @@ -140,7 +138,7 @@ void xmrig::HttpClient::handshake() headers.insert({ "Connection", "close" }); headers.insert({ "User-Agent", Platform::userAgent() }); - if (body.size()) { + if (!body.empty()) { headers.insert({ "Content-Length", std::to_string(body.size()) }); } @@ -169,14 +167,14 @@ void xmrig::HttpClient::read(const char *data, size_t size) void xmrig::HttpClient::write(const std::string &header) { - ClientWriteBaton *baton = new ClientWriteBaton(header, std::move(body)); + auto baton = new ClientWriteBaton(header, std::move(body)); uv_write(&baton->req, stream(), baton->bufs, baton->count(), ClientWriteBaton::onWrite); } void xmrig::HttpClient::onConnect(uv_connect_t *req, int status) { - HttpClient *client = static_cast(req->data); + auto client = static_cast(req->data); if (!client) { delete req; return; @@ -205,7 +203,7 @@ void xmrig::HttpClient::onConnect(uv_connect_t *req, int status) }, [](uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf) { - HttpClient *client = static_cast(tcp->data); + auto client = static_cast(tcp->data); if (nread >= 0) { client->read(buf->base, static_cast(nread)); diff --git a/src/base/net/http/HttpClient.h b/src/base/net/http/HttpClient.h index c5dfc43d9..acf873dc7 100644 --- a/src/base/net/http/HttpClient.h +++ b/src/base/net/http/HttpClient.h @@ -30,6 +30,7 @@ #include "base/net/http/HttpContext.h" #include "base/kernel/interfaces/IDnsListener.h" +#include "base/tools/Object.h" namespace xmrig { @@ -41,6 +42,8 @@ class String; class HttpClient : public HttpContext, public IDnsListener { public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(HttpClient); + HttpClient(int method, const String &url, IHttpListener *listener, const char *data = nullptr, size_t size = 0); ~HttpClient() override; @@ -57,13 +60,13 @@ protected: virtual void read(const char *data, size_t size); virtual void write(const std::string &header); - bool m_quiet; + bool m_quiet = false; private: static void onConnect(uv_connect_t *req, int status); Dns *m_dns; - uint16_t m_port; + uint16_t m_port = 0; }; diff --git a/src/base/net/http/HttpContext.cpp b/src/base/net/http/HttpContext.cpp index e97f989b2..1130d12c5 100644 --- a/src/base/net/http/HttpContext.cpp +++ b/src/base/net/http/HttpContext.cpp @@ -136,7 +136,7 @@ void xmrig::HttpContext::closeAll() int xmrig::HttpContext::onHeaderField(http_parser *parser, const char *at, size_t length) { - HttpContext *ctx = static_cast(parser->data); + auto ctx = static_cast(parser->data); if (ctx->m_wasHeaderValue) { if (!ctx->m_lastHeaderField.empty()) { @@ -155,7 +155,7 @@ int xmrig::HttpContext::onHeaderField(http_parser *parser, const char *at, size_ int xmrig::HttpContext::onHeaderValue(http_parser *parser, const char *at, size_t length) { - HttpContext *ctx = static_cast(parser->data); + auto ctx = static_cast(parser->data); if (!ctx->m_wasHeaderValue) { ctx->m_lastHeaderValue = std::string(at, length); @@ -185,7 +185,7 @@ void xmrig::HttpContext::attach(http_parser_settings *settings) settings->on_header_value = onHeaderValue; settings->on_headers_complete = [](http_parser* parser) -> int { - HttpContext *ctx = static_cast(parser->data); + auto ctx = static_cast(parser->data); ctx->status = parser->status_code; if (parser->type == HTTP_REQUEST) { @@ -208,7 +208,7 @@ void xmrig::HttpContext::attach(http_parser_settings *settings) settings->on_message_complete = [](http_parser *parser) -> int { - HttpContext *ctx = static_cast(parser->data); + auto ctx = static_cast(parser->data); ctx->m_listener->onHttpData(*ctx); ctx->m_listener = nullptr; diff --git a/src/base/net/http/HttpContext.h b/src/base/net/http/HttpContext.h index fbb453aa9..ba4418b61 100644 --- a/src/base/net/http/HttpContext.h +++ b/src/base/net/http/HttpContext.h @@ -28,15 +28,16 @@ #define XMRIG_HTTPCONTEXT_H -typedef struct http_parser http_parser; -typedef struct http_parser_settings http_parser_settings; -typedef struct uv_connect_s uv_connect_t; -typedef struct uv_handle_s uv_handle_t; -typedef struct uv_stream_s uv_stream_t; -typedef struct uv_tcp_s uv_tcp_t; +using http_parser = struct http_parser; +using http_parser_settings = struct http_parser_settings; +using uv_connect_t = struct uv_connect_s; +using uv_handle_t = struct uv_handle_s; +using uv_stream_t = struct uv_stream_s; +using uv_tcp_t = struct uv_tcp_s; #include "base/net/http/HttpData.h" +#include "base/tools/Object.h" namespace xmrig { @@ -48,6 +49,8 @@ class IHttpListener; class HttpContext : public HttpData { public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(HttpContext) + HttpContext(int parser_type, IHttpListener *listener); virtual ~HttpContext(); diff --git a/src/base/net/http/HttpsClient.cpp b/src/base/net/http/HttpsClient.cpp index 2c2873309..747aeb31f 100644 --- a/src/base/net/http/HttpsClient.cpp +++ b/src/base/net/http/HttpsClient.cpp @@ -24,7 +24,7 @@ */ -#include +#include #include #include diff --git a/src/base/net/http/HttpsClient.h b/src/base/net/http/HttpsClient.h index c6a228099..a0de150e5 100644 --- a/src/base/net/http/HttpsClient.h +++ b/src/base/net/http/HttpsClient.h @@ -28,10 +28,10 @@ #define XMRIG_HTTPSCLIENT_H -typedef struct bio_st BIO; -typedef struct ssl_ctx_st SSL_CTX; -typedef struct ssl_st SSL; -typedef struct x509_st X509; +using BIO = struct bio_st; +using SSL_CTX = struct ssl_ctx_st; +using SSL = struct ssl_st; +using X509 = struct x509_st; #include "base/net/http/HttpClient.h" @@ -44,6 +44,8 @@ namespace xmrig { class HttpsClient : public HttpClient { public: + XMRIG_DISABLE_COPY_MOVE_DEFAULT(HttpsClient) + HttpsClient(int method, const String &url, IHttpListener *listener, const char *data, size_t size, const String &fingerprint); ~HttpsClient() override; diff --git a/src/base/net/stratum/SelfSelectClient.cpp b/src/base/net/stratum/SelfSelectClient.cpp index 3b6034a7e..ae82dfafb 100644 --- a/src/base/net/stratum/SelfSelectClient.cpp +++ b/src/base/net/stratum/SelfSelectClient.cpp @@ -73,6 +73,20 @@ xmrig::SelfSelectClient::~SelfSelectClient() } +void xmrig::SelfSelectClient::tick(uint64_t now) +{ + m_client->tick(now); + + if (m_state == RetryState) { + if (Chrono::steadyMSecs() - m_timestamp < m_retryPause) { + return; + } + + getBlockTemplate(); + } +} + + void xmrig::SelfSelectClient::onJobReceived(IClient *, const Job &job, const rapidjson::Value &) { m_job = job; @@ -96,7 +110,7 @@ bool xmrig::SelfSelectClient::parseResponse(int64_t id, rapidjson::Value &result } if (error.IsObject()) { - LOG_ERR("[%s:%d] error: " RED_BOLD("\"%s\"") RED_S ", code: %d", pool().daemon().host().data(), pool().daemon().port(), Json::getString(error, "message"), Json::getInt(error, "code")); + LOG_ERR("[%s] error: " RED_BOLD("\"%s\"") RED_S ", code: %d", pool().daemon().url().data(), Json::getString(error, "message"), Json::getInt(error, "code")); return false; } @@ -107,7 +121,7 @@ bool xmrig::SelfSelectClient::parseResponse(int64_t id, rapidjson::Value &result for (auto field : required_fields) { if (!result.HasMember(field)) { - LOG_ERR("[%s:%d] required field " RED_BOLD("\"%s\"") RED_S " not found", pool().daemon().host().data(), pool().daemon().port(), field); + LOG_ERR("[%s] required field " RED_BOLD("\"%s\"") RED_S " not found", pool().daemon().url().data(), field); return false; } @@ -122,14 +136,14 @@ bool xmrig::SelfSelectClient::parseResponse(int64_t id, rapidjson::Value &result submitBlockTemplate(result); - - return true; } void xmrig::SelfSelectClient::getBlockTemplate() { + setState(WaitState); + using namespace rapidjson; Document doc(kObjectType); auto &allocator = doc.GetAllocator(); @@ -138,7 +152,7 @@ void xmrig::SelfSelectClient::getBlockTemplate() params.AddMember("wallet_address", m_job.poolWallet().toJSON(), allocator); params.AddMember("extra_nonce", m_job.extraNonce().toJSON(), allocator); - JsonRequest::create(doc, sequence(), "getblocktemplate", params); + JsonRequest::create(doc, m_sequence++, "getblocktemplate", params); send(HTTP_POST, "/json_rpc", doc); } @@ -146,15 +160,14 @@ void xmrig::SelfSelectClient::getBlockTemplate() void xmrig::SelfSelectClient::retry() { - // FIXME + setState(RetryState); } void xmrig::SelfSelectClient::send(int method, const char *url, const char *data, size_t size) { - LOG_DEBUG("[%s:%d] " MAGENTA_BOLD("\"%s %s\"") BLACK_BOLD_S " send (%zu bytes): \"%.*s\"", - pool().daemon().host().data(), - pool().daemon().port(), + LOG_DEBUG("[%s] " MAGENTA_BOLD("\"%s %s\"") BLACK_BOLD_S " send (%zu bytes): \"%.*s\"", + pool().daemon().url().data(), http_method_str(static_cast(method)), url, size, @@ -172,7 +185,7 @@ void xmrig::SelfSelectClient::send(int method, const char *url, const char *data client = new HttpClient(method, url, this, data, size); } - client->setQuiet(m_quiet); + client->setQuiet(isQuiet()); client->connect(pool().daemon().host(), pool().daemon().port()); } @@ -189,6 +202,37 @@ void xmrig::SelfSelectClient::send(int method, const char *url, const rapidjson: } +void xmrig::SelfSelectClient::setState(State state) +{ + if (m_state == state) { + return; + } + + switch (state) { + case IdleState: + m_timestamp = 0; + m_failures = 0; + break; + + case WaitState: + m_timestamp = Chrono::steadyMSecs(); + break; + + case RetryState: + m_timestamp = Chrono::steadyMSecs(); + + if (m_failures > m_retries) { + m_listener->onClose(this, static_cast(m_failures)); + } + + m_failures++; + break; + } + + m_state = state; +} + + void xmrig::SelfSelectClient::submitBlockTemplate(rapidjson::Value &result) { using namespace rapidjson; @@ -196,18 +240,35 @@ void xmrig::SelfSelectClient::submitBlockTemplate(rapidjson::Value &result) auto &allocator = doc.GetAllocator(); Value params(kObjectType); - params.AddMember(StringRef(kId), m_job.clientId().toJSON(), allocator); - params.AddMember(StringRef(kJobId), m_job.id().toJSON(), allocator); - params.AddMember(StringRef(kBlob), result[kBlocktemplateBlob], allocator); - params.AddMember(StringRef(kHeight), m_job.height(), allocator); - params.AddMember(StringRef(kDifficulty), result[kDifficulty], allocator); - params.AddMember(StringRef(kPrevHash), result[kPrevHash], allocator); - params.AddMember(StringRef(kSeedHash), result[kSeedHash], allocator); + params.AddMember(StringRef(kId), m_job.clientId().toJSON(), allocator); + params.AddMember(StringRef(kJobId), m_job.id().toJSON(), allocator); + params.AddMember(StringRef(kBlob), result[kBlocktemplateBlob], allocator); + params.AddMember(StringRef(kHeight), m_job.height(), allocator); + params.AddMember(StringRef(kDifficulty), result[kDifficulty], allocator); + params.AddMember(StringRef(kPrevHash), result[kPrevHash], allocator); + params.AddMember(StringRef(kSeedHash), result[kSeedHash], allocator); params.AddMember(StringRef(kNextSeedHash), result[kNextSeedHash], allocator); JsonRequest::create(doc, sequence(), "block_template", params); send(doc, [this](const rapidjson::Value &result, bool success, uint64_t elapsed) { + if (!success) { + if (!isQuiet()) { + LOG_ERR("[%s] error: " RED_BOLD("\"%s\"") RED_S ", code: %d", pool().daemon().url().data(), Json::getString(result, "message"), Json::getInt(result, "code")); + } + + return retry(); + } + + if (!m_active) { + return; + } + + if (m_failures > m_retries) { + m_listener->onLoginSuccess(this); + } + + setState(IdleState); m_listener->onJobReceived(this, m_job, rapidjson::Value{}); }); } @@ -219,18 +280,23 @@ void xmrig::SelfSelectClient::onHttpData(const HttpData &data) return retry(); } - LOG_DEBUG("[%s:%d] received (%d bytes): \"%.*s\"", pool().daemon().host().data(), pool().daemon().port(), static_cast(data.body.size()), static_cast(data.body.size()), data.body.c_str()); + LOG_DEBUG("[%s] received (%d bytes): \"%.*s\"", pool().daemon().url().data(), static_cast(data.body.size()), static_cast(data.body.size()), data.body.c_str()); rapidjson::Document doc; if (doc.Parse(data.body.c_str()).HasParseError()) { - if (!m_quiet) { - LOG_ERR("[%s:%d] JSON decode failed: \"%s\"", pool().daemon().host().data(), pool().daemon().port(), rapidjson::GetParseError_En(doc.GetParseError())); + if (!isQuiet()) { + LOG_ERR("[%s] JSON decode failed: \"%s\"", pool().daemon().url().data(), rapidjson::GetParseError_En(doc.GetParseError())); } return retry(); } - if (!parseResponse(Json::getInt64(doc, "id", -1), doc["result"], Json::getObject(doc, "error"))) { + const int64_t id = Json::getInt64(doc, "id", -1); + if (id > 0 && m_sequence - id != 1) { + return; + } + + if (!parseResponse(id, doc["result"], Json::getObject(doc, "error"))) { retry(); } } diff --git a/src/base/net/stratum/SelfSelectClient.h b/src/base/net/stratum/SelfSelectClient.h index d8bc5585e..c48c50782 100644 --- a/src/base/net/stratum/SelfSelectClient.h +++ b/src/base/net/stratum/SelfSelectClient.h @@ -69,13 +69,14 @@ protected: inline void setEnabled(bool enabled) override { m_client->setEnabled(enabled); } inline void setPool(const Pool &pool) override { m_client->setPool(pool); } inline void setQuiet(bool quiet) override { m_client->setQuiet(quiet); m_quiet = quiet; } - inline void setRetries(int retries) override { m_client->setRetries(retries); } - inline void setRetryPause(uint64_t ms) override { m_client->setRetryPause(ms); } - inline void tick(uint64_t now) override { m_client->tick(now); } + inline void setRetries(int retries) override { m_client->setRetries(retries); m_retries = retries; } + inline void setRetryPause(uint64_t ms) override { m_client->setRetryPause(ms); m_retryPause = ms; } + + void tick(uint64_t now) override; // IClientListener - inline void onClose(IClient *, int failures) override { m_listener->onClose(this, failures); } - inline void onLoginSuccess(IClient *) override { m_listener->onLoginSuccess(this); } + inline void onClose(IClient *, int failures) override { m_listener->onClose(this, failures); setState(IdleState); m_active = false; } + inline void onLoginSuccess(IClient *) override { m_listener->onLoginSuccess(this); setState(IdleState); m_active = true; } inline void onResultAccepted(IClient *, const SubmitResult &result, const char *error) override { m_listener->onResultAccepted(this, result, error); } inline void onVerifyAlgorithm(const IClient *, const Algorithm &algorithm, bool *ok) override { m_listener->onVerifyAlgorithm(this, algorithm, ok); } @@ -86,17 +87,33 @@ protected: void onHttpData(const HttpData &data) override; private: + enum State { + IdleState, + WaitState, + RetryState + }; + + inline bool isQuiet() const { return m_quiet || m_failures >= m_retries; } + bool parseResponse(int64_t id, rapidjson::Value &result, const rapidjson::Value &error); void getBlockTemplate(); void retry(); void send(int method, const char *url, const char *data = nullptr, size_t size = 0); void send(int method, const char *url, const rapidjson::Document &doc); + void setState(State state); void submitBlockTemplate(rapidjson::Value &result); - bool m_quiet = false; + bool m_active = false; + bool m_quiet = false; IClient *m_client; IClientListener *m_listener; + int m_retries = 5; + int64_t m_failures = 0; + int64_t m_sequence = 1; Job m_job; + State m_state = IdleState; + uint64_t m_retryPause = 5000; + uint64_t m_timestamp = 0; };