mirror of
https://github.com/lux-tel/libsession-util.git
synced 2026-10-30 20:25:11 +00:00
231 lines
8.4 KiB
C++
231 lines
8.4 KiB
C++
#pragma once
|
|
|
|
#include <atomic>
|
|
#include <deque>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "session/network/backends/session_file_server.hpp"
|
|
#include "session/network/request_queue.hpp"
|
|
#include "session/network/routing/network_router.hpp"
|
|
#include "session/network/snode_pool.hpp"
|
|
|
|
namespace session::network {
|
|
|
|
namespace config {
|
|
struct OnionRequestRouter {
|
|
FileServer file_server_config;
|
|
std::optional<std::filesystem::path> cache_directory;
|
|
std::chrono::days edge_node_cache_duration;
|
|
|
|
opt::netid::Target netid;
|
|
std::vector<service_node> seed_nodes;
|
|
|
|
network::opt::retry_delay retry_delay;
|
|
|
|
uint8_t path_length;
|
|
uint8_t path_strike_threshold;
|
|
uint8_t path_build_retry_limit;
|
|
std::chrono::minutes path_rotation_frequency;
|
|
uint8_t node_strike_threshold;
|
|
bool disable_pre_build_paths;
|
|
bool single_path_mode;
|
|
std::unordered_map<PathCategory, uint8_t> min_path_counts;
|
|
};
|
|
} // namespace config
|
|
|
|
struct cached_edge_node {
|
|
service_node node;
|
|
std::chrono::system_clock::time_point first_connected_at;
|
|
|
|
template <typename OutputIt>
|
|
void to_disk(OutputIt out) const {
|
|
auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(
|
|
first_connected_at.time_since_epoch())
|
|
.count();
|
|
|
|
fmt::format_to(
|
|
out,
|
|
"{}|{}|{}|{}|{}.{}.{}|{}|{}\n",
|
|
node.remote_pubkey.hex(),
|
|
node.host(),
|
|
node.https_port,
|
|
node.omq_port,
|
|
node.storage_server_version[0],
|
|
node.storage_server_version[1],
|
|
node.storage_server_version[2],
|
|
node.swarm_id,
|
|
timestamp);
|
|
}
|
|
|
|
static cached_edge_node from_disk(std::string_view str);
|
|
};
|
|
|
|
namespace cached_edge_node_disk_format {
|
|
constexpr size_t TIMESTAMP = 10; // unix timestamp
|
|
|
|
constexpr size_t MAX_LINE_SIZE = service_node_disk_format::MAX_LINE_SIZE + TIMESTAMP;
|
|
} // namespace cached_edge_node_disk_format
|
|
|
|
struct OnionPath {
|
|
std::string id;
|
|
std::vector<service_node> nodes;
|
|
std::chrono::system_clock::time_point created_at;
|
|
std::chrono::system_clock::time_point edge_first_connected_at;
|
|
|
|
size_t active_requests = 0;
|
|
uint16_t strike_count = 0;
|
|
bool rotation_in_progress = false;
|
|
|
|
std::string to_string() const;
|
|
};
|
|
|
|
struct PendingRotation {
|
|
std::string old_path_id;
|
|
PathCategory category;
|
|
OnionPath new_path;
|
|
};
|
|
|
|
inline PathCategory to_path_category(RequestCategory category) {
|
|
switch (category) {
|
|
case RequestCategory::standard: return PathCategory::standard;
|
|
case RequestCategory::standard_small: return PathCategory::standard;
|
|
case RequestCategory::file: return PathCategory::file;
|
|
case RequestCategory::file_small: return PathCategory::file;
|
|
}
|
|
return PathCategory::standard; // Should not be reached
|
|
}
|
|
|
|
class OnionRequestRouter : public IRouter, public std::enable_shared_from_this<OnionRequestRouter> {
|
|
private:
|
|
friend class TestOnionRequestRouter;
|
|
|
|
bool _ready = false;
|
|
bool _suspended = false;
|
|
config::OnionRequestRouter _config;
|
|
std::shared_ptr<oxen::quic::Loop> _loop;
|
|
std::shared_ptr<oxen::quic::Loop> _disk_loop;
|
|
std::weak_ptr<SnodePool> _snode_pool;
|
|
std::weak_ptr<ITransport> _transport;
|
|
|
|
std::vector<cached_edge_node> _cached_edge_nodes;
|
|
std::unordered_map<PathCategory, std::vector<OnionPath>> _paths;
|
|
std::unordered_map<PathCategory, std::vector<OnionPath>> _paths_pending_drop;
|
|
std::unordered_map<PathCategory, std::shared_ptr<detail::RequestQueue>> _request_queues;
|
|
std::unordered_map<std::string, std::pair<UploadRequest, std::thread>> _active_uploads;
|
|
std::unordered_map<std::string, DownloadRequest> _active_downloads;
|
|
|
|
oxen::quic::event_ptr _path_rotation_timer;
|
|
std::unordered_map<PathCategory, int> _in_progress_path_builds;
|
|
std::unordered_map<std::string, int> _path_build_retries;
|
|
std::unordered_map<
|
|
std::string,
|
|
std::pair<
|
|
std::vector<service_node>,
|
|
std::optional<std::chrono::system_clock::time_point>>>
|
|
_pending_paths;
|
|
std::multimap<std::chrono::steady_clock::time_point, std::pair<std::string, PathCategory>>
|
|
_path_rotation_schedule;
|
|
std::unordered_map<std::string, PendingRotation> _pending_rotation_paths;
|
|
|
|
// Disk I/O
|
|
std::filesystem::path _edge_node_cache_file_path;
|
|
|
|
public:
|
|
OnionRequestRouter(
|
|
config::OnionRequestRouter config,
|
|
std::shared_ptr<oxen::quic::Loop> loop,
|
|
std::shared_ptr<oxen::quic::Loop> disk_loop,
|
|
std::weak_ptr<SnodePool> snode_pool,
|
|
std::weak_ptr<ITransport> transport);
|
|
~OnionRequestRouter() override;
|
|
|
|
void suspend() override;
|
|
void resume(bool automatically_reconnect = true) override;
|
|
void close_connections() override;
|
|
void clear_cache() override;
|
|
|
|
ConnectionStatus get_status() const override { return _status.load(); };
|
|
std::vector<PathInfo> get_active_paths() override;
|
|
std::vector<service_node> get_all_used_nodes() override;
|
|
void send_request(Request request, network_response_callback_t callback) override;
|
|
void upload(UploadRequest request) override;
|
|
void download(DownloadRequest request) override;
|
|
|
|
private:
|
|
std::atomic<ConnectionStatus> _status{ConnectionStatus::unknown};
|
|
|
|
// Disk I/O functions
|
|
void _load_from_disk();
|
|
static void _clear_disk_cache(const std::filesystem::path& file_path);
|
|
static void _perform_edge_node_write(
|
|
const std::filesystem::path& file_path, std::span<const cached_edge_node> edge_nodes);
|
|
|
|
// All of the below functions should only be called from within `_loop`
|
|
void _finish_setup();
|
|
void _pre_build_paths_if_needed();
|
|
void _close_connections();
|
|
void _update_status();
|
|
void _send_request_internal(Request request, network_response_callback_t callback);
|
|
void _upload_internal(UploadRequest request);
|
|
void _download_internal(DownloadRequest request);
|
|
|
|
void _build_path(
|
|
PathCategory category,
|
|
std::optional<std::string> initiating_req_id,
|
|
const std::vector<service_node>& nodes_to_exclude,
|
|
std::optional<std::string> original_path_id = std::nullopt,
|
|
std::optional<service_node> specific_edge_node = std::nullopt,
|
|
std::optional<std::chrono::system_clock::time_point> edge_node_first_connection_at =
|
|
std::nullopt);
|
|
void _on_edge_connectivity_response(
|
|
const std::string& path_id,
|
|
PathCategory category,
|
|
std::optional<std::string> initiating_req_id,
|
|
bool success,
|
|
std::optional<uint64_t> error_code);
|
|
|
|
OnionPath* _find_valid_path(const Request& request);
|
|
|
|
void _send_on_path(OnionPath& path, Request request, network_response_callback_t callback);
|
|
void _handle_transport_response(
|
|
std::string path_id,
|
|
Request original_request,
|
|
bool success,
|
|
bool timeout,
|
|
int16_t status_code,
|
|
std::vector<std::pair<std::string, std::string>> headers,
|
|
std::optional<std::string> decrypted_body,
|
|
network_response_callback_t callback);
|
|
|
|
void _decrement_and_cleanup_path(const std::string& path_id, PathCategory category);
|
|
void _handle_path_failure(
|
|
const std::string& path_id,
|
|
const PathCategory& category,
|
|
const std::unordered_set<ed25519_pubkey>& already_penalized_nodes);
|
|
void _try_repair_path(
|
|
const std::string& path_id,
|
|
const PathCategory& category,
|
|
const ed25519_pubkey& bad_node_pubkey);
|
|
|
|
void _schedule_path_rotation(
|
|
const std::string& path_id,
|
|
PathCategory category,
|
|
std::chrono::steady_clock::time_point rotate_at);
|
|
void _check_path_rotations(std::optional<std::chrono::steady_clock::time_point> now);
|
|
void _update_rotation_timer();
|
|
void _rotate_path(const std::string& path_id, PathCategory category);
|
|
void _on_rotation_verification_response(
|
|
const std::string& new_path_id,
|
|
bool success,
|
|
bool timeout,
|
|
int16_t status_code,
|
|
std::chrono::steady_clock::time_point rotate_at);
|
|
};
|
|
|
|
} // namespace session::network
|