handle torrent-get RPC

This commit is contained in:
orignal
2026-08-17 16:34:52 -04:00
parent d9cd383b08
commit fad3294ed3
3 changed files with 99 additions and 17 deletions
+39 -6
View File
@@ -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<Torrent> TorrentsTunnel::FindTorrent (const Torrent::InfoHash& infoHash) const
{
std::lock_guard<std::mutex> l(m_TorrentsMutex);
auto it = m_Torrents.find (infoHash);
if (it != m_Torrents.end ())
return it->second;
return nullptr;
}
std::pair<std::shared_ptr<Torrent>, bool> TorrentsTunnel::AddTorrent (std::string_view torrentFileContent)
std::shared_ptr<Torrent> TorrentsTunnel::FindTorrentByID (int id) const
{
std::lock_guard<std::mutex> l(m_TorrentsMutex);
auto it = m_TorrentsByID.find (id);
if (it != m_TorrentsByID.end ())
return it->second.lock ();
return nullptr;
}
std::vector<int> TorrentsTunnel::GetTorrentIDs () const
{
std::vector<int> ids;
std::lock_guard<std::mutex> l(m_TorrentsMutex);
for (const auto& it: m_TorrentsByID)
if (!it.second.expired ()) ids.push_back (it.first);
return ids;
}
std::pair<std::shared_ptr<Torrent>, int> TorrentsTunnel::AddTorrent (std::string_view torrentFileContent)
{
auto torrent = std::make_shared<Torrent> (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> torrent)
{
if (!torrent) return 0;
std::lock_guard<std::mutex> 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 ()
+7 -1
View File
@@ -24,6 +24,7 @@
#include <random>
#include <tuple>
#include <filesystem>
#include <mutex>
#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<Torrent> FindTorrent (const Torrent::InfoHash& infoHash) const;
std::pair<std::shared_ptr<Torrent>, bool> AddTorrent (std::string_view torrentFileContent);
std::shared_ptr<Torrent> FindTorrentByID (int id) const;
std::vector<int> GetTorrentIDs () const;
std::pair<std::shared_ptr<Torrent>, int> AddTorrent (std::string_view torrentFileContent); // (tunnel, id)
std::list<std::shared_ptr<PeerConnection> > GetTorrentConnections (std::shared_ptr<Torrent> 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> torrent);
int InsertTorrent (std::shared_ptr<Torrent> 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> torrent);
void RequestTracker (std::shared_ptr<Torrent> torrent, std::string_view event = "");
@@ -354,6 +358,8 @@ namespace torrents
std::filesystem::path m_TorrentsDir;
std::vector<std::string> m_Trackers;
std::map<Torrent::InfoHash, std::shared_ptr<Torrent> > m_Torrents;
std::map<int, std::weak_ptr<Torrent> > m_TorrentsByID;
mutable std::mutex m_TorrentsMutex;
boost::asio::steady_timer m_TrackerRequestsCheckTimer, m_KeepAliveCheckTimer,
m_ReconnectCheckTimer, m_TorrentsStatusUpdateTimer;
DiskIOService m_DiskIOService;
+53 -10
View File
@@ -12,6 +12,7 @@
#define JSON_SUPPORTED
#endif
#include <boost/algorithm/hex.hpp>
#include <vector>
#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<TorrentsTunnel> 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<int> 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,