Skip to content

Commit c65dfc7

Browse files
committed
Migrate info and search verbs to new REST API
This lets us drop the notion that a single RpcRequest can result in multiple HTTP requests, greatly simplifying our dispatch logic.
1 parent d52bba1 commit c65dfc7

File tree

12 files changed

+163
-227
lines changed

12 files changed

+163
-227
lines changed

src/aur/aur.cc

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class AurImpl : public Aur {
3636

3737
void QueueRawRequest(const HttpRequest& request,
3838
const RawResponseCallback& callback) override;
39+
void QueueRawRequest(const RpcRequest& request,
40+
const RawResponseCallback& callback) override;
3941

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

57+
template <typename ResponseHandlerType>
58+
void QueueRpcRequest(const RpcRequest& request,
59+
const ResponseHandlerType::CallbackType& callback);
60+
5561
int FinishRequest(CURL* curl, CURLcode result, bool dispatch_callback);
5662
int FinishRequest(sd_event_source* source);
5763

@@ -498,36 +504,39 @@ template <typename ResponseHandlerType>
498504
void AurImpl::QueueHttpRequest(
499505
const HttpRequest& request,
500506
const ResponseHandlerType::CallbackType& callback) {
501-
for (const auto& r : request.Build(options_.baseurl)) {
502-
auto* curl = curl_easy_init();
503-
auto* handler = new ResponseHandlerType(this, callback);
504-
505-
using RH = ResponseHandler;
506-
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2);
507-
curl_easy_setopt(curl, CURLOPT_URL, r.c_str());
508-
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RH::BodyCallback);
509-
curl_easy_setopt(curl, CURLOPT_WRITEDATA, handler);
510-
curl_easy_setopt(curl, CURLOPT_PRIVATE, handler);
511-
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, handler->error_buffer.data());
512-
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
513-
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
514-
curl_easy_setopt(curl, CURLOPT_USERAGENT, options_.useragent.c_str());
515-
516-
switch (debug_level_) {
517-
case DebugLevel::NONE:
518-
break;
519-
case DebugLevel::REQUESTS:
520-
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, &RH::DebugCallback);
521-
curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_stream_);
522-
[[fallthrough]];
523-
case DebugLevel::VERBOSE_STDERR:
524-
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
525-
break;
526-
}
507+
auto* curl = curl_easy_init();
508+
auto* handler = new ResponseHandlerType(this, callback);
509+
510+
using RH = ResponseHandler;
511+
curl_easy_setopt(curl, CURLOPT_URL, request.Url(options_.baseurl).c_str());
512+
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2);
513+
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
514+
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
515+
curl_easy_setopt(curl, CURLOPT_USERAGENT, options_.useragent.c_str());
516+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &RH::BodyCallback);
517+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, handler);
518+
curl_easy_setopt(curl, CURLOPT_PRIVATE, handler);
519+
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, handler->error_buffer.data());
520+
521+
if (request.command() == RpcRequest::Command::POST) {
522+
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, request.Payload().c_str());
523+
}
527524

528-
curl_multi_add_handle(curl_multi_, curl);
529-
active_requests_.emplace(curl);
525+
switch (debug_level_) {
526+
case DebugLevel::NONE:
527+
break;
528+
case DebugLevel::REQUESTS:
529+
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION,
530+
&ResponseHandler::DebugCallback);
531+
curl_easy_setopt(curl, CURLOPT_DEBUGDATA, &debug_stream_);
532+
[[fallthrough]];
533+
case DebugLevel::VERBOSE_STDERR:
534+
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
535+
break;
530536
}
537+
538+
curl_multi_add_handle(curl_multi_, curl);
539+
active_requests_.emplace(curl);
531540
}
532541

533542
// static
@@ -561,7 +570,7 @@ void AurImpl::QueueCloneRequest(const CloneRequest& request,
561570
}
562571

563572
if (pid == 0) {
564-
const auto url = request.Build(options_.baseurl)[0];
573+
const auto url = request.Url(options_.baseurl);
565574

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

617+
void AurImpl::QueueRawRequest(const RpcRequest& request,
618+
const RawResponseCallback& callback) {
619+
QueueHttpRequest<RawResponseHandler>(request, callback);
620+
}
621+
608622
void AurImpl::QueueRpcRequest(const RpcRequest& request,
609623
const RpcResponseCallback& callback) {
610624
QueueHttpRequest<RpcResponseHandler>(request, callback);

src/aur/aur.hh

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,17 @@ class Aur {
5151
Aur(Aur&&) = default;
5252
Aur& operator=(Aur&&) = default;
5353

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

5959
// Asynchronously issue a raw request. The callback will be invoked when the
6060
// call completes.
6161
virtual void QueueRawRequest(const HttpRequest& request,
6262
const RawResponseCallback& callback) = 0;
63+
virtual void QueueRawRequest(const RpcRequest& request,
64+
const RawResponseCallback& callback) = 0;
6365

6466
// Clone a git repository.
6567
virtual void QueueCloneRequest(const CloneRequest& request,

src/aur/request.cc

Lines changed: 11 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -28,53 +28,17 @@ void QueryParamFormatter(std::string* out, const HttpRequest::QueryParam& kv) {
2828

2929
} // namespace
3030

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

35-
std::vector<std::string> RpcRequest::Build(std::string_view baseurl) const {
36-
const auto next_span = [&](std::string_view s) -> std::string_view {
37-
// No chopping needed.
38-
if (s.size() <= approx_max_length_) {
39-
return s;
40-
}
41-
42-
// Try to chop at the final ampersand before the cutoff length.
43-
auto n = s.substr(0, approx_max_length_).rfind('&');
44-
if (n != std::string_view::npos) {
45-
return s.substr(0, n);
46-
}
47-
48-
// We found a single arg which is Too Damn Long. Look for the ampersand just
49-
// after the length limit and use all of it.
50-
n = s.substr(approx_max_length_).find('&');
51-
if (n != std::string_view::npos) {
52-
return s.substr(0, n + approx_max_length_);
53-
}
54-
55-
// We're at the end of the querystring and have no place to chop.
56-
return s;
57-
};
58-
59-
const auto qs = absl::StrJoin(args_, "&", &QueryParamFormatter);
60-
std::string_view sv(qs);
61-
62-
std::vector<std::string> requests;
63-
while (!sv.empty()) {
64-
const auto span = next_span(sv);
65-
66-
requests.push_back(
67-
absl::StrCat(baseurl, "/rpc?", base_querystring_, "&", span));
68-
sv.remove_prefix(std::min(sv.length(), span.length() + 1));
69-
}
70-
71-
return requests;
35+
void RpcRequest::AddArg(std::string key, std::string value) {
36+
params_.push_back({std::move(key), std::move(value)});
7237
}
7338

74-
RpcRequest::RpcRequest(const HttpRequest::QueryParams& base_params,
75-
size_type approx_max_length)
76-
: base_querystring_(absl::StrJoin(base_params, "&", &QueryParamFormatter)),
77-
approx_max_length_(approx_max_length) {}
39+
std::string RpcRequest::Payload() const {
40+
return absl::StrJoin(params_, "&", QueryParamFormatter);
41+
}
7842

7943
// static
8044
RawRequest RawRequest::ForSourceFile(const Package& package,
@@ -83,12 +47,12 @@ RawRequest RawRequest::ForSourceFile(const Package& package,
8347
"?h=", UrlEscape(package.pkgbase)));
8448
}
8549

86-
std::vector<std::string> RawRequest::Build(std::string_view baseurl) const {
87-
return {absl::StrCat(baseurl, urlpath_)};
50+
std::string RawRequest::Url(std::string_view baseurl) const {
51+
return absl::StrCat(baseurl, urlpath_);
8852
}
8953

90-
std::vector<std::string> CloneRequest::Build(std::string_view baseurl) const {
91-
return {absl::StrCat(baseurl, "/", reponame_)};
54+
std::string CloneRequest::Url(std::string_view baseurl) const {
55+
return absl::StrCat(baseurl, "/", reponame_);
9256
}
9357

9458
} // namespace aur

src/aur/request.hh

Lines changed: 41 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <utility>
77
#include <vector>
88

9+
#include "absl/strings/str_format.h"
910
#include "package.hh"
1011

1112
namespace aur {
@@ -15,13 +16,42 @@ class Request {
1516
public:
1617
virtual ~Request() = default;
1718

18-
virtual std::vector<std::string> Build(std::string_view baseurl) const = 0;
19+
virtual std::string Url(std::string_view baseurl) const = 0;
1920
};
2021

2122
class HttpRequest : public Request {
2223
public:
24+
enum class Command : int8_t {
25+
GET,
26+
POST,
27+
};
28+
2329
using QueryParam = std::pair<std::string, std::string>;
2430
using QueryParams = std::vector<QueryParam>;
31+
32+
explicit HttpRequest(Command command) : command_(command) {}
33+
34+
Command command() const { return command_; }
35+
36+
virtual std::string Payload() const = 0;
37+
38+
protected:
39+
Command command_;
40+
};
41+
42+
class RpcRequest : public HttpRequest {
43+
public:
44+
RpcRequest(Command command, std::string endpoint)
45+
: HttpRequest(command), endpoint_(std::move(endpoint)) {}
46+
47+
std::string Url(std::string_view baseurl) const override;
48+
std::string Payload() const override;
49+
50+
void AddArg(std::string key, std::string value);
51+
52+
private:
53+
std::string endpoint_;
54+
QueryParams params_;
2555
};
2656

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

33-
explicit RawRequest(std::string urlpath) : urlpath_(std::move(urlpath)) {}
63+
explicit RawRequest(std::string urlpath)
64+
: HttpRequest(HttpRequest::Command::GET), urlpath_(std::move(urlpath)) {}
3465

3566
RawRequest(const RawRequest&) = delete;
3667
RawRequest& operator=(const RawRequest&) = delete;
3768

3869
RawRequest(RawRequest&&) = default;
3970
RawRequest& operator=(RawRequest&&) = default;
4071

41-
std::vector<std::string> Build(std::string_view baseurl) const override;
72+
std::string Url(std::string_view baseurl) const override;
73+
std::string Payload() const override { return std::string(); }
4274

4375
private:
4476
std::string urlpath_;
@@ -58,40 +90,12 @@ class CloneRequest : public Request {
5890

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

61-
std::vector<std::string> Build(std::string_view baseurl) const override;
93+
std::string Url(std::string_view baseurl) const override;
6294

6395
private:
6496
std::string reponame_;
6597
};
6698

67-
// A base class describing a GET request to the RPC endpoint of the AUR.
68-
class RpcRequest : public HttpRequest {
69-
public:
70-
using size_type = std::string_view::size_type;
71-
72-
// Upper limit on aur.archlinux.org seems to be somewhere around 8k.
73-
static constexpr size_type kMaxUriLength = 8000;
74-
75-
RpcRequest(const HttpRequest::QueryParams& base_params,
76-
size_type approx_max_length = kMaxUriLength);
77-
78-
RpcRequest(const RpcRequest&) = delete;
79-
RpcRequest& operator=(const RpcRequest&) = delete;
80-
81-
RpcRequest(RpcRequest&&) = default;
82-
RpcRequest& operator=(RpcRequest&&) = default;
83-
84-
std::vector<std::string> Build(std::string_view baseurl) const override;
85-
86-
void AddArg(std::string_view key, std::string_view value);
87-
88-
private:
89-
std::string base_querystring_;
90-
size_type approx_max_length_;
91-
92-
HttpRequest::QueryParams args_;
93-
};
94-
9599
class InfoRequest : public RpcRequest {
96100
public:
97101
explicit InfoRequest(const std::vector<std::string>& args) : InfoRequest() {
@@ -106,9 +110,9 @@ class InfoRequest : public RpcRequest {
106110
InfoRequest(InfoRequest&&) = default;
107111
InfoRequest& operator=(InfoRequest&&) = default;
108112

109-
InfoRequest() : RpcRequest({{"v", "5"}, {"type", "info"}}) {}
113+
InfoRequest() : RpcRequest(HttpRequest::Command::POST, "/rpc/v5/info") {}
110114

111-
void AddArg(std::string_view arg) { RpcRequest::AddArg("arg[]", arg); }
115+
void AddArg(std::string arg) { RpcRequest::AddArg("arg[]", std::move(arg)); }
112116
};
113117

114118
class SearchRequest : public RpcRequest {
@@ -178,13 +182,9 @@ class SearchRequest : public RpcRequest {
178182
}
179183

180184
SearchRequest(SearchBy by, std::string_view arg)
181-
: RpcRequest({
182-
{"v", "5"},
183-
{"type", "search"},
184-
{"by", SearchByToString(by)},
185-
}) {
186-
AddArg("arg", arg);
187-
}
185+
: RpcRequest(HttpRequest::Command::GET,
186+
absl::StrFormat("/rpc/v5/search/%s?by=%s", arg,
187+
SearchByToString(by))) {}
188188

189189
SearchRequest(const SearchRequest&) = delete;
190190
SearchRequest& operator=(const SearchRequest&) = delete;

0 commit comments

Comments
 (0)