Add stream errors handling
This commit is contained in:
parent
bb336da4d9
commit
0636e1c234
11 changed files with 216 additions and 170 deletions
|
@ -55,7 +55,12 @@ CPMAddPackage(
|
||||||
|
|
||||||
CPMAddPackage("gh:zeux/pugixml@1.14")
|
CPMAddPackage("gh:zeux/pugixml@1.14")
|
||||||
CPMAddPackage("gh:fmtlib/fmt#10.2.1")
|
CPMAddPackage("gh:fmtlib/fmt#10.2.1")
|
||||||
CPMAddPackage("gh:Neargye/nameof@0.10.4")
|
CPMAddPackage(NAME nameof
|
||||||
|
VERSION 0.10.4
|
||||||
|
GIT_REPOSITORY "https://github.com/Neargye/nameof.git"
|
||||||
|
EXCLUDE_FROM_ALL ON
|
||||||
|
OPTIONS "NAMEOF_OPT_INSTALL ON"
|
||||||
|
)
|
||||||
|
|
||||||
CPMAddPackage(
|
CPMAddPackage(
|
||||||
NAME spdlog
|
NAME spdlog
|
||||||
|
@ -167,7 +172,7 @@ if(TARGET Boost::pfr)
|
||||||
OpenSSL::SSL nameof::nameof
|
OpenSSL::SSL nameof::nameof
|
||||||
OpenSSL::Crypto spdlog xmlplusplus ${LIBXML2_LIBRARIES})
|
OpenSSL::Crypto spdlog xmlplusplus ${LIBXML2_LIBRARIES})
|
||||||
else()
|
else()
|
||||||
find_package(Boost 1.85.0 REQUIRED)
|
find_package(Boost 1.85.0 COMPONENTS serialization REQUIRED)
|
||||||
target_link_libraries(larra_xmpp PUBLIC
|
target_link_libraries(larra_xmpp PUBLIC
|
||||||
utempl::utempl ${Boost_LIBRARIES} pugixml::pugixml OpenSSL::SSL
|
utempl::utempl ${Boost_LIBRARIES} pugixml::pugixml OpenSSL::SSL
|
||||||
nameof::nameof
|
nameof::nameof
|
||||||
|
|
|
@ -11,15 +11,16 @@
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
#include <boost/asio/use_awaitable.hpp>
|
#include <boost/asio/use_awaitable.hpp>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
#include <larra/client/challenge_response.hpp>
|
||||||
#include <larra/client/options.hpp>
|
#include <larra/client/options.hpp>
|
||||||
|
#include <larra/client/starttls_response.hpp>
|
||||||
|
#include <larra/client/xmpp_client_stream_features.hpp>
|
||||||
#include <larra/encryption.hpp>
|
#include <larra/encryption.hpp>
|
||||||
#include <larra/features.hpp>
|
#include <larra/features.hpp>
|
||||||
#include <larra/raw_xml_stream.hpp>
|
|
||||||
#include <larra/stream.hpp>
|
#include <larra/stream.hpp>
|
||||||
#include <larra/user_account.hpp>
|
#include <larra/user_account.hpp>
|
||||||
|
#include <larra/xml_stream.hpp>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
#include "larra/client/xmpp_client_stream_features.hpp"
|
|
||||||
namespace larra::xmpp {
|
namespace larra::xmpp {
|
||||||
|
|
||||||
constexpr auto kDefaultXmppPort = 5222;
|
constexpr auto kDefaultXmppPort = 5222;
|
||||||
|
@ -33,15 +34,15 @@ namespace views = std::views;
|
||||||
|
|
||||||
template <typename Connection>
|
template <typename Connection>
|
||||||
struct Client {
|
struct Client {
|
||||||
constexpr Client(BareJid jid, RawXmlStream<Connection> connection) : jid(std::move(jid)), connection(std::move(connection)) {};
|
constexpr Client(BareJid jid, XmlStream<Connection> connection) : jid(std::move(jid)), connection(std::move(connection)) {};
|
||||||
template <boost::asio::completion_token_for<void()> Token = boost::asio::use_awaitable_t<>>
|
template <boost::asio::completion_token_for<void()> Token = boost::asio::use_awaitable_t<>>
|
||||||
constexpr auto Close(Token token = {}) {
|
constexpr auto Close(Token token = {}) {
|
||||||
this->active = false;
|
this->active = false;
|
||||||
return boost::asio::async_initiate<Token, void()>(
|
return boost::asio::async_initiate<Token, void()>(
|
||||||
[]<typename Handler>(Handler&& h, RawXmlStream<Connection> connection) { // NOLINT
|
[]<typename Handler>(Handler&& h, XmlStream<Connection> connection) { // NOLINT
|
||||||
boost::asio::co_spawn(
|
boost::asio::co_spawn(
|
||||||
connection.next_layer().get_executor(),
|
connection.next_layer().get_executor(),
|
||||||
[](auto h, RawXmlStream<Connection> connection) -> boost::asio::awaitable<void> {
|
[](auto h, XmlStream<Connection> connection) -> boost::asio::awaitable<void> {
|
||||||
co_await boost::asio::async_write(
|
co_await boost::asio::async_write(
|
||||||
connection.next_layer(), boost::asio::buffer("</stream:stream>"), boost::asio::use_awaitable);
|
connection.next_layer(), boost::asio::buffer("</stream:stream>"), boost::asio::use_awaitable);
|
||||||
std::string response;
|
std::string response;
|
||||||
|
@ -70,7 +71,7 @@ struct Client {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool active = true;
|
bool active = true;
|
||||||
RawXmlStream<Connection> connection;
|
XmlStream<Connection> connection;
|
||||||
BareJid jid;
|
BareJid jid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -158,7 +159,7 @@ struct ClientCreateVisitor {
|
||||||
const Options& options;
|
const Options& options;
|
||||||
|
|
||||||
template <typename Socket>
|
template <typename Socket>
|
||||||
auto Auth(PlainUserAccount account, RawXmlStream<Socket>& stream, ServerToUserStream streamHeader, StreamFeatures features)
|
auto Auth(PlainUserAccount account, XmlStream<Socket>& stream, ServerToUserStream streamHeader, StreamFeatures features)
|
||||||
-> boost::asio::awaitable<void> {
|
-> boost::asio::awaitable<void> {
|
||||||
SPDLOG_DEBUG("Start Plain Auth");
|
SPDLOG_DEBUG("Start Plain Auth");
|
||||||
if(!std::ranges::contains(features.saslMechanisms.mechanisms, "PLAIN")) {
|
if(!std::ranges::contains(features.saslMechanisms.mechanisms, "PLAIN")) {
|
||||||
|
@ -166,11 +167,17 @@ struct ClientCreateVisitor {
|
||||||
}
|
}
|
||||||
const features::PlainAuthData data{.username = account.jid.username, .password = account.password};
|
const features::PlainAuthData data{.username = account.jid.username, .password = account.password};
|
||||||
co_await stream.Send(data);
|
co_await stream.Send(data);
|
||||||
std::ignore = co_await stream.Read();
|
sasl::Response response = co_await stream.template Read<sasl::Response>();
|
||||||
|
std::visit(utempl::Overloaded(
|
||||||
|
[](auto error) {
|
||||||
|
throw std::move(error);
|
||||||
|
},
|
||||||
|
[](sasl::Success) {}),
|
||||||
|
response);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Socket, typename Tag>
|
template <typename Socket, typename Tag>
|
||||||
auto ScramAuth(std::string methodName, EncryptionUserAccount account, RawXmlStream<Socket>& stream, Tag tag)
|
auto ScramAuth(std::string methodName, EncryptionUserAccount account, XmlStream<Socket>& stream, Tag tag)
|
||||||
-> boost::asio::awaitable<void> {
|
-> boost::asio::awaitable<void> {
|
||||||
SPDLOG_DEBUG("Start Scram Auth using '{}'", methodName);
|
SPDLOG_DEBUG("Start Scram Auth using '{}'", methodName);
|
||||||
const auto nonce = GenerateNonce();
|
const auto nonce = GenerateNonce();
|
||||||
|
@ -191,23 +198,18 @@ struct ClientCreateVisitor {
|
||||||
.iterations = challenge.iterations,
|
.iterations = challenge.iterations,
|
||||||
.tag = tag};
|
.tag = tag};
|
||||||
co_await stream.Send(challengeResponse);
|
co_await stream.Send(challengeResponse);
|
||||||
std::unique_ptr<xmlpp::Document> doc = co_await stream.Read();
|
sasl::Response response = co_await stream.template Read<sasl::Response>();
|
||||||
auto root = doc->get_root_node();
|
std::visit(utempl::Overloaded(
|
||||||
if(!root || root->get_name() == "failure") {
|
[](auto error) {
|
||||||
if(auto textNode = root->get_first_child("text")) {
|
throw std::move(error);
|
||||||
if(auto text = dynamic_cast<xmlpp::Element*>(textNode)) {
|
},
|
||||||
if(auto childText = text->get_first_child_text()) {
|
[](sasl::Success) {}),
|
||||||
throw std::runtime_error(std::format("Auth failed: {}", childText->get_content()));
|
response);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw std::runtime_error("Auth failed");
|
|
||||||
}
|
|
||||||
SPDLOG_DEBUG("Success auth for JID {}", ToString(account.jid));
|
SPDLOG_DEBUG("Success auth for JID {}", ToString(account.jid));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Socket>
|
template <typename Socket>
|
||||||
auto Auth(EncryptionRequiredUserAccount account, RawXmlStream<Socket>& stream, ServerToUserStream streamHeader, StreamFeatures features)
|
auto Auth(EncryptionRequiredUserAccount account, XmlStream<Socket>& stream, ServerToUserStream streamHeader, StreamFeatures features)
|
||||||
-> boost::asio::awaitable<void> {
|
-> boost::asio::awaitable<void> {
|
||||||
if(std::ranges::contains(features.saslMechanisms.mechanisms, "SCRAM-SHA-512")) {
|
if(std::ranges::contains(features.saslMechanisms.mechanisms, "SCRAM-SHA-512")) {
|
||||||
co_return co_await ScramAuth("SCRAM-SHA-512", std::move(account), stream, sha512sum::EncryptionTag{});
|
co_return co_await ScramAuth("SCRAM-SHA-512", std::move(account), stream, sha512sum::EncryptionTag{});
|
||||||
|
@ -222,7 +224,7 @@ struct ClientCreateVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Socket>
|
template <typename Socket>
|
||||||
auto Auth(EncryptionUserAccount account, RawXmlStream<Socket>& stream, ServerToUserStream streamHeader, StreamFeatures features)
|
auto Auth(EncryptionUserAccount account, XmlStream<Socket>& stream, ServerToUserStream streamHeader, StreamFeatures features)
|
||||||
-> boost::asio::awaitable<void> {
|
-> boost::asio::awaitable<void> {
|
||||||
Contains(features.saslMechanisms.mechanisms, "SCRAM-SHA-1", "SCRAM-SHA-256", "SCRAM-SHA-512")
|
Contains(features.saslMechanisms.mechanisms, "SCRAM-SHA-1", "SCRAM-SHA-256", "SCRAM-SHA-512")
|
||||||
? co_await this->Auth(EncryptionRequiredUserAccount{std::move(account)}, stream, std::move(streamHeader), std::move(features))
|
? co_await this->Auth(EncryptionRequiredUserAccount{std::move(account)}, stream, std::move(streamHeader), std::move(features))
|
||||||
|
@ -230,7 +232,7 @@ struct ClientCreateVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Socket>
|
template <typename Socket>
|
||||||
auto Auth(RawXmlStream<Socket>& stream, ServerToUserStream streamHeader, StreamFeatures features) -> boost::asio::awaitable<void> {
|
auto Auth(XmlStream<Socket>& stream, ServerToUserStream streamHeader, StreamFeatures features) -> boost::asio::awaitable<void> {
|
||||||
co_return co_await std::visit(
|
co_return co_await std::visit(
|
||||||
[&](auto& account) -> boost::asio::awaitable<void> {
|
[&](auto& account) -> boost::asio::awaitable<void> {
|
||||||
return this->Auth(std::move(account), stream, std::move(streamHeader), std::move(features));
|
return this->Auth(std::move(account), stream, std::move(streamHeader), std::move(features));
|
||||||
|
@ -251,17 +253,16 @@ struct ClientCreateVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Socket>
|
template <typename Socket>
|
||||||
auto ProcessTls(RawXmlStream<boost::asio::ssl::stream<Socket>>& stream) -> boost::asio::awaitable<void> {
|
auto ProcessTls(XmlStream<boost::asio::ssl::stream<Socket>>& stream) -> boost::asio::awaitable<void> {
|
||||||
const StartTlsRequest request;
|
const StartTlsRequest request;
|
||||||
co_await stream.Send(request);
|
co_await stream.Send(request);
|
||||||
std::unique_ptr<xmlpp::Document> doc = co_await stream.Read();
|
starttls::Response response = co_await stream.template Read<starttls::Response>();
|
||||||
if(auto node = doc->get_root_node()) {
|
std::visit(utempl::Overloaded(
|
||||||
if(node->get_name() == "proceed") {
|
[](starttls::Failure error) {
|
||||||
goto proceed; // NOLINT
|
throw error;
|
||||||
}
|
},
|
||||||
throw StartTlsNegotiationError{"Failure XMPP"};
|
[](starttls::Success) {}),
|
||||||
}
|
response);
|
||||||
proceed:
|
|
||||||
auto& socket = stream.next_layer();
|
auto& socket = stream.next_layer();
|
||||||
SSL_set_tlsext_host_name(socket.native_handle(), this->account.Jid().server.c_str());
|
SSL_set_tlsext_host_name(socket.native_handle(), this->account.Jid().server.c_str());
|
||||||
try {
|
try {
|
||||||
|
@ -270,57 +271,14 @@ struct ClientCreateVisitor {
|
||||||
throw StartTlsNegotiationError{e.what()};
|
throw StartTlsNegotiationError{e.what()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static constexpr auto GetEnumerated(boost::asio::streambuf& streambuf) {
|
|
||||||
return std::views::zip(std::views::iota(std::size_t{}, streambuf.size()), ::larra::xmpp::impl::GetCharsRangeFromBuf(streambuf));
|
|
||||||
}
|
|
||||||
using EnumeratedT = decltype(std::views::zip(std::views::iota(std::size_t{}, std::size_t{}),
|
|
||||||
::larra::xmpp::impl::GetCharsRangeFromBuf(std::declval<boost::asio::streambuf&>())));
|
|
||||||
struct Splitter {
|
|
||||||
EnumeratedT range;
|
|
||||||
struct Sentinel {
|
|
||||||
std::ranges::sentinel_t<EnumeratedT> end;
|
|
||||||
};
|
|
||||||
struct Iterator {
|
|
||||||
std::ranges::iterator_t<EnumeratedT> it;
|
|
||||||
std::ranges::sentinel_t<EnumeratedT> end;
|
|
||||||
friend constexpr auto operator==(const Iterator& self, const Sentinel& it) -> bool {
|
|
||||||
return self.it == it.end;
|
|
||||||
}
|
|
||||||
auto operator++() -> Iterator& {
|
|
||||||
if(this->it == this->end) {
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
this->it = std::ranges::find(this->it, this->end, '>', [](auto v) {
|
|
||||||
auto [_, c] = v;
|
|
||||||
return c;
|
|
||||||
});
|
|
||||||
if(this->it != this->end) {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
};
|
|
||||||
auto operator*() const {
|
|
||||||
return *it;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
auto begin() -> Iterator {
|
|
||||||
return Iterator{.it = std::ranges::begin(this->range), .end = std::ranges::end(this->range)};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto end() -> Sentinel {
|
|
||||||
return {.end = std::ranges::end(this->range)};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Socket>
|
template <typename Socket>
|
||||||
auto ReadStartStream(RawXmlStream<Socket>& stream) -> boost::asio::awaitable<ServerToUserStream> {
|
auto ReadStartStream(XmlStream<Socket>& stream) -> boost::asio::awaitable<ServerToUserStream> {
|
||||||
auto doc = (co_await stream.ReadOne(), co_await stream.ReadOne());
|
co_return (co_await stream.ReadOne(), co_await stream.template ReadOne<ServerToUserStream>());
|
||||||
co_return ServerToUserStream::Parse(doc->get_root_node());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Socket>
|
template <typename Socket>
|
||||||
inline auto operator()(RawXmlStream<Socket> stream)
|
inline auto operator()(XmlStream<Socket> stream)
|
||||||
-> boost::asio::awaitable<std::variant<Client<Socket>, Client<boost::asio::ssl::stream<Socket>>>> {
|
-> boost::asio::awaitable<std::variant<Client<Socket>, Client<boost::asio::ssl::stream<Socket>>>> {
|
||||||
co_await this->Connect(stream.next_layer(), co_await this->Resolve());
|
co_await this->Connect(stream.next_layer(), co_await this->Resolve());
|
||||||
|
|
||||||
|
@ -339,7 +297,7 @@ struct ClientCreateVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Socket>
|
template <typename Socket>
|
||||||
inline auto operator()(RawXmlStream<boost::asio::ssl::stream<Socket>> stream)
|
inline auto operator()(XmlStream<boost::asio::ssl::stream<Socket>> stream)
|
||||||
-> boost::asio::awaitable<std::variant<Client<Socket>, Client<boost::asio::ssl::stream<Socket>>>> {
|
-> boost::asio::awaitable<std::variant<Client<Socket>, Client<boost::asio::ssl::stream<Socket>>>> {
|
||||||
auto& socket = stream.next_layer();
|
auto& socket = stream.next_layer();
|
||||||
co_await this->Connect(socket.next_layer(), co_await this->Resolve());
|
co_await this->Connect(socket.next_layer(), co_await this->Resolve());
|
||||||
|
@ -353,14 +311,14 @@ struct ClientCreateVisitor {
|
||||||
throw std::runtime_error("XMPP server not support STARTTLS");
|
throw std::runtime_error("XMPP server not support STARTTLS");
|
||||||
}
|
}
|
||||||
socket.next_layer().close();
|
socket.next_layer().close();
|
||||||
co_return co_await (*this)(RawXmlStream<Socket>{Socket{std::move(socket.next_layer())}, std::move(stream.streambuf)});
|
co_return co_await (*this)(XmlStream<Socket>{Socket{std::move(socket.next_layer())}, std::move(stream.streambuf)});
|
||||||
}
|
}
|
||||||
co_await this->ProcessTls(stream);
|
co_await this->ProcessTls(stream);
|
||||||
co_await stream.Send(UserStream{.from = account.Jid(), .to = account.Jid().server}, socket.next_layer());
|
co_await stream.Send(UserStream{.from = account.Jid(), .to = account.Jid().server}, socket.next_layer());
|
||||||
auto newStreamHeader = co_await this->ReadStartStream(stream);
|
auto newStreamHeader = co_await this->ReadStartStream(stream);
|
||||||
auto newFeatures = co_await stream.template Read<StreamFeatures>();
|
auto newFeatures = co_await stream.template Read<StreamFeatures>();
|
||||||
co_await this->Auth(stream, std::move(newStreamHeader), std::move(newFeatures));
|
co_await this->Auth(stream, std::move(newStreamHeader), std::move(newFeatures));
|
||||||
co_return Client{std::move(this->account).Jid(), RawXmlStream{std::move(socket)}};
|
co_return Client{std::move(this->account).Jid(), XmlStream{std::move(socket)}};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -374,8 +332,8 @@ inline auto CreateClient(UserAccount account, Options options = {})
|
||||||
co_return co_await std::visit(
|
co_return co_await std::visit(
|
||||||
impl::ClientCreateVisitor{.account = std::move(account), .options = options},
|
impl::ClientCreateVisitor{.account = std::move(account), .options = options},
|
||||||
options.useTls == Options::kNever
|
options.useTls == Options::kNever
|
||||||
? std::variant<RawXmlStream<Socket>, RawXmlStream<boost::asio::ssl::stream<Socket>>>{RawXmlStream{Socket{executor}}}
|
? std::variant<XmlStream<Socket>, XmlStream<boost::asio::ssl::stream<Socket>>>{XmlStream{Socket{executor}}}
|
||||||
: RawXmlStream{boost::asio::ssl::stream<Socket>(executor, ctx)});
|
: XmlStream{boost::asio::ssl::stream<Socket>(executor, ctx)});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace larra::xmpp::client
|
} // namespace larra::xmpp::client
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
|
|
||||||
#include <larra/encryption.hpp>
|
#include <larra/encryption.hpp>
|
||||||
#include <larra/jid.hpp>
|
#include <larra/jid.hpp>
|
||||||
#include <larra/raw_xml_stream.hpp>
|
#include <larra/xml_stream.hpp>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace larra::xmpp::client::features {
|
namespace larra::xmpp::client::features {
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -17,6 +17,7 @@ struct SaslMechanisms {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StreamFeatures {
|
struct StreamFeatures {
|
||||||
|
static constexpr auto kDefaultName = "stream:features";
|
||||||
struct StartTlsType {
|
struct StartTlsType {
|
||||||
Required required;
|
Required required;
|
||||||
[[nodiscard]] constexpr auto Required(Required required) const -> StartTlsType {
|
[[nodiscard]] constexpr auto Required(Required required) const -> StartTlsType {
|
||||||
|
|
|
@ -13,6 +13,7 @@ struct BareJid {
|
||||||
[[nodiscard]] static auto Parse(std::string_view jid) -> BareJid;
|
[[nodiscard]] static auto Parse(std::string_view jid) -> BareJid;
|
||||||
friend auto ToString(const BareJid& jid) -> std::string;
|
friend auto ToString(const BareJid& jid) -> std::string;
|
||||||
|
|
||||||
|
constexpr auto operator==(const BareJid&) const -> bool = default;
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
[[nodiscard]] constexpr auto Username(this Self&& self, std::string username) -> BareJid {
|
[[nodiscard]] constexpr auto Username(this Self&& self, std::string username) -> BareJid {
|
||||||
return utils::FieldSetHelper::With<"username", BareJid>(std::forward<Self>(self), std::move(username));
|
return utils::FieldSetHelper::With<"username", BareJid>(std::forward<Self>(self), std::move(username));
|
||||||
|
@ -30,6 +31,8 @@ struct BareResourceJid {
|
||||||
[[nodiscard]] static auto Parse(std::string_view jid) -> BareResourceJid;
|
[[nodiscard]] static auto Parse(std::string_view jid) -> BareResourceJid;
|
||||||
friend auto ToString(const BareResourceJid& jid) -> std::string;
|
friend auto ToString(const BareResourceJid& jid) -> std::string;
|
||||||
|
|
||||||
|
constexpr auto operator==(const BareResourceJid&) const -> bool = default;
|
||||||
|
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
[[nodiscard]] constexpr auto Server(this Self&& self, std::string server) -> BareResourceJid {
|
[[nodiscard]] constexpr auto Server(this Self&& self, std::string server) -> BareResourceJid {
|
||||||
return utils::FieldSetHelper::With<"server", BareResourceJid>(std::forward<Self>(self), std::move(server));
|
return utils::FieldSetHelper::With<"server", BareResourceJid>(std::forward<Self>(self), std::move(server));
|
||||||
|
@ -48,6 +51,8 @@ struct FullJid {
|
||||||
[[nodiscard]] static auto Parse(std::string_view jid) -> FullJid;
|
[[nodiscard]] static auto Parse(std::string_view jid) -> FullJid;
|
||||||
friend auto ToString(const FullJid& jid) -> std::string;
|
friend auto ToString(const FullJid& jid) -> std::string;
|
||||||
|
|
||||||
|
constexpr auto operator==(const FullJid&) const -> bool = default;
|
||||||
|
|
||||||
template <typename Self>
|
template <typename Self>
|
||||||
[[nodiscard]] constexpr auto Username(this Self&& self, std::string username) -> FullJid {
|
[[nodiscard]] constexpr auto Username(this Self&& self, std::string username) -> FullJid {
|
||||||
return utils::FieldSetHelper::With<"username", FullJid>(std::forward<Self>(self), std::move(username));
|
return utils::FieldSetHelper::With<"username", FullJid>(std::forward<Self>(self), std::move(username));
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
#include <boost/asio/write.hpp>
|
#include <boost/asio/write.hpp>
|
||||||
#include <larra/raw_xml_stream.hpp>
|
#include <larra/xml_stream.hpp>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <libxml++/libxml++.h>
|
#include <libxml++/libxml++.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include <nameof.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utempl/utils.hpp>
|
#include <utempl/utils.hpp>
|
||||||
|
|
||||||
|
@ -80,23 +81,35 @@ struct SerializationBase {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
|
static constexpr auto StartCheck(xmlpp::Element* element) -> bool {
|
||||||
|
if constexpr(requires {
|
||||||
|
{ T::StartCheck(element) } -> std::same_as<bool>;
|
||||||
|
}) {
|
||||||
|
return T::StartCheck(element);
|
||||||
|
} else {
|
||||||
|
return element && element->get_name() == kDefaultName;
|
||||||
|
}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct Serialization : SerializationBase<T> {
|
struct Serialization : SerializationBase<T> {
|
||||||
[[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) -> T {
|
[[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) -> T {
|
||||||
|
if(!Serialization::StartCheck(element)) {
|
||||||
|
throw std::runtime_error("StartCheck failed");
|
||||||
|
}
|
||||||
return T::Parse(element);
|
return T::Parse(element);
|
||||||
}
|
}
|
||||||
[[nodiscard]] static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<T> {
|
[[nodiscard]] static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<T> {
|
||||||
if constexpr(HasTryParse<T>) {
|
if constexpr(HasTryParse<T>) {
|
||||||
return T::TryParse(element);
|
return Serialization::StartCheck(element) ? T::TryParse(element) : std::nullopt;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return T::Parse(element);
|
return Serialization::StartCheck(element) ? std::optional{T::Parse(element)} : std::nullopt;
|
||||||
} catch(const std::exception& e) {
|
} catch(const std::exception& e) {
|
||||||
SPDLOG_WARN("Failed Parse but no TryParse found: {}", e.what());
|
SPDLOG_WARN("Type {}: Failed Parse but no TryParse found: {}", e.what(), nameof::nameof_type<T>());
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
SPDLOG_WARN("Failed Parse but no TryParse found");
|
SPDLOG_WARN("Type {}: Failed Parse but no TryParse found", nameof::nameof_type<T>());
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -122,9 +135,17 @@ struct Serialization<std::optional<T>> : SerializationBase<T> {
|
||||||
|
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
struct Serialization<std::variant<Ts...>> : SerializationBase<> {
|
struct Serialization<std::variant<Ts...>> : SerializationBase<> {
|
||||||
|
static constexpr auto StartCheck(xmlpp::Element* element) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
[[nodiscard]] static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<std::variant<Ts...>> {
|
[[nodiscard]] static constexpr auto TryParse(xmlpp::Element* element) -> std::optional<std::variant<Ts...>> {
|
||||||
return utempl::FirstOf(utempl::Tuple{[&] {
|
return utempl::FirstOf(utempl::Tuple{[&] -> std::optional<Ts> {
|
||||||
|
if(Serialization<Ts>::StartCheck(element)) {
|
||||||
return Serialization<Ts>::TryParse(element);
|
return Serialization<Ts>::TryParse(element);
|
||||||
|
} else {
|
||||||
|
SPDLOG_DEBUG("StartCheck failed for type {}", nameof::nameof_type<Ts>());
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
}...},
|
}...},
|
||||||
std::optional<std::variant<Ts...>>{});
|
std::optional<std::variant<Ts...>>{});
|
||||||
}
|
}
|
||||||
|
@ -140,4 +161,19 @@ struct Serialization<std::variant<Ts...>> : SerializationBase<> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Serialization<std::monostate> : SerializationBase<> {
|
||||||
|
static constexpr auto StartCheck(xmlpp::Element*) -> bool {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
[[nodiscard]] static constexpr auto TryParse(xmlpp::Element*) -> std::optional<std::monostate> {
|
||||||
|
return std::monostate{};
|
||||||
|
}
|
||||||
|
[[nodiscard]] static constexpr auto Parse(xmlpp::Element*) -> std::monostate {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
static constexpr auto Serialize(xmlpp::Element*, const std::monostate&) -> void {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace larra::xmpp
|
} // namespace larra::xmpp
|
||||||
|
|
|
@ -48,12 +48,15 @@ constexpr auto ToKebabCaseName() -> std::string_view {
|
||||||
|
|
||||||
namespace error::stream {
|
namespace error::stream {
|
||||||
|
|
||||||
|
struct BaseError : std::exception {};
|
||||||
|
|
||||||
|
// DO NOT MOVE TO ANOTHER NAMESPACE(where no heirs). VIA friend A FUNCTION IS ADDED THAT VIA ADL WILL BE SEARCHED FOR HEIRS
|
||||||
|
// C++20 modules very unstable in clangd :(
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct BaseError : std::exception {
|
struct ErrorImpl : BaseError {
|
||||||
static constexpr auto kDefaultName = "error";
|
static constexpr auto kDefaultName = "stream:error";
|
||||||
static constexpr auto kDefaultNamespace = "stream";
|
|
||||||
static inline const auto kKebabCaseName = static_cast<std::string>(impl::ToKebabCaseName<T>());
|
static inline const auto kKebabCaseName = static_cast<std::string>(impl::ToKebabCaseName<T>());
|
||||||
static inline const std::string kErrorContentNamespace = "urn:ietf:params:xml:ns:xmpp-streams";
|
|
||||||
static constexpr auto kErrorMessage = [] -> std::string_view {
|
static constexpr auto kErrorMessage = [] -> std::string_view {
|
||||||
static constexpr auto str = [] {
|
static constexpr auto str = [] {
|
||||||
return std::array{std::string_view{"Stream Error: "}, nameof::nameof_short_type<T>(), std::string_view{"\0", 1}} | std::views::join;
|
return std::array{std::string_view{"Stream Error: "}, nameof::nameof_short_type<T>(), std::string_view{"\0", 1}} | std::views::join;
|
||||||
|
@ -76,38 +79,38 @@ struct BaseError : std::exception {
|
||||||
}
|
}
|
||||||
friend constexpr auto operator<<(xmlpp::Element* element, const T& obj) -> void {
|
friend constexpr auto operator<<(xmlpp::Element* element, const T& obj) -> void {
|
||||||
auto node = element->add_child_element(kKebabCaseName);
|
auto node = element->add_child_element(kKebabCaseName);
|
||||||
node->set_namespace_declaration(kErrorContentNamespace);
|
node->set_namespace_declaration("urn:ietf:params:xml:ns:xmpp-streams");
|
||||||
}
|
}
|
||||||
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
|
[[nodiscard]] constexpr auto what() const noexcept -> const char* override {
|
||||||
return kErrorMessage.data();
|
return kErrorMessage.data();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BadFormat : BaseError<BadFormat> {};
|
struct BadFormat : ErrorImpl<BadFormat> {};
|
||||||
struct BadNamespacePrefix : BaseError<BadNamespacePrefix> {};
|
struct BadNamespacePrefix : ErrorImpl<BadNamespacePrefix> {};
|
||||||
struct Conflict : BaseError<Conflict> {};
|
struct Conflict : ErrorImpl<Conflict> {};
|
||||||
struct ConnectionTimeout : BaseError<ConnectionTimeout> {};
|
struct ConnectionTimeout : ErrorImpl<ConnectionTimeout> {};
|
||||||
struct HostGone : BaseError<HostGone> {};
|
struct HostGone : ErrorImpl<HostGone> {};
|
||||||
struct HostUnknown : BaseError<HostUnknown> {};
|
struct HostUnknown : ErrorImpl<HostUnknown> {};
|
||||||
struct ImproperAdressing : BaseError<ImproperAdressing> {};
|
struct ImproperAdressing : ErrorImpl<ImproperAdressing> {};
|
||||||
struct InternalServerError : BaseError<InternalServerError> {};
|
struct InternalServerError : ErrorImpl<InternalServerError> {};
|
||||||
struct InvalidForm : BaseError<InvalidForm> {};
|
struct InvalidForm : ErrorImpl<InvalidForm> {};
|
||||||
struct InvalidNamespace : BaseError<InvalidNamespace> {};
|
struct InvalidNamespace : ErrorImpl<InvalidNamespace> {};
|
||||||
struct InvalidXml : BaseError<InvalidXml> {};
|
struct InvalidXml : ErrorImpl<InvalidXml> {};
|
||||||
struct NotAuthorized : BaseError<NotAuthorized> {};
|
struct NotAuthorized : ErrorImpl<NotAuthorized> {};
|
||||||
struct NotWellFormed : BaseError<NotWellFormed> {};
|
struct NotWellFormed : ErrorImpl<NotWellFormed> {};
|
||||||
struct PolicyViolation : BaseError<PolicyViolation> {};
|
struct PolicyViolation : ErrorImpl<PolicyViolation> {};
|
||||||
struct RemoteConnectionFailed : BaseError<RemoteConnectionFailed> {};
|
struct RemoteConnectionFailed : ErrorImpl<RemoteConnectionFailed> {};
|
||||||
struct Reset : BaseError<Reset> {};
|
struct Reset : ErrorImpl<Reset> {};
|
||||||
struct ResourceConstraint : BaseError<ResourceConstraint> {};
|
struct ResourceConstraint : ErrorImpl<ResourceConstraint> {};
|
||||||
struct RestrictedXml : BaseError<RestrictedXml> {};
|
struct RestrictedXml : ErrorImpl<RestrictedXml> {};
|
||||||
struct SeeOtherHost : BaseError<SeeOtherHost> {};
|
struct SeeOtherHost : ErrorImpl<SeeOtherHost> {};
|
||||||
struct SystemShutdown : BaseError<SystemShutdown> {};
|
struct SystemShutdown : ErrorImpl<SystemShutdown> {};
|
||||||
struct UndefinedCondition : BaseError<UndefinedCondition> {};
|
struct UndefinedCondition : ErrorImpl<UndefinedCondition> {};
|
||||||
struct UnsupportedEncoding : BaseError<UnsupportedEncoding> {};
|
struct UnsupportedEncoding : ErrorImpl<UnsupportedEncoding> {};
|
||||||
struct UnsupportedFeature : BaseError<UnsupportedFeature> {};
|
struct UnsupportedFeature : ErrorImpl<UnsupportedFeature> {};
|
||||||
struct UnsupportedStanzaType : BaseError<UnsupportedStanzaType> {};
|
struct UnsupportedStanzaType : ErrorImpl<UnsupportedStanzaType> {};
|
||||||
struct UnsupportedVersion : BaseError<UnsupportedVersion> {};
|
struct UnsupportedVersion : ErrorImpl<UnsupportedVersion> {};
|
||||||
|
|
||||||
} // namespace error::stream
|
} // namespace error::stream
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <boost/asio/write.hpp>
|
#include <boost/asio/write.hpp>
|
||||||
#include <boost/system/result.hpp>
|
#include <boost/system/result.hpp>
|
||||||
#include <larra/serialization.hpp>
|
#include <larra/serialization.hpp>
|
||||||
|
#include <larra/stream_error.hpp>
|
||||||
#include <larra/utils.hpp>
|
#include <larra/utils.hpp>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <utempl/utils.hpp>
|
#include <utempl/utils.hpp>
|
||||||
|
@ -89,8 +90,8 @@ auto IsExtraContentAtTheDocument(const _xmlError* error) -> bool;
|
||||||
} // namespace impl
|
} // namespace impl
|
||||||
|
|
||||||
template <typename Stream, typename BufferType = boost::asio::streambuf>
|
template <typename Stream, typename BufferType = boost::asio::streambuf>
|
||||||
struct RawXmlStream : Stream {
|
struct XmlStream : Stream {
|
||||||
constexpr RawXmlStream(Stream stream, std::unique_ptr<BufferType> buff = std::make_unique<BufferType>()) :
|
constexpr XmlStream(Stream stream, std::unique_ptr<BufferType> buff = std::make_unique<BufferType>()) :
|
||||||
Stream(std::forward<Stream>(stream)), streambuf(std::move(buff)) {};
|
Stream(std::forward<Stream>(stream)), streambuf(std::move(buff)) {};
|
||||||
using Stream::Stream;
|
using Stream::Stream;
|
||||||
auto next_layer() -> Stream& {
|
auto next_layer() -> Stream& {
|
||||||
|
@ -101,7 +102,7 @@ struct RawXmlStream : Stream {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ReadOne(auto& socket) -> boost::asio::awaitable<std::unique_ptr<xmlpp::Document>> {
|
auto ReadOneRaw(auto& socket) -> boost::asio::awaitable<std::unique_ptr<xmlpp::Document>> {
|
||||||
auto doc = std::make_unique<xmlpp::Document>();
|
auto doc = std::make_unique<xmlpp::Document>();
|
||||||
impl::Parser parser(*doc);
|
impl::Parser parser(*doc);
|
||||||
for(;;) {
|
for(;;) {
|
||||||
|
@ -144,10 +145,21 @@ struct RawXmlStream : Stream {
|
||||||
co_return doc;
|
co_return doc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto ReadOne() -> boost::asio::awaitable<std::unique_ptr<xmlpp::Document>> {
|
template <typename T>
|
||||||
co_return co_await this->ReadOne(this->next_layer());
|
auto ReadOneRaw(auto& stream) -> boost::asio::awaitable<T> {
|
||||||
|
auto doc = co_await this->ReadOneRaw(stream);
|
||||||
|
co_return Serialization<T>::Parse(doc->get_root_node());
|
||||||
}
|
}
|
||||||
inline auto Read(auto& socket) -> boost::asio::awaitable<std::unique_ptr<xmlpp::Document>> {
|
|
||||||
|
template <typename T>
|
||||||
|
auto ReadOneRaw(auto& stream) -> boost::asio::awaitable<T>
|
||||||
|
requires requires(std::unique_ptr<xmlpp::Document> ptr) {
|
||||||
|
{ Serialization<T>::Parse(std::move(ptr)) } -> std::same_as<T>;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
co_return Serialization<T>::Parse(co_await this->ReadOneRaw(stream));
|
||||||
|
}
|
||||||
|
inline auto ReadRaw(auto& socket) -> boost::asio::awaitable<std::unique_ptr<xmlpp::Document>> {
|
||||||
auto doc = std::make_unique<xmlpp::Document>(); // Not movable :(
|
auto doc = std::make_unique<xmlpp::Document>(); // Not movable :(
|
||||||
impl::Parser parser(*doc);
|
impl::Parser parser(*doc);
|
||||||
std::size_t lines = 1;
|
std::size_t lines = 1;
|
||||||
|
@ -186,7 +198,7 @@ struct RawXmlStream : Stream {
|
||||||
|
|
||||||
if(!error) {
|
if(!error) {
|
||||||
auto linesAdd = impl::CountLines(impl::BufferToStringView(buff, n));
|
auto linesAdd = impl::CountLines(impl::BufferToStringView(buff, n));
|
||||||
SPDLOG_DEBUG("Readed {} bytes for RawXmlStream with {} lines", n, linesAdd);
|
SPDLOG_DEBUG("Readed {} bytes for XmlStream with {} lines", n, linesAdd);
|
||||||
|
|
||||||
lines += linesAdd;
|
lines += linesAdd;
|
||||||
if(linesAdd == 0) {
|
if(linesAdd == 0) {
|
||||||
|
@ -209,27 +221,56 @@ struct RawXmlStream : Stream {
|
||||||
co_return doc;
|
co_return doc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto Read() -> boost::asio::awaitable<std::unique_ptr<xmlpp::Document>> {
|
|
||||||
co_return co_await this->Read(this->next_layer());
|
|
||||||
}
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto Read(auto& stream) -> boost::asio::awaitable<T> {
|
auto ReadRaw(auto& stream) -> boost::asio::awaitable<T> {
|
||||||
auto doc = co_await this->Read(stream);
|
auto doc = co_await this->ReadRaw(stream);
|
||||||
co_return Serialization<T>::Parse(doc->get_root_node());
|
co_return Serialization<T>::Parse(doc->get_root_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto Read(auto& stream) -> boost::asio::awaitable<T>
|
auto ReadRaw(auto& stream) -> boost::asio::awaitable<T>
|
||||||
requires requires(std::unique_ptr<xmlpp::Document> ptr) {
|
requires requires(std::unique_ptr<xmlpp::Document> ptr) {
|
||||||
{ Serialization<T>::Parse(std::move(ptr)) } -> std::same_as<T>;
|
{ Serialization<T>::Parse(std::move(ptr)) } -> std::same_as<T>;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
co_return Serialization<T>::Parse(co_await this->Read(stream));
|
co_return Serialization<T>::Parse(co_await this->ReadRaw(stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto Read() -> boost::asio::awaitable<T> {
|
auto ReadImpl(boost::asio::awaitable<std::variant<StreamError, T>> awaitable) -> boost::asio::awaitable<T> {
|
||||||
co_return co_await this->template Read<T>(this->next_layer());
|
co_return std::visit(utempl::Overloaded(
|
||||||
|
[](T value) -> T {
|
||||||
|
return std::move(value);
|
||||||
|
},
|
||||||
|
[](StreamError error) -> T {
|
||||||
|
std::visit(
|
||||||
|
[](auto error) {
|
||||||
|
throw error;
|
||||||
|
},
|
||||||
|
error);
|
||||||
|
std::unreachable();
|
||||||
|
}),
|
||||||
|
co_await std::move(awaitable));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename T = std::monostate>
|
||||||
|
auto Read(auto& stream) {
|
||||||
|
return this->ReadImpl(this->ReadRaw<std::variant<StreamError, T>>(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T = std::monostate>
|
||||||
|
auto Read() {
|
||||||
|
return this->Read<T>(this->next_layer());
|
||||||
|
}
|
||||||
|
template <typename T = std::monostate>
|
||||||
|
auto ReadOne(auto& stream) {
|
||||||
|
return this->ReadImpl(this->ReadOneRaw<std::variant<StreamError, T>>(stream));
|
||||||
|
}
|
||||||
|
template <typename T = std::monostate>
|
||||||
|
auto ReadOne() {
|
||||||
|
return this->ReadOne<T>(this->next_layer());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Send(xmlpp::Document& doc, auto& stream, bool bAddXmlDecl, bool removeEnd) const -> boost::asio::awaitable<void> {
|
auto Send(xmlpp::Document& doc, auto& stream, bool bAddXmlDecl, bool removeEnd) const -> boost::asio::awaitable<void> {
|
||||||
|
@ -270,7 +311,7 @@ struct RawXmlStream : Stream {
|
||||||
co_await this->Send(xso, this->next_layer());
|
co_await this->Send(xso, this->next_layer());
|
||||||
}
|
}
|
||||||
|
|
||||||
RawXmlStream(RawXmlStream&& other) = default;
|
XmlStream(XmlStream&& other) = default;
|
||||||
|
|
||||||
std::unique_ptr<BufferType> streambuf; // Not movable :(
|
std::unique_ptr<BufferType> streambuf; // Not movable :(
|
||||||
};
|
};
|
|
@ -1,8 +1,8 @@
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
|
|
||||||
#include <larra/impl/public_cast.hpp>
|
#include <larra/impl/public_cast.hpp>
|
||||||
#include <larra/raw_xml_stream.hpp>
|
|
||||||
#include <larra/utils.hpp>
|
#include <larra/utils.hpp>
|
||||||
|
#include <larra/xml_stream.hpp>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
#include <larra/features.hpp>
|
#include <larra/features.hpp>
|
||||||
#include <larra/impl/mock_socket.hpp>
|
#include <larra/impl/mock_socket.hpp>
|
||||||
#include <larra/raw_xml_stream.hpp>
|
|
||||||
#include <larra/stream.hpp>
|
#include <larra/stream.hpp>
|
||||||
|
#include <larra/xml_stream.hpp>
|
||||||
#include <utempl/utils.hpp>
|
#include <utempl/utils.hpp>
|
||||||
|
|
||||||
namespace larra::xmpp {
|
namespace larra::xmpp {
|
||||||
|
@ -23,7 +23,7 @@ constexpr std::string_view kDoc3 =
|
||||||
constexpr std::string_view kDoc4 =
|
constexpr std::string_view kDoc4 =
|
||||||
R"(<?xml version='1.0'?><stream:stream id='68321991947053239' version='1.0' xml:lang='en' xmlns:stream='http://etherx.jabber.org/streams' to='test1@localhost' from='localhost' xmlns='jabber:client'>)";
|
R"(<?xml version='1.0'?><stream:stream id='68321991947053239' version='1.0' xml:lang='en' xmlns:stream='http://etherx.jabber.org/streams' to='test1@localhost' from='localhost' xmlns='jabber:client'>)";
|
||||||
|
|
||||||
TEST(RawXmlStream, ReadByOne) {
|
TEST(XmlStream, ReadByOne) {
|
||||||
boost::asio::io_context context;
|
boost::asio::io_context context;
|
||||||
bool error{};
|
bool error{};
|
||||||
|
|
||||||
|
@ -31,14 +31,14 @@ TEST(RawXmlStream, ReadByOne) {
|
||||||
context,
|
context,
|
||||||
// NOLINTNEXTLINE: Safe
|
// NOLINTNEXTLINE: Safe
|
||||||
[&] -> boost::asio::awaitable<void> {
|
[&] -> boost::asio::awaitable<void> {
|
||||||
RawXmlStream stream{impl::MockSocket{context.get_executor(), 1}};
|
XmlStream stream{impl::MockSocket{context.get_executor(), 1}};
|
||||||
stream.AddReceivedData(kDoc);
|
stream.AddReceivedData(kDoc);
|
||||||
try {
|
try {
|
||||||
auto doc = co_await stream.Read();
|
auto doc = co_await stream.ReadRaw(stream.next_layer());
|
||||||
auto node = doc->get_root_node();
|
auto node = doc->get_root_node();
|
||||||
EXPECT_EQ(node->get_name(), std::string_view{"doc"});
|
EXPECT_EQ(node->get_name(), std::string_view{"doc"});
|
||||||
EXPECT_FALSE(node->has_child_text());
|
EXPECT_FALSE(node->has_child_text());
|
||||||
auto doc2 = co_await stream.Read();
|
auto doc2 = co_await stream.ReadRaw(stream.next_layer());
|
||||||
auto node2 = doc2->get_root_node();
|
auto node2 = doc2->get_root_node();
|
||||||
EXPECT_EQ(node2->get_name(), std::string_view{"doc2"});
|
EXPECT_EQ(node2->get_name(), std::string_view{"doc2"});
|
||||||
EXPECT_FALSE(node2->has_child_text());
|
EXPECT_FALSE(node2->has_child_text());
|
||||||
|
@ -54,20 +54,20 @@ TEST(RawXmlStream, ReadByOne) {
|
||||||
EXPECT_FALSE(error);
|
EXPECT_FALSE(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RawXmlStream, ReadAll) {
|
TEST(XmlStream, ReadAll) {
|
||||||
boost::asio::io_context context;
|
boost::asio::io_context context;
|
||||||
bool error{};
|
bool error{};
|
||||||
boost::asio::co_spawn(
|
boost::asio::co_spawn(
|
||||||
context, // NOLINTNEXTLINE: Safe
|
context, // NOLINTNEXTLINE: Safe
|
||||||
[&] -> boost::asio::awaitable<void> {
|
[&] -> boost::asio::awaitable<void> {
|
||||||
RawXmlStream stream{impl::MockSocket{context.get_executor(), kDoc.size()}};
|
XmlStream stream{impl::MockSocket{context.get_executor(), kDoc.size()}};
|
||||||
stream.AddReceivedData(kDoc);
|
stream.AddReceivedData(kDoc);
|
||||||
try {
|
try {
|
||||||
auto doc = co_await stream.Read();
|
auto doc = co_await stream.ReadRaw(stream.next_layer());
|
||||||
auto node = doc->get_root_node();
|
auto node = doc->get_root_node();
|
||||||
EXPECT_EQ(node->get_name(), std::string_view{"doc"});
|
EXPECT_EQ(node->get_name(), std::string_view{"doc"});
|
||||||
EXPECT_FALSE(node->has_child_text());
|
EXPECT_FALSE(node->has_child_text());
|
||||||
auto doc2 = co_await stream.Read();
|
auto doc2 = co_await stream.ReadRaw(stream.next_layer());
|
||||||
auto node2 = doc2->get_root_node();
|
auto node2 = doc2->get_root_node();
|
||||||
EXPECT_EQ(node2->get_name(), std::string_view{"doc2"});
|
EXPECT_EQ(node2->get_name(), std::string_view{"doc2"});
|
||||||
EXPECT_FALSE(node2->has_child_text());
|
EXPECT_FALSE(node2->has_child_text());
|
||||||
|
@ -82,20 +82,20 @@ TEST(RawXmlStream, ReadAll) {
|
||||||
EXPECT_FALSE(error);
|
EXPECT_FALSE(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RawXmlStream, ReadAllWithEnd) {
|
TEST(XmlStream, ReadAllWithEnd) {
|
||||||
boost::asio::io_context context;
|
boost::asio::io_context context;
|
||||||
bool error{};
|
bool error{};
|
||||||
boost::asio::co_spawn(
|
boost::asio::co_spawn(
|
||||||
context, // NOLINTNEXTLINE: Safe
|
context, // NOLINTNEXTLINE: Safe
|
||||||
[&] -> boost::asio::awaitable<void> {
|
[&] -> boost::asio::awaitable<void> {
|
||||||
RawXmlStream stream{impl::MockSocket{context.get_executor(), kDoc2.size()}};
|
XmlStream stream{impl::MockSocket{context.get_executor(), kDoc2.size()}};
|
||||||
stream.AddReceivedData(kDoc2);
|
stream.AddReceivedData(kDoc2);
|
||||||
try {
|
try {
|
||||||
auto doc = co_await stream.Read();
|
auto doc = co_await stream.ReadRaw(stream.next_layer());
|
||||||
auto node = doc->get_root_node();
|
auto node = doc->get_root_node();
|
||||||
EXPECT_EQ(node->get_name(), std::string_view{"doc"});
|
EXPECT_EQ(node->get_name(), std::string_view{"doc"});
|
||||||
EXPECT_FALSE(node->has_child_text());
|
EXPECT_FALSE(node->has_child_text());
|
||||||
auto doc2 = co_await stream.Read();
|
auto doc2 = co_await stream.ReadRaw(stream.next_layer());
|
||||||
auto node2 = doc2->get_root_node();
|
auto node2 = doc2->get_root_node();
|
||||||
EXPECT_EQ(node2->get_name(), std::string_view{"doc2"});
|
EXPECT_EQ(node2->get_name(), std::string_view{"doc2"});
|
||||||
EXPECT_FALSE(node2->has_child_text());
|
EXPECT_FALSE(node2->has_child_text());
|
||||||
|
@ -110,13 +110,13 @@ TEST(RawXmlStream, ReadAllWithEnd) {
|
||||||
EXPECT_FALSE(error);
|
EXPECT_FALSE(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RawXmlStream, ReadFeatures) {
|
TEST(XmlStream, ReadFeatures) {
|
||||||
boost::asio::io_context context;
|
boost::asio::io_context context;
|
||||||
bool error{};
|
bool error{};
|
||||||
boost::asio::co_spawn(
|
boost::asio::co_spawn(
|
||||||
context, // NOLINTNEXTLINE: Safe
|
context, // NOLINTNEXTLINE: Safe
|
||||||
[&] -> boost::asio::awaitable<void> {
|
[&] -> boost::asio::awaitable<void> {
|
||||||
RawXmlStream stream{impl::MockSocket{context.get_executor(), kDoc3.size()}};
|
XmlStream stream{impl::MockSocket{context.get_executor(), kDoc3.size()}};
|
||||||
stream.AddReceivedData(kDoc3);
|
stream.AddReceivedData(kDoc3);
|
||||||
try {
|
try {
|
||||||
auto features = co_await stream.template Read<StreamFeatures>();
|
auto features = co_await stream.template Read<StreamFeatures>();
|
||||||
|
@ -139,13 +139,13 @@ struct SomeStruct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(RawXmlStream, Write) {
|
TEST(XmlStream, Write) {
|
||||||
boost::asio::io_context context;
|
boost::asio::io_context context;
|
||||||
bool error{};
|
bool error{};
|
||||||
boost::asio::co_spawn(
|
boost::asio::co_spawn(
|
||||||
context, // NOLINTNEXTLINE: Safe
|
context, // NOLINTNEXTLINE: Safe
|
||||||
[&] -> boost::asio::awaitable<void> {
|
[&] -> boost::asio::awaitable<void> {
|
||||||
RawXmlStream stream1{impl::MockSocket{context.get_executor()}};
|
XmlStream stream1{impl::MockSocket{context.get_executor()}};
|
||||||
auto stream = std::move(stream1);
|
auto stream = std::move(stream1);
|
||||||
try {
|
try {
|
||||||
co_await stream.Send(SomeStruct{});
|
co_await stream.Send(SomeStruct{});
|
||||||
|
@ -160,23 +160,21 @@ TEST(RawXmlStream, Write) {
|
||||||
EXPECT_FALSE(error);
|
EXPECT_FALSE(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RawXmlStream, ReadOneByOne) {
|
TEST(XmlStream, ReadOneByOne) {
|
||||||
boost::asio::io_context context;
|
boost::asio::io_context context;
|
||||||
bool error{};
|
bool error{};
|
||||||
boost::asio::co_spawn(
|
boost::asio::co_spawn(
|
||||||
context,
|
context,
|
||||||
// NOLINTNEXTLINE: Safe
|
// NOLINTNEXTLINE: Safe
|
||||||
[&] -> boost::asio::awaitable<void> {
|
[&] -> boost::asio::awaitable<void> {
|
||||||
RawXmlStream stream{impl::MockSocket{context.get_executor(), 1}};
|
XmlStream stream{impl::MockSocket{context.get_executor(), 1}};
|
||||||
stream.AddReceivedData(kDoc4);
|
stream.AddReceivedData(kDoc4);
|
||||||
try {
|
try {
|
||||||
auto doc = (co_await stream.ReadOne(), co_await stream.ReadOne());
|
ServerToUserStream value = (co_await stream.ReadOne(), co_await stream.ReadOne<ServerToUserStream>());
|
||||||
auto node = doc->get_root_node();
|
EXPECT_EQ(value.id, "68321991947053239");
|
||||||
EXPECT_TRUE(node);
|
EXPECT_EQ(value.version, "1.0");
|
||||||
if(!node) {
|
EXPECT_EQ(value.to, BareJid::Parse("test1@localhost"));
|
||||||
co_return;
|
EXPECT_EQ(value.from, "localhost");
|
||||||
}
|
|
||||||
auto stream = ServerToUserStream::Parse(node);
|
|
||||||
} catch(const std::exception& err) {
|
} catch(const std::exception& err) {
|
||||||
SPDLOG_ERROR("{}", err.what());
|
SPDLOG_ERROR("{}", err.what());
|
||||||
error = true;
|
error = true;
|
Loading…
Reference in a new issue