Skip to content

Commit

Permalink
Migrate info and search verbs to new REST API
Browse files Browse the repository at this point in the history
This lets us drop the notion that a single RpcRequest can result in
multiple HTTP requests, greatly simplifying our dispatch logic.
  • Loading branch information
falconindy committed Aug 11, 2024
1 parent d52bba1 commit c65dfc7
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 227 deletions.
72 changes: 43 additions & 29 deletions src/aur/aur.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class AurImpl : public Aur {

void QueueRawRequest(const HttpRequest& request,
const RawResponseCallback& callback) override;
void QueueRawRequest(const RpcRequest& request,
const RawResponseCallback& callback) override;

void QueueCloneRequest(const CloneRequest& request,
const CloneResponseCallback& callback) override;
Expand All @@ -52,6 +54,10 @@ class AurImpl : public Aur {
void QueueHttpRequest(const HttpRequest& request,
const ResponseHandlerType::CallbackType& callback);

template <typename ResponseHandlerType>
void QueueRpcRequest(const RpcRequest& request,
const ResponseHandlerType::CallbackType& callback);

int FinishRequest(CURL* curl, CURLcode result, bool dispatch_callback);
int FinishRequest(sd_event_source* source);

Expand Down Expand Up @@ -498,36 +504,39 @@ template <typename ResponseHandlerType>
void AurImpl::QueueHttpRequest(
const HttpRequest& request,
const ResponseHandlerType::CallbackType& callback) {
for (const auto& r : request.Build(options_.baseurl)) {
auto* curl = curl_easy_init();
auto* handler = new ResponseHandlerType(this, callback);

using RH = ResponseHandler;
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2);
curl_easy_setopt(curl, CURLOPT_URL, r.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RH::BodyCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, handler);
curl_easy_setopt(curl, CURLOPT_PRIVATE, handler);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, handler->error_buffer.data());
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, options_.useragent.c_str());

switch (debug_level_) {
case DebugLevel::NONE:
break;
case DebugLevel::REQUESTS:
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, &RH::DebugCallback);
curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_stream_);
[[fallthrough]];
case DebugLevel::VERBOSE_STDERR:
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
break;
}
auto* curl = curl_easy_init();
auto* handler = new ResponseHandlerType(this, callback);

using RH = ResponseHandler;
curl_easy_setopt(curl, CURLOPT_URL, request.Url(options_.baseurl).c_str());
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2);
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, options_.useragent.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RH::BodyCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, handler);
curl_easy_setopt(curl, CURLOPT_PRIVATE, handler);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, handler->error_buffer.data());

if (request.command() == RpcRequest::Command::POST) {
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, request.Payload().c_str());
}

curl_multi_add_handle(curl_multi_, curl);
active_requests_.emplace(curl);
switch (debug_level_) {
case DebugLevel::NONE:
break;
case DebugLevel::REQUESTS:
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
&ResponseHandler::DebugCallback);
curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_stream_);
[[fallthrough]];
case DebugLevel::VERBOSE_STDERR:
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
break;
}

curl_multi_add_handle(curl_multi_, curl);
active_requests_.emplace(curl);
}

// static
Expand Down Expand Up @@ -561,7 +570,7 @@ void AurImpl::QueueCloneRequest(const CloneRequest& request,
}

if (pid == 0) {
const auto url = request.Build(options_.baseurl)[0];
const auto url = request.Url(options_.baseurl);

std::vector<const char*> cmd;
if (update) {
Expand Down Expand Up @@ -605,6 +614,11 @@ void AurImpl::QueueRawRequest(const HttpRequest& request,
QueueHttpRequest<RawResponseHandler>(request, callback);
}

void AurImpl::QueueRawRequest(const RpcRequest& request,
const RawResponseCallback& callback) {
QueueHttpRequest<RawResponseHandler>(request, callback);
}

void AurImpl::QueueRpcRequest(const RpcRequest& request,
const RpcResponseCallback& callback) {
QueueHttpRequest<RpcResponseHandler>(request, callback);
Expand Down
6 changes: 4 additions & 2 deletions src/aur/aur.hh
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,17 @@ class Aur {
Aur(Aur&&) = default;
Aur& operator=(Aur&&) = default;

// Asynchronously issue an RPC request. The callback will be invoked when the
// call completes.
// Asynchronously issue an RPC request using the REST API. The callback will
// be invoked when the call completes.
virtual void QueueRpcRequest(const RpcRequest& request,
const RpcResponseCallback& callback) = 0;

// Asynchronously issue a raw request. The callback will be invoked when the
// call completes.
virtual void QueueRawRequest(const HttpRequest& request,
const RawResponseCallback& callback) = 0;
virtual void QueueRawRequest(const RpcRequest& request,
const RawResponseCallback& callback) = 0;

// Clone a git repository.
virtual void QueueCloneRequest(const CloneRequest& request,
Expand Down
58 changes: 11 additions & 47 deletions src/aur/request.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,53 +28,17 @@ void QueryParamFormatter(std::string* out, const HttpRequest::QueryParam& kv) {

} // namespace

void RpcRequest::AddArg(std::string_view key, std::string_view value) {
args_.emplace_back(key, value);
std::string RpcRequest::Url(std::string_view baseurl) const {
return absl::StrCat(baseurl, endpoint_);
}

std::vector<std::string> RpcRequest::Build(std::string_view baseurl) const {
const auto next_span = [&](std::string_view s) -> std::string_view {
// No chopping needed.
if (s.size() <= approx_max_length_) {
return s;
}

// Try to chop at the final ampersand before the cutoff length.
auto n = s.substr(0, approx_max_length_).rfind('&');
if (n != std::string_view::npos) {
return s.substr(0, n);
}

// We found a single arg which is Too Damn Long. Look for the ampersand just
// after the length limit and use all of it.
n = s.substr(approx_max_length_).find('&');
if (n != std::string_view::npos) {
return s.substr(0, n + approx_max_length_);
}

// We're at the end of the querystring and have no place to chop.
return s;
};

const auto qs = absl::StrJoin(args_, "&", &QueryParamFormatter);
std::string_view sv(qs);

std::vector<std::string> requests;
while (!sv.empty()) {
const auto span = next_span(sv);

requests.push_back(
absl::StrCat(baseurl, "/rpc?", base_querystring_, "&", span));
sv.remove_prefix(std::min(sv.length(), span.length() + 1));
}

return requests;
void RpcRequest::AddArg(std::string key, std::string value) {
params_.push_back({std::move(key), std::move(value)});
}

RpcRequest::RpcRequest(const HttpRequest::QueryParams& base_params,
size_type approx_max_length)
: base_querystring_(absl::StrJoin(base_params, "&", &QueryParamFormatter)),
approx_max_length_(approx_max_length) {}
std::string RpcRequest::Payload() const {
return absl::StrJoin(params_, "&", QueryParamFormatter);
}

// static
RawRequest RawRequest::ForSourceFile(const Package& package,
Expand All @@ -83,12 +47,12 @@ RawRequest RawRequest::ForSourceFile(const Package& package,
"?h=", UrlEscape(package.pkgbase)));
}

std::vector<std::string> RawRequest::Build(std::string_view baseurl) const {
return {absl::StrCat(baseurl, urlpath_)};
std::string RawRequest::Url(std::string_view baseurl) const {
return absl::StrCat(baseurl, urlpath_);
}

std::vector<std::string> CloneRequest::Build(std::string_view baseurl) const {
return {absl::StrCat(baseurl, "/", reponame_)};
std::string CloneRequest::Url(std::string_view baseurl) const {
return absl::StrCat(baseurl, "/", reponame_);
}

} // namespace aur
82 changes: 41 additions & 41 deletions src/aur/request.hh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <utility>
#include <vector>

#include "absl/strings/str_format.h"
#include "package.hh"

namespace aur {
Expand All @@ -15,13 +16,42 @@ class Request {
public:
virtual ~Request() = default;

virtual std::vector<std::string> Build(std::string_view baseurl) const = 0;
virtual std::string Url(std::string_view baseurl) const = 0;
};

class HttpRequest : public Request {
public:
enum class Command : int8_t {
GET,
POST,
};

using QueryParam = std::pair<std::string, std::string>;
using QueryParams = std::vector<QueryParam>;

explicit HttpRequest(Command command) : command_(command) {}

Command command() const { return command_; }

virtual std::string Payload() const = 0;

protected:
Command command_;
};

class RpcRequest : public HttpRequest {
public:
RpcRequest(Command command, std::string endpoint)
: HttpRequest(command), endpoint_(std::move(endpoint)) {}

std::string Url(std::string_view baseurl) const override;
std::string Payload() const override;

void AddArg(std::string key, std::string value);

private:
std::string endpoint_;
QueryParams params_;
};

// A class describing a GET request for an arbitrary URL on the AUR.
Expand All @@ -30,15 +60,17 @@ class RawRequest : public HttpRequest {
static RawRequest ForSourceFile(const Package& package,
std::string_view filename);

explicit RawRequest(std::string urlpath) : urlpath_(std::move(urlpath)) {}
explicit RawRequest(std::string urlpath)
: HttpRequest(HttpRequest::Command::GET), urlpath_(std::move(urlpath)) {}

RawRequest(const RawRequest&) = delete;
RawRequest& operator=(const RawRequest&) = delete;

RawRequest(RawRequest&&) = default;
RawRequest& operator=(RawRequest&&) = default;

std::vector<std::string> Build(std::string_view baseurl) const override;
std::string Url(std::string_view baseurl) const override;
std::string Payload() const override { return std::string(); }

private:
std::string urlpath_;
Expand All @@ -58,40 +90,12 @@ class CloneRequest : public Request {

const std::string& reponame() const { return reponame_; }

std::vector<std::string> Build(std::string_view baseurl) const override;
std::string Url(std::string_view baseurl) const override;

private:
std::string reponame_;
};

// A base class describing a GET request to the RPC endpoint of the AUR.
class RpcRequest : public HttpRequest {
public:
using size_type = std::string_view::size_type;

// Upper limit on aur.archlinux.org seems to be somewhere around 8k.
static constexpr size_type kMaxUriLength = 8000;

RpcRequest(const HttpRequest::QueryParams& base_params,
size_type approx_max_length = kMaxUriLength);

RpcRequest(const RpcRequest&) = delete;
RpcRequest& operator=(const RpcRequest&) = delete;

RpcRequest(RpcRequest&&) = default;
RpcRequest& operator=(RpcRequest&&) = default;

std::vector<std::string> Build(std::string_view baseurl) const override;

void AddArg(std::string_view key, std::string_view value);

private:
std::string base_querystring_;
size_type approx_max_length_;

HttpRequest::QueryParams args_;
};

class InfoRequest : public RpcRequest {
public:
explicit InfoRequest(const std::vector<std::string>& args) : InfoRequest() {
Expand All @@ -106,9 +110,9 @@ class InfoRequest : public RpcRequest {
InfoRequest(InfoRequest&&) = default;
InfoRequest& operator=(InfoRequest&&) = default;

InfoRequest() : RpcRequest({{"v", "5"}, {"type", "info"}}) {}
InfoRequest() : RpcRequest(HttpRequest::Command::POST, "/rpc/v5/info") {}

void AddArg(std::string_view arg) { RpcRequest::AddArg("arg[]", arg); }
void AddArg(std::string arg) { RpcRequest::AddArg("arg[]", std::move(arg)); }
};

class SearchRequest : public RpcRequest {
Expand Down Expand Up @@ -178,13 +182,9 @@ class SearchRequest : public RpcRequest {
}

SearchRequest(SearchBy by, std::string_view arg)
: RpcRequest({
{"v", "5"},
{"type", "search"},
{"by", SearchByToString(by)},
}) {
AddArg("arg", arg);
}
: RpcRequest(HttpRequest::Command::GET,
absl::StrFormat("/rpc/v5/search/%s?by=%s", arg,
SearchByToString(by))) {}

SearchRequest(const SearchRequest&) = delete;
SearchRequest& operator=(const SearchRequest&) = delete;
Expand Down
Loading

0 comments on commit c65dfc7

Please sign in to comment.