From 3f1eefb131656f647e160a8433189de43622ec7c Mon Sep 17 00:00:00 2001 From: XMRig Date: Fri, 29 Mar 2019 17:14:30 +0700 Subject: [PATCH] HTTP layer ready for API calls. --- src/api/Httpd.cpp | 62 +++++++++++++++++++++-- src/api/Httpd.h | 2 + src/base/base.cmake | 2 + src/base/net/http/Http.h | 1 + src/base/net/http/HttpApiResponse.cpp | 73 +++++++++++++++++++++++++++ src/base/net/http/HttpApiResponse.h | 57 +++++++++++++++++++++ src/base/net/http/HttpContext.cpp | 4 +- src/base/net/http/HttpRequest.h | 8 +-- 8 files changed, 198 insertions(+), 11 deletions(-) create mode 100644 src/base/net/http/HttpApiResponse.cpp create mode 100644 src/base/net/http/HttpApiResponse.h diff --git a/src/api/Httpd.cpp b/src/api/Httpd.cpp index ea5bd252..2e3194e6 100644 --- a/src/api/Httpd.cpp +++ b/src/api/Httpd.cpp @@ -23,16 +23,25 @@ */ +#include "3rdparty/http-parser/http_parser.h" #include "api/Httpd.h" #include "base/io/log/Log.h" #include "base/net/http/HttpRequest.h" -#include "base/net/http/HttpResponse.h" +#include "base/net/http/HttpApiResponse.h" #include "base/net/http/HttpServer.h" #include "base/net/tools/TcpServer.h" #include "core/Config.h" #include "core/Controller.h" +namespace xmrig { + +static const char *kAuthorization = "authorization"; +static const char *kContentType = "content-type"; + +} // namespace xmrig + + xmrig::Httpd::Httpd(Controller *controller) : m_controller(controller), m_http(nullptr), @@ -104,9 +113,52 @@ void xmrig::Httpd::onConfigChanged(Config *config, Config *previousConfig) void xmrig::Httpd::onHttpRequest(const HttpRequest &req) { - HttpResponse res(req.id()); - res.setStatus(200); + if (req.method == HTTP_OPTIONS) { + return HttpApiResponse(req.id()).end(); + } - LOG_INFO(GREEN_BOLD_S "OK"); - res.end("{}"); + if (req.method > 4) { + return HttpApiResponse(req.id(), HTTP_STATUS_METHOD_NOT_ALLOWED).end(); + } + + const int status = auth(req); + if (status != HTTP_STATUS_OK) { + return HttpApiResponse(req.id(), status).end(); + } + + if (req.method != HTTP_GET) { + if (m_controller->config()->http().isRestricted()) { + return HttpApiResponse(req.id(), HTTP_STATUS_FORBIDDEN).end(); + } + + if (!req.headers.count(kContentType) || req.headers.at(kContentType) != "application/json") { + return HttpApiResponse(req.id(), HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE).end(); + } + } + + HttpApiResponse res(req.id()); + res.end(); +} + + +int xmrig::Httpd::auth(const HttpRequest &req) const +{ + const Http &config = m_controller->config()->http(); + + if (!req.headers.count(kAuthorization)) { + return config.isAuthRequired() ? HTTP_STATUS_UNAUTHORIZED : HTTP_STATUS_OK; + } + + if (config.token().isNull()) { + return HTTP_STATUS_UNAUTHORIZED; + } + + const std::string &token = req.headers.at(kAuthorization); + const size_t size = token.size(); + + if (token.size() < 8 || config.token().size() != size - 7 || memcmp("Bearer ", token.c_str(), 7) != 0) { + return HTTP_STATUS_FORBIDDEN; + } + + return strncmp(config.token().data(), token.c_str() + 7, config.token().size()) == 0 ? HTTP_STATUS_OK : HTTP_STATUS_FORBIDDEN; } diff --git a/src/api/Httpd.h b/src/api/Httpd.h index 6a6a1bae..99ee2e88 100644 --- a/src/api/Httpd.h +++ b/src/api/Httpd.h @@ -55,6 +55,8 @@ protected: void onHttpRequest(const HttpRequest &req) override; private: + int auth(const HttpRequest &req) const; + Controller *m_controller; HttpServer *m_http; TcpServer *m_server; diff --git a/src/base/base.cmake b/src/base/base.cmake index 5bc3fd2d..09c28d0e 100644 --- a/src/base/base.cmake +++ b/src/base/base.cmake @@ -87,6 +87,7 @@ if (WITH_HTTPD) src/3rdparty/http-parser/http_parser.h src/base/kernel/interfaces/IHttpListener.h src/base/kernel/interfaces/ITcpServerListener.h + src/base/net/http/HttpApiResponse.h src/base/net/http/HttpContext.h src/base/net/http/HttpRequest.h src/base/net/http/HttpResponse.h @@ -96,6 +97,7 @@ if (WITH_HTTPD) set(SOURCES_BASE_HTTP src/3rdparty/http-parser/http_parser.c + src/base/net/http/HttpApiResponse.cpp src/base/net/http/HttpContext.cpp src/base/net/http/HttpResponse.cpp src/base/net/http/HttpServer.cpp diff --git a/src/base/net/http/Http.h b/src/base/net/http/Http.h index f8aa15bb..21eb581a 100644 --- a/src/base/net/http/Http.h +++ b/src/base/net/http/Http.h @@ -38,6 +38,7 @@ class Http public: Http(); + inline bool isAuthRequired() const { return m_restricted == false || !m_token.isNull(); } inline bool isEnabled() const { return m_enabled; } inline bool isRestricted() const { return m_restricted; } inline const String &host() const { return m_host; } diff --git a/src/base/net/http/HttpApiResponse.cpp b/src/base/net/http/HttpApiResponse.cpp new file mode 100644 index 00000000..56dd9bb8 --- /dev/null +++ b/src/base/net/http/HttpApiResponse.cpp @@ -0,0 +1,73 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 3 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, see . + */ + + +#include "3rdparty/http-parser/http_parser.h" +#include "base/net/http/HttpApiResponse.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/stringbuffer.h" + + +xmrig::HttpApiResponse::HttpApiResponse(uint64_t id) : + HttpResponse(id), + m_doc(rapidjson::kObjectType) +{ +} + + +xmrig::HttpApiResponse::HttpApiResponse(uint64_t id, int status) : + HttpResponse(id), + m_doc(rapidjson::kObjectType) +{ + setStatus(status); +} + + +void xmrig::HttpApiResponse::end() +{ + using namespace rapidjson; + + setHeader("Access-Control-Allow-Origin", "*"); + setHeader("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE"); + setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type"); + + if (statusCode() >= 400 && !m_doc.MemberCount()) { + m_doc.AddMember("status", statusCode(), m_doc.GetAllocator()); + m_doc.AddMember("error", StringRef(http_status_str(static_cast(statusCode()))), m_doc.GetAllocator()); + } + + if (!m_doc.MemberCount()) { + return HttpResponse::end(); + } + + setHeader("Content-Type", "application/json"); + + StringBuffer buffer(nullptr, 4096); + PrettyWriter writer(buffer); + writer.SetMaxDecimalPlaces(10); + m_doc.Accept(writer); + + HttpResponse::end(buffer.GetString(), buffer.GetSize()); +} diff --git a/src/base/net/http/HttpApiResponse.h b/src/base/net/http/HttpApiResponse.h new file mode 100644 index 00000000..bbaf132e --- /dev/null +++ b/src/base/net/http/HttpApiResponse.h @@ -0,0 +1,57 @@ +/* XMRig + * Copyright 2010 Jeff Garzik + * Copyright 2012-2014 pooler + * Copyright 2014 Lucas Jones + * Copyright 2014-2016 Wolf9466 + * Copyright 2016 Jay D Dee + * Copyright 2017-2018 XMR-Stak , + * Copyright 2014-2019 heapwolf + * Copyright 2018-2019 SChernykh + * Copyright 2016-2019 XMRig , + * + * 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 3 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, see . + */ + + +#ifndef XMRIG_HTTPAPIRESPONSE_H +#define XMRIG_HTTPAPIRESPONSE_H + + +#include "base/net/http/HttpResponse.h" +#include "rapidjson/document.h" + + +namespace xmrig { + + +class HttpApiResponse : public HttpResponse +{ +public: + HttpApiResponse(uint64_t id); + HttpApiResponse(uint64_t id, int status); + + inline rapidjson::Document &doc() { return m_doc; } + + void end(); + +private: + rapidjson::Document m_doc; +}; + + +} // namespace xmrig + + +#endif // XMRIG_HTTPAPIRESPONSE_H + diff --git a/src/base/net/http/HttpContext.cpp b/src/base/net/http/HttpContext.cpp index 29dfde7a..e0a1e83c 100644 --- a/src/base/net/http/HttpContext.cpp +++ b/src/base/net/http/HttpContext.cpp @@ -94,7 +94,7 @@ void xmrig::HttpContext::attach(http_parser_settings *settings) settings->on_headers_complete = [](http_parser* parser) -> int { HttpContext *ctx = static_cast(parser->data); - ctx->method = std::string(http_method_str(static_cast(parser->method))); + ctx->method = parser->method; if (!ctx->m_lastHeaderField.empty()) { ctx->setHeader(); @@ -105,7 +105,7 @@ void xmrig::HttpContext::attach(http_parser_settings *settings) settings->on_body = [](http_parser *parser, const char *at, size_t len) -> int { - static_cast(parser->data)->body << std::string(at, len); + static_cast(parser->data)->body += std::string(at, len); return 0; }; diff --git a/src/base/net/http/HttpRequest.h b/src/base/net/http/HttpRequest.h index 25f77aa5..b97f0b8d 100644 --- a/src/base/net/http/HttpRequest.h +++ b/src/base/net/http/HttpRequest.h @@ -39,14 +39,14 @@ namespace xmrig { class HttpRequest { public: - inline HttpRequest(uint64_t id) : m_id(id) {} + inline HttpRequest(uint64_t id) : method(0), m_id(id) {} inline uint64_t id() const { return m_id; } - std::string url; - std::string method; - std::stringstream body; + int method; std::map headers; + std::string body; + std::string url; private: const uint64_t m_id;