23#include <opendht/thread_pool.h>
24#include <dhtnet/multiplexed_socket.h>
25#include <fmt/compile.h>
33using namespace std::string_view_literals;
40 =
" HEAD\0side-band side-band-64k shallow no-progress include-tag"sv;
47 Impl(
const std::string& accountId,
49 const std::string& repository,
50 const std::shared_ptr<dhtnet::ChannelSocket>& socket)
56 JAMI_DEBUG(
"[Account {}] [Conversation {}] [GitServer {}] created",
79 JAMI_DEBUG(
"[Account {}] [Conversation {}] [GitServer {}] destroyed",
104 std::shared_ptr<dhtnet::ChannelSocket>
socket_ {};
126 auto [p,
ec] = std::from_chars(
pkt.data(),
pkt.data() + 4,
pkt_len, 16);
127 if (
ec != std::errc()) {
145 JAMI_LOG(
"[Account {}] [Conversation {}] [GitServer {}] Peer negotiation is done. Answering to want order",
accountId_,
repositoryId_, fmt::ptr(
this));
154 }
else if (
pack.empty()) {
166 auto cmd =
pack.substr(0,
lim);
174 auto versionIt = parameters.find(
"version");
177 auto [p,
ec] = std::from_chars(
versionIt->second.data(),
180 if (
ec == std::errc()) {
198 const auto& commit =
haveRefs_.emplace_back(
dat.substr(0, 40));
235 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to open {}", accountId_, repositoryId_, fmt::ptr(
this), repository_);
246 constexpr auto toSend =
"000eversion 1\0"sv;
247 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.data()),
251 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}", accountId_, repositoryId_, fmt::ptr(
this), repository_,
ec.message());
259 JAMI_ERROR(
"[Account {}] [Conversation {}] [GitServer {}] Unable to get reference for HEAD", accountId_, repositoryId_, fmt::ptr(
this));
266 std::ostringstream packet;
273 for (std::size_t
i = 0;
i <
refs.count; ++
i) {
274 std::string_view ref =
refs.strings[
i];
276 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to get reference for {}", accountId_, repositoryId_, fmt::ptr(
this), ref);
289 auto toSend = packet.str();
290 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.data()),
toSend.size(),
ec);
292 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}", accountId_, repositoryId_, fmt::ptr(
this), repository_,
ec.message());
302 if (!common_.empty()) {
304 18 + common_.size() , common_);
305 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.c_str()),
toSend.size(),
ec);
307 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}", accountId_, repositoryId_, fmt::ptr(
this), repository_,
ec.message());
318 if (!common_.empty()) {
320 9 + common_.size() , common_);
321 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.c_str()),
toSend.size(),
ec);
323 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}", accountId_, repositoryId_, fmt::ptr(
this), repository_,
ec.message());
336 socket_->write(
reinterpret_cast<const unsigned char*
>(
NAK_PKT.data()),
NAK_PKT.size(),
ec);
338 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}", accountId_, repositoryId_, fmt::ptr(
this), repository_,
ec.message());
350 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to open {}", accountId_, repositoryId_, fmt::ptr(
this), repository_);
357 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to open packbuilder for {}", accountId_, repositoryId_, fmt::ptr(
this), repository_);
362 std::string
fetched = wantedReference_;
365 JAMI_ERROR(
"[Account {}] [Conversation {}] [GitServer {}] Unable to get reference for commit {}", accountId_, repositoryId_, fmt::ptr(
this),
fetched);
378 std::set<std::string> parents;
384 haveCommit |= std::find(haveRefs_.begin(), haveRefs_.end(),
id) != haveRefs_.end();
385 auto itParents = std::find(parents.begin(), parents.end(),
id);
391 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to open insert commit {} for {}", accountId_, repositoryId_, fmt::ptr(
this),
git_oid_tostr_s(&
oid), repository_);
398 JAMI_ERROR(
"[Account {}] [Conversation {}] [GitServer {}] Unable to look up current commit", accountId_, repositoryId_, fmt::ptr(
this));
413 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to write pack data for {}", accountId_, repositoryId_, fmt::ptr(
this), repository_);
417 std::size_t
sent = 0;
418 std::size_t len = data.size;
425 std::size_t
pkt_size = std::min(
static_cast<std::size_t
>(65515), len -
sent);
433 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSendData.data()),
437 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}", accountId_, repositoryId_, fmt::ptr(
this), repository_,
ec.message());
442 }
while (
sent < len);
449 JAMI_WARNING(
"[Account {}] [Conversation {}] [GitServer {}] Unable to send data for {}: {}", accountId_, repositoryId_, fmt::ptr(
this), repository_,
ec.message());
454 wantedReference_.clear();
460std::map<std::string, std::string>
463 std::map<std::string, std::string> parameters;
464 std::string key,
value;
470 if (
nullChar != 0 && !key.empty()) {
471 parameters.try_emplace(std::move(key), std::move(
value));
477 }
else if (
letter ==
'=') {
491 const std::string& conversationId,
492 const std::shared_ptr<dhtnet::ChannelSocket>&
client)
495 pimpl_ = std::make_unique<GitServer::Impl>(accountId, conversationId, path,
client);
509 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.
std::unique_ptr< git_commit, decltype(&git_commit_free)> GitCommit
std::unique_ptr< git_repository, decltype(&git_repository_free)> GitRepository
std::unique_ptr< git_packbuilder, decltype(&git_packbuilder_free)> GitPackBuilder
std::unique_ptr< git_revwalk, decltype(&git_revwalk_free)> GitRevWalker
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
void emitSignal(Args... args)
std::string toGitHex(size_t value)
std::function< void(const std::string &)> onFetchedCb