2019-03-19 04:30:04 +01:00
|
|
|
#pragma once
|
|
|
|
|
2019-06-17 08:59:01 +02:00
|
|
|
#include <chrono>
|
2021-01-05 22:03:27 +01:00
|
|
|
#include <filesystem>
|
2019-03-19 04:30:04 +01:00
|
|
|
#include <iostream>
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
2020-08-08 07:21:33 +02:00
|
|
|
#include <optional>
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2021-01-05 18:30:04 +01:00
|
|
|
#include <nlohmann/json_fwd.hpp>
|
2019-03-19 04:30:04 +01:00
|
|
|
#include <boost/asio.hpp>
|
2019-05-31 03:32:52 +02:00
|
|
|
#include <boost/asio/ssl/stream.hpp>
|
2019-03-19 04:30:04 +01:00
|
|
|
#include <boost/beast/core.hpp>
|
|
|
|
#include <boost/beast/http.hpp>
|
|
|
|
#include <boost/beast/version.hpp>
|
2019-06-24 02:20:43 +02:00
|
|
|
#include <boost/format.hpp>
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2020-08-08 07:21:33 +02:00
|
|
|
#include "loki_common.h"
|
2019-12-03 20:58:02 +01:00
|
|
|
#include "lokid_key.h"
|
2020-03-10 02:54:45 +01:00
|
|
|
#include "swarm.h"
|
2019-04-02 05:28:10 +02:00
|
|
|
|
2019-05-06 03:43:59 +02:00
|
|
|
constexpr auto LOKI_SENDER_SNODE_PUBKEY_HEADER = "X-Loki-Snode-PubKey";
|
|
|
|
constexpr auto LOKI_SNODE_SIGNATURE_HEADER = "X-Loki-Snode-Signature";
|
2019-12-13 02:20:17 +01:00
|
|
|
constexpr auto LOKI_SENDER_KEY_HEADER = "X-Sender-Public-Key";
|
|
|
|
constexpr auto LOKI_TARGET_SNODE_KEY = "X-Target-Snode-Key";
|
2020-03-11 05:19:00 +01:00
|
|
|
constexpr auto LOKI_LONG_POLL_HEADER = "X-Loki-Long-Poll";
|
2019-05-06 03:43:59 +02:00
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
template <typename T>
|
|
|
|
class ChannelEncryption;
|
|
|
|
|
2019-05-20 09:21:01 +02:00
|
|
|
class RateLimiter;
|
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
namespace http = boost::beast::http; // from <boost/beast/http.hpp>
|
2019-05-31 03:32:52 +02:00
|
|
|
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
using request_t = http::request<http::string_body>;
|
2019-03-31 08:50:19 +02:00
|
|
|
using response_t = http::response<http::string_body>;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
namespace loki {
|
2019-12-18 04:34:40 +01:00
|
|
|
|
|
|
|
std::shared_ptr<request_t> build_post_request(const char* target,
|
|
|
|
std::string&& data);
|
|
|
|
|
2020-10-01 16:18:49 +02:00
|
|
|
class Security;
|
2019-04-12 08:04:19 +02:00
|
|
|
|
2020-03-06 07:27:15 +01:00
|
|
|
class RequestHandler;
|
|
|
|
class Response;
|
|
|
|
|
2019-06-26 06:42:45 +02:00
|
|
|
namespace storage {
|
|
|
|
struct Item;
|
|
|
|
}
|
|
|
|
|
|
|
|
using storage::Item;
|
|
|
|
|
2019-06-20 07:39:01 +02:00
|
|
|
enum class SNodeError { NO_ERROR, ERROR_OTHER, NO_REACH, HTTP_ERROR };
|
2019-04-08 04:20:48 +02:00
|
|
|
|
|
|
|
struct sn_response_t {
|
2019-04-10 09:15:45 +02:00
|
|
|
SNodeError error_code;
|
|
|
|
std::shared_ptr<std::string> body;
|
2020-08-08 07:21:33 +02:00
|
|
|
std::optional<response_t> raw_response;
|
2019-04-08 04:20:48 +02:00
|
|
|
};
|
|
|
|
|
2019-12-13 02:20:17 +01:00
|
|
|
template <typename OStream>
|
2020-03-10 02:54:45 +01:00
|
|
|
OStream& operator<<(OStream& os, const sn_response_t& res) {
|
2019-12-13 02:20:17 +01:00
|
|
|
switch (res.error_code) {
|
2020-03-10 02:54:45 +01:00
|
|
|
case SNodeError::NO_ERROR:
|
|
|
|
os << "NO_ERROR";
|
|
|
|
break;
|
|
|
|
case SNodeError::ERROR_OTHER:
|
|
|
|
os << "ERROR_OTHER";
|
|
|
|
break;
|
|
|
|
case SNodeError::NO_REACH:
|
|
|
|
os << "NO_REACH";
|
|
|
|
break;
|
|
|
|
case SNodeError::HTTP_ERROR:
|
|
|
|
os << "HTTP_ERROR";
|
|
|
|
break;
|
2019-12-13 02:20:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return os << "(" << (res.body ? *res.body : "n/a") << ")";
|
|
|
|
}
|
|
|
|
|
2019-05-23 07:38:40 +02:00
|
|
|
struct blockchain_test_answer_t {
|
|
|
|
uint64_t res_height;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Blockchain test parameters
|
|
|
|
struct bc_test_params_t {
|
|
|
|
uint64_t max_height;
|
|
|
|
uint64_t seed;
|
|
|
|
};
|
|
|
|
|
2019-04-08 04:20:48 +02:00
|
|
|
using http_callback_t = std::function<void(sn_response_t)>;
|
|
|
|
|
2019-06-21 08:30:26 +02:00
|
|
|
class LokidClient {
|
|
|
|
|
|
|
|
boost::asio::io_context& ioc_;
|
2019-12-03 20:55:16 +01:00
|
|
|
std::string lokid_rpc_ip_;
|
|
|
|
const uint16_t lokid_rpc_port_;
|
2019-06-21 08:30:26 +02:00
|
|
|
|
|
|
|
public:
|
2019-12-03 20:55:16 +01:00
|
|
|
LokidClient(boost::asio::io_context& ioc, std::string ip, uint16_t port);
|
2020-09-15 08:54:49 +02:00
|
|
|
void make_lokid_request(std::string_view method,
|
2019-06-21 08:30:26 +02:00
|
|
|
const nlohmann::json& params,
|
2019-07-05 08:15:47 +02:00
|
|
|
http_callback_t&& cb) const;
|
2019-09-25 08:51:05 +02:00
|
|
|
void make_custom_lokid_request(const std::string& daemon_ip,
|
|
|
|
const uint16_t daemon_port,
|
2020-09-15 08:54:49 +02:00
|
|
|
std::string_view method,
|
2019-09-25 08:51:05 +02:00
|
|
|
const nlohmann::json& params,
|
|
|
|
http_callback_t&& cb) const;
|
2020-03-10 02:54:45 +01:00
|
|
|
// Synchronously fetches the private key from lokid. Designed to be called
|
|
|
|
// *before* the io_context has been started (this runs it, waits for a
|
|
|
|
// successful fetch, then restarts it when finished).
|
|
|
|
std::tuple<private_key_t, private_key_ed25519_t, private_key_t>
|
|
|
|
wait_for_privkey();
|
2019-06-21 08:30:26 +02:00
|
|
|
};
|
|
|
|
|
2020-11-25 07:47:33 +01:00
|
|
|
constexpr auto SESSION_TIME_LIMIT = std::chrono::seconds(60);
|
2019-06-03 06:26:16 +02:00
|
|
|
|
2019-05-02 06:53:29 +02:00
|
|
|
void make_http_request(boost::asio::io_context& ioc, const std::string& ip,
|
2019-04-24 08:59:41 +02:00
|
|
|
uint16_t port, const std::shared_ptr<request_t>& req,
|
2019-04-08 08:59:02 +02:00
|
|
|
http_callback_t&& cb);
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
class HttpClientSession
|
|
|
|
: public std::enable_shared_from_this<HttpClientSession> {
|
|
|
|
|
|
|
|
using tcp = boost::asio::ip::tcp;
|
|
|
|
|
|
|
|
boost::asio::io_context& ioc_;
|
2019-04-10 07:57:40 +02:00
|
|
|
tcp::socket socket_;
|
|
|
|
tcp::endpoint endpoint_;
|
|
|
|
http_callback_t callback_;
|
|
|
|
boost::asio::steady_timer deadline_timer_;
|
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
boost::beast::flat_buffer buffer_;
|
2019-04-24 08:59:41 +02:00
|
|
|
/// NOTE: this needs to be a shared pointer since
|
|
|
|
/// it is very common for the same request to be
|
|
|
|
/// sent to multiple snodes
|
|
|
|
std::shared_ptr<request_t> req_;
|
2019-03-31 08:50:19 +02:00
|
|
|
response_t res_;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
bool used_callback_ = false;
|
2019-09-25 02:50:30 +02:00
|
|
|
bool needs_cleanup = true;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-04-10 07:57:40 +02:00
|
|
|
void on_connect();
|
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
void on_write(boost::system::error_code ec, std::size_t bytes_transferred);
|
|
|
|
|
|
|
|
void on_read(boost::system::error_code ec, std::size_t bytes_transferred);
|
|
|
|
|
2020-03-10 02:54:45 +01:00
|
|
|
void trigger_callback(SNodeError error,
|
|
|
|
std::shared_ptr<std::string>&& body);
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-09-25 02:50:30 +02:00
|
|
|
void clean_up();
|
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
public:
|
|
|
|
// Resolver and socket require an io_context
|
2019-04-10 07:57:40 +02:00
|
|
|
HttpClientSession(boost::asio::io_context& ioc, const tcp::endpoint& ep,
|
2019-05-01 04:28:55 +02:00
|
|
|
const std::shared_ptr<request_t>& req,
|
|
|
|
http_callback_t&& cb);
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-04-10 07:57:40 +02:00
|
|
|
// initiate the client connection
|
|
|
|
void start();
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
~HttpClientSession();
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace http_server {
|
|
|
|
|
|
|
|
class connection_t : public std::enable_shared_from_this<connection_t> {
|
|
|
|
|
|
|
|
using tcp = boost::asio::ip::tcp;
|
|
|
|
|
|
|
|
private:
|
|
|
|
boost::asio::io_context& ioc_;
|
2019-05-31 03:32:52 +02:00
|
|
|
ssl::context& ssl_ctx_;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
// The socket for the currently connected client.
|
|
|
|
tcp::socket socket_;
|
|
|
|
|
|
|
|
// The buffer for performing reads.
|
|
|
|
boost::beast::flat_buffer buffer_{8192};
|
2019-05-31 03:32:52 +02:00
|
|
|
ssl::stream<tcp::socket&> stream_;
|
2019-06-27 06:08:14 +02:00
|
|
|
const Security& security_;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-12-19 00:37:36 +01:00
|
|
|
// Contains the request message
|
|
|
|
http::request_parser<http::string_body> request_;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
// The response message.
|
2019-03-31 08:50:19 +02:00
|
|
|
response_t response_;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-04-09 09:17:04 +02:00
|
|
|
// whether the response should be sent asyncronously,
|
|
|
|
// as opposed to directly after connection_t::process_request
|
|
|
|
bool delay_response_ = false;
|
|
|
|
|
2020-03-06 07:27:15 +01:00
|
|
|
// TODO: remove SN, only use Reqeust Handler as a mediator
|
2019-04-23 05:09:26 +02:00
|
|
|
ServiceNode& service_node_;
|
|
|
|
|
2020-03-06 07:27:15 +01:00
|
|
|
RequestHandler& request_handler_;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-05-20 09:21:01 +02:00
|
|
|
RateLimiter& rate_limiter_;
|
|
|
|
|
2019-05-10 07:47:26 +02:00
|
|
|
// The timer for repeating an action within one connection
|
|
|
|
boost::asio::steady_timer repeat_timer_;
|
|
|
|
int repetition_count_ = 0;
|
2019-06-17 08:59:01 +02:00
|
|
|
std::chrono::time_point<std::chrono::steady_clock> start_timestamp_;
|
2019-05-10 07:47:26 +02:00
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
// The timer for putting a deadline on connection processing.
|
2019-04-09 09:17:04 +02:00
|
|
|
boost::asio::steady_timer deadline_;
|
|
|
|
|
2019-04-23 05:09:26 +02:00
|
|
|
/// TODO: move these if possible
|
|
|
|
std::map<std::string, std::string> header_;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-05-02 06:53:29 +02:00
|
|
|
std::stringstream body_stream_;
|
2019-03-19 04:30:04 +01:00
|
|
|
|
2019-04-12 08:04:19 +02:00
|
|
|
// Note that we are only sending a single message through the
|
|
|
|
// notification mechanism. If we somehow accumulated multiple
|
|
|
|
// messages before notification event happens (unlikely), the
|
|
|
|
// following messages will be delivered with the client's
|
|
|
|
// consequent (and immediate) retrieve request
|
|
|
|
struct notification_context_t {
|
|
|
|
// The timer used for internal db polling
|
|
|
|
boost::asio::steady_timer timer;
|
|
|
|
// the message is stored here momentarily; needed because
|
|
|
|
// we can't pass it using current notification mechanism
|
2020-08-08 07:21:33 +02:00
|
|
|
std::optional<message_t> message;
|
2019-07-05 08:49:26 +02:00
|
|
|
// Messenger public key that this connection is registered for
|
|
|
|
std::string pubkey;
|
|
|
|
};
|
|
|
|
|
2020-08-08 07:21:33 +02:00
|
|
|
std::optional<notification_context_t> notification_ctx_;
|
2019-04-12 08:04:19 +02:00
|
|
|
|
2019-12-13 02:20:17 +01:00
|
|
|
// If present, this function will be called just before
|
|
|
|
// writing the response
|
2020-08-08 07:21:33 +02:00
|
|
|
std::function<void(response_t&)> response_modifier_;
|
2019-12-13 02:20:17 +01:00
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
public:
|
2019-05-31 03:32:52 +02:00
|
|
|
connection_t(boost::asio::io_context& ioc, ssl::context& ssl_ctx,
|
2020-03-06 07:27:15 +01:00
|
|
|
tcp::socket socket, ServiceNode& sn, RequestHandler& rh,
|
2019-06-27 06:08:14 +02:00
|
|
|
RateLimiter& rate_limiter, const Security& security);
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
~connection_t();
|
|
|
|
|
2019-07-05 08:49:26 +02:00
|
|
|
// Connection index, mainly used for debugging
|
|
|
|
uint64_t conn_idx;
|
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
/// Initiate the asynchronous operations associated with the connection.
|
|
|
|
void start();
|
|
|
|
|
2020-08-08 07:21:33 +02:00
|
|
|
void notify(const message_t* msg);
|
2019-04-16 07:42:36 +02:00
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
private:
|
2019-05-31 03:32:52 +02:00
|
|
|
void do_handshake();
|
|
|
|
void on_handshake(boost::system::error_code ec);
|
2019-03-19 04:30:04 +01:00
|
|
|
/// Asynchronously receive a complete request message.
|
|
|
|
void read_request();
|
|
|
|
|
2019-05-31 03:32:52 +02:00
|
|
|
void do_close();
|
|
|
|
void on_shutdown(boost::system::error_code ec);
|
|
|
|
|
2019-06-26 06:42:45 +02:00
|
|
|
/// process GET /get_stats/v1
|
2019-06-18 06:54:32 +02:00
|
|
|
void on_get_stats();
|
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
/// Determine what needs to be done with the request message
|
|
|
|
/// (synchronously).
|
|
|
|
void process_request();
|
|
|
|
|
2019-10-25 02:28:19 +02:00
|
|
|
/// Unsubscribe listener (if any) and shutdown the connection
|
|
|
|
void clean_up();
|
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
/// Asynchronously transmit the response message.
|
|
|
|
void write_response();
|
|
|
|
|
|
|
|
/// Syncronously (?) process client store/load requests
|
2019-12-13 02:20:17 +01:00
|
|
|
void process_client_req_rate_limited();
|
|
|
|
|
2020-09-15 08:54:49 +02:00
|
|
|
void process_swarm_req(std::string_view target);
|
2019-06-21 04:26:17 +02:00
|
|
|
|
2020-09-21 09:04:38 +02:00
|
|
|
/// Process onion request from the client (json)
|
|
|
|
void process_onion_req_v1();
|
|
|
|
|
|
|
|
/// Process onion request from the client (binary)
|
|
|
|
void process_onion_req_v2();
|
2020-03-06 07:27:15 +01:00
|
|
|
|
2019-12-18 04:34:40 +01:00
|
|
|
void process_proxy_req();
|
|
|
|
|
|
|
|
void process_file_proxy_req();
|
2019-12-13 02:20:17 +01:00
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
// Check whether we have spent enough time on this connection.
|
|
|
|
void register_deadline();
|
|
|
|
|
2019-05-23 07:38:40 +02:00
|
|
|
/// Process storage test request and repeat if necessary
|
|
|
|
void process_storage_test_req(uint64_t height,
|
2019-05-15 03:20:22 +02:00
|
|
|
const std::string& tester_addr,
|
|
|
|
const std::string& msg_hash);
|
2019-05-06 03:43:59 +02:00
|
|
|
|
2019-09-03 08:45:04 +02:00
|
|
|
void process_blockchain_test_req(uint64_t height,
|
|
|
|
const std::string& tester_pk,
|
|
|
|
bc_test_params_t params);
|
|
|
|
|
2020-03-06 07:27:15 +01:00
|
|
|
void set_response(const Response& res);
|
|
|
|
|
2019-05-06 03:43:59 +02:00
|
|
|
bool parse_header(const char* key);
|
|
|
|
|
|
|
|
template <typename... Args>
|
|
|
|
bool parse_header(const char* first, Args... args);
|
2019-04-03 03:08:36 +02:00
|
|
|
|
2019-05-20 09:21:01 +02:00
|
|
|
bool validate_snode_request();
|
2019-03-19 04:30:04 +01:00
|
|
|
};
|
|
|
|
|
2019-07-02 05:20:53 +02:00
|
|
|
void run(boost::asio::io_context& ioc, const std::string& ip, uint16_t port,
|
2021-01-05 22:03:27 +01:00
|
|
|
const std::filesystem::path& base_path, ServiceNode& sn,
|
2020-03-10 02:54:45 +01:00
|
|
|
RequestHandler& rh, RateLimiter& rate_limiter, Security&);
|
2019-03-19 04:30:04 +01:00
|
|
|
|
|
|
|
} // namespace http_server
|
|
|
|
|
2020-03-10 02:54:45 +01:00
|
|
|
constexpr const char* error_string(SNodeError err) {
|
2019-12-03 20:58:02 +01:00
|
|
|
switch (err) {
|
2020-03-10 02:54:45 +01:00
|
|
|
case loki::SNodeError::NO_ERROR:
|
|
|
|
return "NO_ERROR";
|
|
|
|
case loki::SNodeError::ERROR_OTHER:
|
|
|
|
return "ERROR_OTHER";
|
|
|
|
case loki::SNodeError::NO_REACH:
|
|
|
|
return "NO_REACH";
|
|
|
|
case loki::SNodeError::HTTP_ERROR:
|
|
|
|
return "HTTP_ERROR";
|
|
|
|
default:
|
|
|
|
return "[UNKNOWN]";
|
2019-12-03 20:58:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-21 09:04:38 +02:00
|
|
|
struct CiphertextPlusJson {
|
|
|
|
std::string ciphertext;
|
|
|
|
std::string json;
|
|
|
|
};
|
|
|
|
|
|
|
|
// TODO: move this from http_connection.h after refactoring
|
|
|
|
auto parse_combined_payload(const std::string& payload) -> CiphertextPlusJson;
|
|
|
|
|
2019-03-19 04:30:04 +01:00
|
|
|
} // namespace loki
|
2019-08-26 09:29:38 +02:00
|
|
|
|
|
|
|
namespace fmt {
|
|
|
|
|
2019-09-02 09:40:17 +02:00
|
|
|
template <>
|
|
|
|
struct formatter<loki::SNodeError> {
|
2019-08-26 09:29:38 +02:00
|
|
|
|
|
|
|
template <typename ParseContext>
|
2019-09-02 09:40:17 +02:00
|
|
|
constexpr auto parse(ParseContext& ctx) {
|
|
|
|
return ctx.begin();
|
|
|
|
}
|
2019-08-26 09:29:38 +02:00
|
|
|
|
|
|
|
template <typename FormatContext>
|
2019-09-02 09:40:17 +02:00
|
|
|
auto format(const loki::SNodeError& err, FormatContext& ctx) {
|
2019-12-03 20:58:02 +01:00
|
|
|
return format_to(ctx.out(), error_string(err));
|
2019-08-26 09:29:38 +02:00
|
|
|
}
|
2019-09-02 09:40:17 +02:00
|
|
|
};
|
2019-08-26 09:29:38 +02:00
|
|
|
|
2019-09-02 09:40:17 +02:00
|
|
|
} // namespace fmt
|