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_INFO(
"Peer negotiation is done. Answering to want order");
154 }
else if (
pack.empty()) {
166 auto cmd =
pack.substr(0,
lim);
171 JAMI_INFO(
"Upload pack command detected.");
174 auto versionIt = parameters.find(
"version");
187 JAMI_ERR(
"That protocol version is not yet supported (version: %u)",
version);
196 const auto& commit =
haveRefs_.emplace_back(
dat.substr(0, 40));
244 constexpr auto toSend =
"000eversion 1\0"sv;
245 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.data()),
249 JAMI_WARNING(
"Unable to send data for {}: {}", repository_,
ec.message());
257 JAMI_ERROR(
"Unable to get reference for HEAD");
264 std::ostringstream packet;
271 for (std::size_t
i = 0;
i <
refs.count; ++
i) {
272 std::string_view ref =
refs.strings[
i];
287 auto toSend = packet.str();
288 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.data()),
toSend.size(),
ec);
290 JAMI_WARNING(
"Unable to send data for {}: {}", repository_,
ec.message());
300 if (!common_.empty()) {
302 18 + common_.size() , common_);
303 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.c_str()),
toSend.size(),
ec);
305 JAMI_WARNING(
"Unable to send data for {}: {}", repository_,
ec.message());
316 if (!common_.empty()) {
318 9 + common_.size() , common_);
319 socket_->write(
reinterpret_cast<const unsigned char*
>(
toSend.c_str()),
toSend.size(),
ec);
321 JAMI_WARNING(
"Unable to send data for {}: {}", repository_,
ec.message());
334 socket_->write(
reinterpret_cast<const unsigned char*
>(
NAK_PKT.data()),
NAK_PKT.size(),
ec);
336 JAMI_WARNING(
"Unable to send data for {}: {}", repository_,
ec.message());
348 JAMI_WARN(
"Unable to open %s", repository_.c_str());
355 JAMI_WARNING(
"Unable to open packbuilder for {}", repository_);
360 std::string
fetched = wantedReference_;
376 std::set<std::string> parents;
382 haveCommit |= std::find(haveRefs_.begin(), haveRefs_.end(),
id) != haveRefs_.end();
383 auto itParents = std::find(parents.begin(), parents.end(),
id);
389 JAMI_WARN(
"Unable to open insert commit %s for %s",
391 repository_.c_str());
398 JAMI_ERR(
"Unable to look up current commit");
413 JAMI_WARN(
"Unable to write pack data for %s", repository_.c_str());
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(
"Unable to send data for {}: {}", repository_,
ec.message());
442 }
while (
sent < len);
449 JAMI_WARNING(
"Unable to send data for {}: {}", 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[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,...)
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