Skip to content

Commit

Permalink
[feature] add qt test
Browse files Browse the repository at this point in the history
  • Loading branch information
BusyStudent committed Sep 28, 2024
1 parent b704e7d commit 19490ba
Show file tree
Hide file tree
Showing 12 changed files with 605 additions and 23 deletions.
2 changes: 1 addition & 1 deletion include/ilias/detail/expected.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
#include <ilias/error.hpp>

#if defined(__cpp_lib_expected)
#if __cpp_lib_expected > 202211L
#if __cpp_lib_expected >= 202211L
#include <expected>
#define ILIAS_STD_EXPECTED_HPP
#endif
Expand Down
10 changes: 7 additions & 3 deletions include/ilias/http/cookie.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ inline auto HttpCookieJar::cookiesForUrl(const Url &url) -> std::vector<HttpCook
}
auto host = url.host();
auto cur = std::string_view(host);
while (!cur.empty() && cur.contains('.')) { //for each level of the domain, www.google.com -> google.com
while (!cur.empty()) { //for each level of the domain, www.google.com -> .google.com -> google.com
auto iter = mCookies.find(cur);
if (iter != mCookies.end()) {
// Add all items in current domain to it
Expand All @@ -499,14 +499,18 @@ inline auto HttpCookieJar::cookiesForUrl(const Url &url) -> std::vector<HttpCook
}
}
// Find the next domain by dot
auto pos = cur.find('.', 1);
if (cur.starts_with('.')) {
cur.remove_prefix(1);
continue;
}
auto pos = cur.find('.');
if (pos == std::string::npos) {
break;
}
if (pos + 1 == cur.size()) { //< xxx. (dot on the last)
break;
}
cur = cur.substr(pos + 1);
cur = cur.substr(pos);
}
return ret;
}
Expand Down
53 changes: 45 additions & 8 deletions include/ilias/http/session.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <ilias/net/socks5.hpp>
#include <ilias/net/tcp.hpp>
#include <ilias/io/context.hpp>
#include <ilias/task.hpp>
#include <ilias/ssl.hpp>
#include <ilias/log.hpp>
#include <span>
Expand Down Expand Up @@ -101,6 +102,20 @@ class HttpSession {
* @param proxy
*/
auto setProxy(const Url &proxy) -> void { mProxy = proxy; }

/**
* @brief Get the Cookie Jar object
*
* @return HttpCookieJar*
*/
auto cookieJar() const -> HttpCookieJar * { return mCookieJar; }

/**
* @brief Get the proxy
*
* @return const Url&
*/
auto proxy() const -> const Url & { return mProxy; }
private:
/**
* @brief The sendRequest implementation, only do the connection handling
Expand All @@ -126,8 +141,9 @@ class HttpSession {
* @brief Collect cookies from the reply and add them to the cookie jar
*
* @param reply
* @param url Current request url
*/
auto parseReply(HttpReply &reply) -> void;
auto parseReply(HttpReply &reply, const Url &url) -> void;

/**
* @brief Connect to the server by url and return the HttpStream for transfer
Expand Down Expand Up @@ -183,20 +199,37 @@ inline auto HttpSession::sendRequest(std::string_view method, const HttpRequest
}
int idx = 0; // The number of redirects
while (true) {
#if 1
auto [reply_, timeout] = co_await whenAny(
sendRequestImpl(method, url, headers, payload, request.streamMode()),
sleep(request.transferTimeout())
);
if (timeout) { //< Timed out
co_return Unexpected(Error::TimedOut);
}
if (!reply_) { //< No reply, canceled
co_return Unexpected(Error::Canceled);
}
if (!reply_->has_value()) { //< Failed to get
co_return Unexpected(reply_->error());
}
auto &reply = reply_.value();
#else
auto reply = co_await sendRequestImpl(method, url, headers, payload, request.streamMode());
if (!reply) {
co_return Unexpected(reply.error());
}
#endif
const std::array redirectCodes = {301, 302, 303, 307, 308};
if (std::find(redirectCodes.begin(), redirectCodes.end(), reply->statusCode()) != redirectCodes.end() &&
idx < maximumRedirects) {
auto location = reply->headers().value(HttpHeaders::Location);
Url location = reply->headers().value(HttpHeaders::Location);
if (location.empty()) {
co_return Unexpected(Error::HttpBadReply);
}
ILIAS_INFO("Http", "Redirecting to {} ({} of maximum {})", location, idx + 1, maximumRedirects);
// Do redirect
url = location;
url = url.resolved(location);
headers = request.headers();
++idx;
continue;
Expand All @@ -209,8 +242,8 @@ inline auto HttpSession::sendRequest(std::string_view method, const HttpRequest

inline auto HttpSession::sendRequestImpl(std::string_view method, const Url &url, HttpHeaders &headers,
std::span<const std::byte> payload, bool streamMode) -> Task<HttpReply> {
normalizeRequest(url, headers);
while (true) {
normalizeRequest(url, headers);
bool fromPool = false;
auto stream = co_await connect(url, fromPool);
if (!stream) {
Expand All @@ -231,7 +264,7 @@ inline auto HttpSession::sendRequestImpl(std::string_view method, const Url &url
}
co_return Unexpected(reply.error());
}
parseReply(reply.value());
parseReply(reply.value(), url);
co_return std::move(*reply);
}
}
Expand Down Expand Up @@ -270,15 +303,15 @@ inline auto HttpSession::normalizeRequest(const Url &url, HttpHeaders &headers)
}
}

inline auto HttpSession::parseReply(HttpReply &reply) -> void {
inline auto HttpSession::parseReply(HttpReply &reply, const Url &url) -> void {
// Update cookie here
if (!mCookieJar) {
return;
}
const auto cookies = reply.headers().values(HttpHeaders::SetCookie);
for (const auto &setCookie : cookies) {
for (auto &cookie : HttpCookie::parse(setCookie)) {
cookie.normalize(reply.url());
cookie.normalize(url);
mCookieJar->insertCookie(cookie);
}
}
Expand All @@ -296,7 +329,7 @@ inline auto HttpSession::connect(const Url &url, bool &fromPool) -> Task<std::un
auto ent = ::getservbyname(scheme.c_str(), "tcp");
if (!ent) {
ILIAS_ERROR("Http", "Failed to get port for scheme: {}", scheme);
co_return Unexpected(Error::HttpBadRequest);
co_return Unexpected(SystemError::fromErrno());
}
port = ::ntohs(ent->s_port);
}
Expand Down Expand Up @@ -382,6 +415,10 @@ inline auto HttpSession::connect(const Url &url, bool &fromPool) -> Task<std::un
}
cur = std::move(sslClient);
}
#else
if (scheme == "https") {
co_return Unexpected(Error::ProtocolNotSupported);
}
#endif

// Done, adding connection
Expand Down
2 changes: 1 addition & 1 deletion include/ilias/io/stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class BufferedStream : public StreamMethod<BufferedStream<T> > {
const auto n = buffer.size();
while (true) {
auto buf = readWindow();
if (!buf.empty()) {
if (!buf.empty() || n == 0) {
// Read data from the buffer
auto len = std::min(buf.size(), n);
::memcpy(buffer.data(), buf.data(), len);
Expand Down
1 change: 1 addition & 0 deletions include/ilias/net/sockfd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ class SocketView {
if (ret < 0) {
return Unexpected(SystemError::fromErrno());
}
return {};
}
#endif

Expand Down
32 changes: 32 additions & 0 deletions include/ilias/net/tcp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,29 @@ class TcpListener {

auto close() { return mBase.close(); }

/**
* @brief Set the socket option.
*
* @tparam T
* @param opt
* @return Result<void>
*/
template <SetSockOption T>
auto setOption(const T &opt) -> Result<void> {
return socket().setOption(opt);
}

/**
* @brief Get the socket option.
*
* @tparam T
* @return Result<T>
*/
template <GetSockOption T>
auto getOption() -> Result<T> {
return socket().getOption<T>();
}

/**
* @brief Bind the listener to a local endpoint.
*
Expand Down Expand Up @@ -217,6 +240,15 @@ class TcpListener {
return mBase.localEndpoint();
}

/**
* @brief Get the underlying socket.
*
* @return SocketView
*/
auto socket() const -> SocketView {
return mBase.socket();
}

/**
* @brief Check if the socket is valid.
*
Expand Down
21 changes: 18 additions & 3 deletions include/ilias/platform/qt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <QMetaObject>
#include <QTimerEvent>
#include <QEventLoop>
#include <QMetaEnum>
#include <QObject>
#include <map>

Expand Down Expand Up @@ -148,6 +149,7 @@ class QIoContext final : public IoContext, public QObject {
auto submitTimer(uint64_t ms, detail::QTimerAwaiter *awaiter) -> int;
auto cancelTimer(int timerId) -> void;

SockInitializer mInit;
size_t mNumOfDescriptors = 0; //< How many descriptors are added
std::map<int, detail::QTimerAwaiter *> mTimers; //< Timer map
friend class detail::QTimerAwaiter;
Expand Down Expand Up @@ -196,12 +198,18 @@ inline auto QIoContext::addDescriptor(fd_t fd, IoDescriptor::Type type) -> Resul
// Prepare env for Socket
if (nfd->pollable) {
nfd->sockfd = qintptr(fd);
nfd->readNotifier = new QSocketNotifier(QSocketNotifier::Read, nfd.get());
nfd->writeNotifier = new QSocketNotifier(QSocketNotifier::Write, nfd.get());
nfd->exceptNotifier = new QSocketNotifier(QSocketNotifier::Exception, nfd.get());
nfd->readNotifier = new QSocketNotifier(nfd->sockfd, QSocketNotifier::Read, nfd.get());
nfd->writeNotifier = new QSocketNotifier(nfd->sockfd, QSocketNotifier::Write, nfd.get());
nfd->exceptNotifier = new QSocketNotifier(nfd->sockfd, QSocketNotifier::Exception, nfd.get());
nfd->readNotifier->setEnabled(false);
nfd->writeNotifier->setEnabled(false);
nfd->exceptNotifier->setEnabled(false);

// Set nonblock
SocketView sockfd(nfd->sockfd);
if (auto ret = sockfd.setBlocking(false); !ret) {
return Unexpected(ret.error());
}
}

++mNumOfDescriptors;
Expand Down Expand Up @@ -332,6 +340,7 @@ inline auto QIoContext::poll(IoDescriptor *fd, uint32_t event) -> Task<uint32_t>
inline auto QIoContext::timerEvent(QTimerEvent *event) -> void {
auto iter = mTimers.find(event->timerId());
if (iter == mTimers.end()) {
ILIAS_WARN("QIo", "Timer {} not found", event->timerId());
return;
}
auto [id, awaiter] = *iter;
Expand All @@ -346,6 +355,7 @@ inline auto QIoContext::submitTimer(uint64_t timeout, detail::QTimerAwaiter *awa
return 0;
}
mTimers.emplace(id, awaiter);
return id;
}

inline auto QIoContext::cancelTimer(int id) -> void {
Expand Down Expand Up @@ -387,6 +397,7 @@ inline auto detail::QTimerAwaiter::onTimeout() -> void {

// Poll
inline auto detail::QPollAwaiter::await_suspend(TaskView<> caller) -> void {
ILIAS_TRACE("QIo", "poll fd {}", mFd->sockfd);
mCaller = caller;
doConnect(); //< Connect the signal
mRegistration = caller.cancellationToken().register_(std::bind(&QPollAwaiter::onCancel, this));
Expand All @@ -398,17 +409,21 @@ inline auto detail::QPollAwaiter::await_resume() -> Result<uint32_t> {
}

inline auto detail::QPollAwaiter::onCancel() -> void {
ILIAS_TRACE("QIo", "poll fd {} was canceled", mFd->sockfd);
doDisconnect();
mResult = Unexpected(Error::Canceled);
mCaller.schedule();
}

inline auto detail::QPollAwaiter::onFdDestroyed() -> void {
ILIAS_TRACE("QIo", "fd {} was destroyed", mFd->sockfd);
doDisconnect();
mResult = Unexpected(Error::Canceled);
mCaller.schedule();
}

inline auto detail::QPollAwaiter::onNotifierActivated(QSocketDescriptor, QSocketNotifier::Type type) -> void {
ILIAS_TRACE("QIo", "fd {} was activated", mFd->sockfd);
doDisconnect();
if (type == QSocketNotifier::Read) {
mResult = PollEvent::In;
Expand Down
32 changes: 32 additions & 0 deletions include/ilias/url.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ class Url {
* @return false
*/
auto isValid() const -> bool;

/**
* @brief Check the url is relative
*
* @return true
* @return false
*/
auto isRelative() const -> bool;

/**
* @brief Get the scheme of the url
Expand Down Expand Up @@ -91,6 +99,14 @@ class Url {
*/
auto port() const -> std::optional<uint16_t>;

/**
* @brief Resolve the relative url to the absolute url (if arg is not relative, just return it)
*
* @param relative
* @return Url
*/
auto resolved(const Url &relative) const -> Url;

/**
* @brief Make the url to the string (encoded)
*
Expand Down Expand Up @@ -203,6 +219,10 @@ inline auto Url::isValid() const -> bool {
return isSafeString(p);
}

inline auto Url::isRelative() const -> bool {
return mScheme.empty();
}

inline auto Url::scheme() const -> std::string_view {
return mScheme;
}
Expand All @@ -226,6 +246,18 @@ inline auto Url::path() const -> std::string_view {
return mPath;
}

// Resolved
inline auto Url::resolved(const Url &rel) const -> Url {
if (!rel.isRelative()) {
return rel;
}
Url copy(rel);
copy.setScheme(mScheme);
copy.setHost(mHost);
copy.setPort(mPort);
return copy;
}

// Set
inline auto Url::setScheme(std::string_view scheme) -> void {
mScheme = scheme;
Expand Down
Loading

0 comments on commit 19490ba

Please sign in to comment.