Skip to content

Commit 417de89

Browse files
committed
Add enum for HttpMethod for which we use to route to different execute methods. Implement head request
1 parent 2b0a272 commit 417de89

3 files changed

Lines changed: 80 additions & 6 deletions

File tree

src/s3cpp/httpclient.cpp

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,19 @@
55
#include <stdexcept>
66
#include <string>
77

8-
HttpResponse HttpRequest::execute() { return client_.execute(*this); }
8+
// Route to its HttpMethod
9+
HttpResponse HttpRequest::execute() {
10+
switch (this->http_method_) {
11+
case HttpMethod::Get:
12+
return client_.execute_get(*this);
13+
case HttpMethod::Head:
14+
return client_.execute_head(*this);
15+
default:
16+
throw std::runtime_error(std::format("No matching enum Http Method"));
17+
}
18+
}
919

10-
HttpResponse HttpClient::execute(HttpRequest &request) {
20+
HttpResponse HttpClient::execute_get(HttpRequest &request) {
1121
if (!curl_handle) {
1222
throw std::runtime_error(
1323
// this can happen both when cURL handle is not initialized or when it
@@ -53,6 +63,50 @@ HttpResponse HttpClient::execute(HttpRequest &request) {
5363
return HttpResponse(static_cast<int>(response_code), std::move(buffer));
5464
}
5565

66+
HttpResponse HttpClient::execute_head(HttpRequest &request) {
67+
if (!curl_handle) {
68+
throw std::runtime_error(
69+
// this can happen both when cURL handle is not initialized or when it
70+
// is invalidated in the HTTPClient copy constructor
71+
"cURL handle is invalid");
72+
}
73+
std::string error_buf;
74+
75+
// TODO(cristian): from libcurl docs, they state that each curl handle has
76+
// "sticky" params, this is why we are resetting at each get request
77+
// However, I think we should only do this when moving a new handle the only
78+
// thing that will change is the URL from now
79+
//
80+
// curl_easy_reset(curl_handle);
81+
82+
curl_easy_setopt(curl_handle, CURLOPT_URL, request.getURL().c_str());
83+
curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1L);
84+
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, request.getTimeout());
85+
86+
// merge client and request headers
87+
// https://stackoverflow.com/questions/34321719
88+
auto headers = request.getHeaders();
89+
headers.insert(this->getHeaders().begin(), this->getHeaders().end());
90+
struct curl_slist *list = NULL;
91+
for (const auto &[k, v] : headers) {
92+
list = curl_slist_append(list, std::format("{}: {}", k, v).c_str());
93+
}
94+
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
95+
96+
CURLcode code = curl_easy_perform(curl_handle);
97+
curl_slist_free_all(list);
98+
if (code != CURLE_OK) {
99+
throw std::runtime_error(
100+
std::format("libcurl error for request: {}", curl_easy_strerror(code)));
101+
}
102+
103+
// HTTP code
104+
long response_code = 0;
105+
curl_easy_getinfo(curl_handle, CURLINFO_HTTP_CODE, &response_code);
106+
107+
return HttpResponse(static_cast<int>(response_code));
108+
}
109+
56110
size_t HttpClient::write_callback(char *ptr, size_t size, size_t nmemb,
57111
void *userdata) {
58112
std::string *buffer = static_cast<std::string *>(userdata);

src/s3cpp/httpclient.h

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,16 @@
1212
// Forward declaration
1313
class HttpClient;
1414

15+
enum class HttpMethod {
16+
Get,
17+
Post,
18+
Put,
19+
Head
20+
};
21+
1522
class HttpResponse {
1623
public:
24+
HttpResponse(int c) : code_(c) {};
1725
HttpResponse(int c, std::string b) : code_(c), body_(std::move(b)) {};
1826

1927
int status() const { return code_; }
@@ -36,8 +44,8 @@ class HttpResponse {
3644
// HttpRequest will handle all the headers and request params
3745
class HttpRequest {
3846
public:
39-
HttpRequest(HttpClient &client, std::string URL)
40-
: client_(client), URL_(std::move(URL)), timeout_(0) {};
47+
HttpRequest(HttpClient &client, std::string URL, const HttpMethod &http_method)
48+
: client_(client), URL_(std::move(URL)), http_method_(std::move(http_method)), timeout_(0) {};
4149

4250
HttpRequest &timeout(const long long &seconds) {
4351
timeout_ = std::chrono::seconds(seconds);
@@ -63,6 +71,7 @@ class HttpRequest {
6371
std::string URL_;
6472
std::unordered_map<std::string, std::string> headers_;
6573
std::chrono::seconds timeout_;
74+
HttpMethod http_method_;
6675
};
6776

6877
// HttpClient should only focus on handling the cURL handle
@@ -108,7 +117,10 @@ class HttpClient {
108117

109118
// HTTP GET
110119
[[nodiscard]] HttpRequest get(const std::string &URL) {
111-
return HttpRequest{*this, URL};
120+
return HttpRequest{*this, URL, HttpMethod::Get};
121+
};
122+
[[nodiscard]] HttpRequest head(const std::string &URL) {
123+
return HttpRequest{*this, URL, HttpMethod::Head};
112124
};
113125

114126
private:
@@ -119,7 +131,8 @@ class HttpClient {
119131

120132
// main logic to perform the request
121133
// this is invoked by HttpRequest
122-
HttpResponse execute(HttpRequest &request);
134+
HttpResponse execute_get(HttpRequest &request);
135+
HttpResponse execute_head(HttpRequest &request);
123136

124137
const std::unordered_map<std::string, std::string> &getHeaders() const { return headers_; }
125138
};

test/httpclient_test.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,10 @@ TEST(HTTP, HTTPRemoveHeader) {
153153
FAIL() << std::format("Request failed: {}", e.what());
154154
}
155155
}
156+
157+
TEST(HTTP, HTTPHead) {
158+
HttpClient client{};
159+
HttpResponse resp = client.head("https://postman-echo.com/get?foo0=bar1&foo2=bar2").execute();
160+
EXPECT_TRUE(resp.body().empty());
161+
}
162+

0 commit comments

Comments
 (0)