From fad3294ed38ba0485cbb1050913e8d2e7dd0705c Mon Sep 17 00:00:00 2001 From: orignal Date: Mon, 17 Aug 2026 16:34:52 -0400 Subject: [PATCH] handle torrent-get RPC --- libi2pd_client/Torrents.cpp | 45 ++++++++++++++++++++---- libi2pd_client/Torrents.h | 8 ++++- libi2pd_client/TorrentsRPC.cpp | 63 ++++++++++++++++++++++++++++------ 3 files changed, 99 insertions(+), 17 deletions(-) diff --git a/libi2pd_client/Torrents.cpp b/libi2pd_client/Torrents.cpp index 7a5763de..0a1d185c 100644 --- a/libi2pd_client/Torrents.cpp +++ b/libi2pd_client/Torrents.cpp @@ -1439,7 +1439,7 @@ namespace torrents { torrent->SetFullPath (m_TorrentsDir/std::filesystem::path (torrent->GetName ())); InitTorrentFiles (torrent); - m_Torrents.emplace (torrent->GetInfoHash (), torrent); + InsertTorrent (torrent); } } @@ -1584,13 +1584,32 @@ namespace torrents std::shared_ptr TorrentsTunnel::FindTorrent (const Torrent::InfoHash& infoHash) const { + std::lock_guard l(m_TorrentsMutex); auto it = m_Torrents.find (infoHash); if (it != m_Torrents.end ()) return it->second; return nullptr; } - std::pair, bool> TorrentsTunnel::AddTorrent (std::string_view torrentFileContent) + std::shared_ptr TorrentsTunnel::FindTorrentByID (int id) const + { + std::lock_guard l(m_TorrentsMutex); + auto it = m_TorrentsByID.find (id); + if (it != m_TorrentsByID.end ()) + return it->second.lock (); + return nullptr; + } + + std::vector TorrentsTunnel::GetTorrentIDs () const + { + std::vector ids; + std::lock_guard l(m_TorrentsMutex); + for (const auto& it: m_TorrentsByID) + if (!it.second.expired ()) ids.push_back (it.first); + return ids; + } + + std::pair, int> TorrentsTunnel::AddTorrent (std::string_view torrentFileContent) { auto torrent = std::make_shared (torrentFileContent); if (m_Torrents.find (torrent->GetInfoHash ()) == m_Torrents.end ()) @@ -1602,13 +1621,27 @@ namespace torrents if (f) f.write (torrentFileContent.data (), torrentFileContent.size ()); else - return { torrent, false }; + return { torrent, 0 }; } InitTorrentFiles (torrent); - m_Torrents.emplace (torrent->GetInfoHash (), torrent); - return { torrent, true }; + return { torrent, InsertTorrent (torrent) }; } - return { torrent, false }; + return { torrent, 0 }; + } + + int TorrentsTunnel::InsertTorrent (std::shared_ptr torrent) + { + if (!torrent) return 0; + std::lock_guard l(m_TorrentsMutex); + if (m_Torrents.emplace (torrent->GetInfoHash (), torrent).second) + { + int id = 1; + if (!m_TorrentsByID.empty ()) + id = m_TorrentsByID.rbegin ()->first + 1; + m_TorrentsByID.emplace (id, torrent); + return id; + } + return 0; } void TorrentsTunnel::Accept () diff --git a/libi2pd_client/Torrents.h b/libi2pd_client/Torrents.h index 18f337da..131cdb0c 100644 --- a/libi2pd_client/Torrents.h +++ b/libi2pd_client/Torrents.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "util.h" #include "Streaming.h" #include "HTTP.h" @@ -313,7 +314,9 @@ namespace torrents const std::string& GetPeerID () const { return m_PeerID; } std::shared_ptr FindTorrent (const Torrent::InfoHash& infoHash) const; - std::pair, bool> AddTorrent (std::string_view torrentFileContent); + std::shared_ptr FindTorrentByID (int id) const; + std::vector GetTorrentIDs () const; + std::pair, int> AddTorrent (std::string_view torrentFileContent); // (tunnel, id) std::list > GetTorrentConnections (std::shared_ptr torrent); const char* GetName() const override { return m_Name.c_str (); } @@ -324,6 +327,7 @@ namespace torrents void Accept (); void ReadTorrentFile (const std::filesystem::path& torrentFilePath); void InitTorrentFiles (std::shared_ptr torrent); + int InsertTorrent (std::shared_ptr torrent); // returns id > 0 if success and 0 if failed bool CreateAndReserveFile (const std::filesystem::path& filePath, size_t reserve); void CompleteTorrent (std::shared_ptr torrent); void RequestTracker (std::shared_ptr torrent, std::string_view event = ""); @@ -354,6 +358,8 @@ namespace torrents std::filesystem::path m_TorrentsDir; std::vector m_Trackers; std::map > m_Torrents; + std::map > m_TorrentsByID; + mutable std::mutex m_TorrentsMutex; boost::asio::steady_timer m_TrackerRequestsCheckTimer, m_KeepAliveCheckTimer, m_ReconnectCheckTimer, m_TorrentsStatusUpdateTimer; DiskIOService m_DiskIOService; diff --git a/libi2pd_client/TorrentsRPC.cpp b/libi2pd_client/TorrentsRPC.cpp index f8f8a064..3cd39dee 100644 --- a/libi2pd_client/TorrentsRPC.cpp +++ b/libi2pd_client/TorrentsRPC.cpp @@ -12,6 +12,7 @@ #define JSON_SUPPORTED #endif #include +#include #include "Log.h" #include "Torrents.h" #include "TorrentsRPC.h" @@ -39,21 +40,23 @@ namespace torrents private: - std::string SuccessResponse (boost::json::object&& arguments); + std::string SuccessResponse (int64_t id, boost::json::object&& arguments); std::string ErrorResponse (JSONRPCErrorCode errorCode, int64_t id, std::string_view message); std::string HandleTorrrentAdd (boost::json::object&& jsonRequest); + std::string HandleTorrrentGet (boost::json::object&& jsonRequest); private: std::shared_ptr m_Tunnel; }; - std::string JSONRPCHandler::SuccessResponse (boost::json::object&& arguments) + std::string JSONRPCHandler::SuccessResponse (int64_t id, boost::json::object&& arguments) { boost::json::object response; response["result"] = "success"; response["arguments"] = arguments; + if (id) response["id"] = id; return boost::json::serialize(response); } @@ -77,11 +80,12 @@ namespace torrents auto method = jsonRequest.at ("method").as_string (); if (method == "torrent-add") return HandleTorrrentAdd (std::move (jsonRequest)); + else if (method == "torrent-get") + return HandleTorrrentGet (std::move (jsonRequest)); else { LogPrint (eLogInfo, "TorrentsRPC: Method not found ", method); - int64_t tag = jsonRequest.at ("tag").as_int64 (); - return ErrorResponse (eMethodNotFound, tag, "Method not found"); + return ErrorResponse (eMethodNotFound, jsonRequest.at ("tag").as_int64 (), "Method not found"); } } catch (const std::exception& ex) @@ -99,18 +103,18 @@ namespace torrents std::string torrentFileContent; torrentFileContent.resize (boost::beast::detail::base64::decoded_size (b64torrent.size ())); boost::beast::detail::base64::decode (torrentFileContent.data (), b64torrent.data (), b64torrent.size ()); - auto [torrent, added] = m_Tunnel->AddTorrent (torrentFileContent); + auto [torrent, id] = m_Tunnel->AddTorrent (torrentFileContent); boost::json::object response, torrentInfo; - std::string hexHash; if (torrent) { + std::string hexHash; boost::algorithm::hex (torrent->GetInfoHash ().begin(), torrent->GetInfoHash ().end(), std::back_inserter(hexHash)); - torrentInfo["id"] = 1; // TODO: + torrentInfo["id"] = id; torrentInfo["hashString"] = hexHash; torrentInfo["name"] = torrent->GetName (); } - if (added) + if (id) { response["torrent-added"] = torrentInfo; LogPrint (eLogDebug, "TorrentsRPC: torrent added ", torrentInfo["name"].as_string ()); @@ -120,7 +124,46 @@ namespace torrents response["torrent-duplicate"] = torrentInfo; LogPrint (eLogDebug, "TorrentsRPC: duplicate torrent ", torrentInfo["name"].as_string ()); } - return SuccessResponse (std::move (response)); + return SuccessResponse (jsonRequest.at ("tag").as_int64 (), std::move (response)); + } + + std::string JSONRPCHandler::HandleTorrrentGet (boost::json::object&& jsonRequest) + { + auto arguments = jsonRequest.at ("arguments").as_object (); + std::vector torrentIds; + if (arguments.contains ("ids")) + { + auto ids = arguments.at ("ids"); + if (ids.is_array ()) + for (const auto& it: ids.as_array ()) + torrentIds.push_back (it.as_int64 ()); + else + torrentIds.push_back (ids.as_int64 ()); + } + else + torrentIds = m_Tunnel->GetTorrentIDs (); + + boost::json::object response; + boost::json::array torrents; + for (auto id: torrentIds) + { + auto torrent = m_Tunnel->FindTorrentByID (id); + if (torrent) + { + boost::json::object t; + t["id"] = id; + t["name"] = torrent->GetName (); + t["pieceCount"] = torrent->GetNumPieces (); + t["pieceSize"] = torrent->GetNumPieces (); + t["totalSize"] = torrent->GetLength (); + std::string hexHash; + boost::algorithm::hex (torrent->GetInfoHash ().begin(), torrent->GetInfoHash ().end(), std::back_inserter(hexHash)); + t["hashString"] = hexHash; + torrents.push_back (t); + } + } + response["torrents"] = torrents; + return SuccessResponse (jsonRequest.at ("tag").as_int64 (), std::move (response)); } #endif @@ -174,7 +217,7 @@ namespace torrents m_Response.version (11); // HTTP/1.1 m_Response.result (result); m_Response.set (boost::beast::http::field::server, "i2pd torents RPC"); - m_Response.set(boost::beast::http::field::content_type, (result == boost::beast::http::status::ok) ? "application/json" : "text/plain"); + m_Response.set(boost::beast::http::field::content_type, (result == boost::beast::http::status::ok) ? "application/json; charset=UTF-8" : "text/plain"); m_Response.body () = data; m_Response.prepare_payload (); boost::beast::http::async_write (m_Socket, m_Response,