23#include <opendht/thread_pool.h>
24#include <dhtnet/multiplexed_socket.h>
25#include <fmt/compile.h>
33using namespace std::string_view_literals;
39constexpr auto SERVER_CAPABILITIES =
" HEAD\0side-band side-band-64k shallow no-progress include-tag"sv;
46 Impl(
const std::string& accountId,
48 const std::string& repository,
49 const std::shared_ptr<dhtnet::ChannelSocket>& socket)
59 dht::ThreadPool::io().run([socket =
socket_] { socket->shutdown(); });
84 dht::ThreadPool::io().run([socket =
socket_] { socket->shutdown(); });
99 std::shared_ptr<dhtnet::ChannelSocket>
socket_ {};
121 auto [p,
ec] = std::from_chars(
pkt.data(),
pkt.data() + 4,
pkt_len, 16);
122 if (
ec != std::errc()) {
123 JAMI_ERROR(
"[Account {}] [Conversation {}] [GitServer {}] Unable to parse packet size",
143 JAMI_LOG(
"[Account {}] [Conversation {}] [GitServer {}] Peer negotiation is done. Answering to want order",
155 }
else if (
pack.empty()) {
167 auto cmd =
pack.substr(0,
lim);
172 JAMI_LOG(
"[Account {}] [Conversation {}] [GitServer {}] Upload pack command detected.",
178 auto versionIt = parameters.find(
"version");
181 auto [p,
ec] = std::from_chars(
versionIt->second.data(),
184 if (
ec == std::errc()) {
187 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Invalid version detected: {}",
197 JAMI_ERROR(
"[Account {}] [Conversation {}] [GitServer {}] That protocol version is not yet supported "
209 JAMI_LOG(
"[Account {}] [Conversation {}] [GitServer {}] Peer want ref: {}",
215 const auto& commit =
haveRefs_.emplace_back(
dat.substr(0, 40));
223 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to open {}",
238 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unwanted packet received: {}",
250 return fmt::format(
FMT_COMPILE(
"{:04x}"), value & 0x0FFFF);
261 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to open {}",
266 dht::ThreadPool::io().run([socket = socket_] { socket->shutdown(); });
276 constexpr auto toSend =
"000eversion 1\0"sv;
277 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.data()),
toSend.size(),
ec);
279 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}",
285 dht::ThreadPool::io().run([socket = socket_] { socket->shutdown(); });
292 JAMI_ERROR(
"[Account {}] [Conversation {}] [GitServer {}] Unable to get reference for HEAD",
296 dht::ThreadPool::io().run([socket = socket_] { socket->shutdown(); });
302 std::ostringstream packet;
309 for (std::size_t
i = 0;
i <
refs.count; ++
i) {
310 std::string_view ref =
refs.strings[
i];
312 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to get reference for {}",
329 auto toSend = packet.str();
330 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.data()),
toSend.size(),
ec);
332 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}",
338 dht::ThreadPool::io().run([socket = socket_] { socket->shutdown(); });
347 if (!common_.empty()) {
349 18 + common_.size() ,
351 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.c_str()),
toSend.size(),
ec);
353 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}",
359 dht::ThreadPool::io().run([socket = socket_] { socket->shutdown(); });
369 if (!common_.empty()) {
373 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.c_str()),
toSend.size(),
ec);
375 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}",
381 dht::ThreadPool::io().run([socket = socket_] { socket->shutdown(); });
393 socket_->write(
reinterpret_cast<const unsigned char*
>(
NAK_PKT.data()),
NAK_PKT.size(),
ec);
395 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}",
401 dht::ThreadPool::io().run([socket = socket_] { socket->shutdown(); });
412 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to open {}",
423 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to open packbuilder for {}",
432 std::string
fetched = wantedReference_;
435 JAMI_ERROR(
"[Account {}] [Conversation {}] [GitServer {}] Unable to get reference for commit {}",
452 std::set<std::string> parents;
458 haveCommit |= std::find(haveRefs_.begin(), haveRefs_.end(),
id) != haveRefs_.end();
459 auto itParents = std::find(parents.begin(), parents.end(),
id);
465 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to open insert commit {} for {}",
477 JAMI_ERROR(
"[Account {}] [Conversation {}] [GitServer {}] Unable to look up current commit",
495 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to write pack data for {}",
503 std::size_t
sent = 0;
504 std::size_t len = data.size;
511 std::size_t
pkt_size = std::min(
static_cast<std::size_t
>(65515), len -
sent);
521 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}",
531 }
while (
sent < len);
538 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}",
548 wantedReference_.clear();
554std::map<std::string, std::string>
557 std::map<std::string, std::string> parameters;
558 std::string key, value;
564 if (
nullChar != 0 && !key.empty()) {
565 parameters.try_emplace(std::move(key), std::move(value));
571 }
else if (
letter ==
'=') {
585 const std::string& conversationId,
586 const std::shared_ptr<dhtnet::ChannelSocket>&
client)
589 pimpl_ = std::make_unique<GitServer::Impl>(accountId, conversationId, path,
client);
603 pimpl_->onFetchedCb_ =
cb;
Impl(const std::string &accountId, const std::string &repositoryId, const std::string &repository, const std::shared_ptr< dhtnet::ChannelSocket > &socket)
void sendReferenceCapabilities(bool sendVersion=false)
std::map< std::string, std::string > getParameters(std::string_view pkt_line)
std::string repositoryId_
std::atomic_bool isDestroying_
bool parseOrder(std::string_view buf={})
std::shared_ptr< dhtnet::ChannelSocket > socket_
std::vector< std::string > haveRefs_
std::string wantedReference_
GitServer(const std::string &accountId, const std::string &conversationId, const std::shared_ptr< dhtnet::ChannelSocket > &client)
Serve a conversation to a remote client This client will be able to fetch commits/clone the repositor...
void setOnFetched(const onFetchedCb &cb)
Add a callback which will be triggered when the peer gets the data.
void stop()
Stopping a GitServer will shut the channel down.
constexpr auto SERVER_CAPABILITIES
constexpr auto UPLOAD_PACK_CMD
#define JAMI_ERROR(formatstr,...)
#define JAMI_DEBUG(formatstr,...)
#define JAMI_WARNING(formatstr,...)
#define JAMI_LOG(formatstr,...)
const std::filesystem::path & get_data_dir()
static constexpr int version
std::unique_ptr< git_repository, GitRepositoryDeleter > GitRepository
void emitSignal(Args... args)
std::unique_ptr< git_commit, GitCommitDeleter > GitCommit
std::string toGitHex(size_t value)
std::unique_ptr< git_packbuilder, GitPackBuilderDeleter > GitPackBuilder
std::function< void(const std::string &)> onFetchedCb
std::unique_ptr< git_revwalk, GitRevWalkerDeleter > GitRevWalker