Arachne 1.0
Arachne - the perpetual stitcher of Wikidata entities.
Loading...
Searching...
No Matches
corespace::http_client Class Referencefinal

Minimal, synchronous HTTP GET client built on libcurl. More...

#include <include/http_client.hpp>

Collaboration diagram for corespace::http_client:

Public Member Functions

 http_client ()
 Construct a client and initialize libcurl.
http_response get (std::string_view url, const parameter_list &params={}, std::string_view accept={}, int timeout_sec=-1)
 Perform an HTTP GET to url with optional query params.
http_response post_form (std::string_view url, const parameter_list &form, const parameter_list &query={}, std::string_view accept={}, int timeout_sec=-1)
 Perform an HTTP POST with form-encoded body.
http_response post_raw (std::string_view url, std::string_view body, std::string_view content_type, const parameter_list &query={}, std::string_view accept={}, int timeout_sec=-1)
 Perform an HTTP POST with a raw body.
const network_metricsmetrics_info () const
 Access aggregated network metrics.

Private Types

using curl_url_ptr = std::unique_ptr<CURLU, decltype(&curl_url_cleanup)>
 Unique pointer type for CURLU with proper deleter.

Private Member Functions

http_response request_get (CURLU *url_handle, std::chrono::milliseconds &elapsed, std::string_view accept={}, int timeout_sec=-1) const
 Execute a single HTTP GET using the prepared URL handle.
http_response request_post (CURLU *url_handle, std::chrono::milliseconds &elapsed, std::string_view content_type, std::string_view body, std::string_view accept={}, int timeout_sec=-1) const
 Execute a single HTTP POST with given body and content type.
std::string build_form_body (const parameter_list &form) const
void update_headers (http_response &response) const
 Refresh the header multimap from the last transfer.
void update_metrics (const http_response &response, std::chrono::milliseconds elapsed)
 Update counters and histograms after an attempt.
long long next_delay (int attempt) const
 Compute the next backoff delay for attempt (1-based).
void apply_server_retry_hint (long long &sleep_ms) const
 Apply server-provided retry hint if present.

Static Private Member Functions

static curl_url_ptr build_url (std::string_view url, const parameter_list &params)
 Construct a CURLU handle from url and append params.
static bool status_good (const http_response &response)
 Success predicate: transport OK and HTTP 2xx.
static bool status_retry (const http_response &response, bool net_ok)
 Retry predicate for transient outcomes.
static size_t write_callback (const char *ptr, size_t size, size_t n, void *data)
 libcurl write callback: append chunk to response body.

Private Attributes

const network_options opt {}
 Fixed options installed at construction.
network_metrics metrics
 Aggregated metrics (atomic counters).
std::mutex mu
std::unique_ptr< CURL, decltype(&curl_easy_cleanup)> curl
 Reused easy handle (not thread-safe).
std::unique_ptr< curl_slist, decltype(&curl_slist_free_all)> header_list
 Owned request header list.

Detailed Description

Minimal, synchronous HTTP GET client built on libcurl.

Responsibilities:

  • Build request URLs with encoded query parameters.
  • Issue HTTP GET requests with redirect following enabled.
  • Apply bounded exponential backoff with jitter for retryable outcomes: network errors, 408 (Request Timeout), 429 (Too Many Requests), and 5xx.
  • Aggregate lightweight, thread-safe network metrics.

Lifetime and thread-safety:

  • A single easy handle (CURL*) is owned by the instance and reused across requests; therefore an http_client object is not thread-safe. Use one instance per calling thread.
  • curl_global_init is performed once per process via std::call_once.

Definition at line 50 of file http_client.hpp.

Member Typedef Documentation

◆ curl_url_ptr

using corespace::http_client::curl_url_ptr = std::unique_ptr<CURLU, decltype(&curl_url_cleanup)>
private

Unique pointer type for CURLU with proper deleter.

Definition at line 149 of file http_client.hpp.

Constructor & Destructor Documentation

◆ http_client()

corespace::http_client::http_client ( )
explicit

Construct a client and initialize libcurl.

Effects:

  • Ensures curl_global_init is called exactly once process-wide.
  • Creates an easy handle and installs default options: user agent, Accept header, redirect following, transparent decoding, timeouts, and signal suppression.
Exceptions
std::runtime_errorif libcurl initialization fails or header allocation fails.

Definition at line 48 of file http_client.cpp.

48 {
49 std::call_once(global_curl, curl_inited);
50
51 curl.reset(curl_easy_init());
52 if (!curl) {
53 throw std::runtime_error("curl_easy_init failed");
54 }
55
56 const std::string accept_header = "Accept: " + opt.accept;
57 curl_slist* headers = curl_slist_append(nullptr, accept_header.c_str());
58 if (!headers) {
59 throw std::runtime_error("failed to allocate curl headers");
60 }
61 header_list.reset(headers);
62
63 curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, opt.user_agent.c_str());
64 curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header_list.get());
65 curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L);
66 curl_easy_setopt(curl.get(), CURLOPT_ACCEPT_ENCODING, "");
67 curl_easy_setopt(curl.get(), CURLOPT_NOSIGNAL, 1L);
68 curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT_MS, opt.timeout_ms);
69 curl_easy_setopt(curl.get(), CURLOPT_CONNECTTIMEOUT_MS, opt.connect_ms);
70}
std::unique_ptr< curl_slist, decltype(&curl_slist_free_all)> header_list
Owned request header list.
const network_options opt
Fixed options installed at construction.
std::unique_ptr< CURL, decltype(&curl_easy_cleanup)> curl
Reused easy handle (not thread-safe).

References corespace::anonymous_namespace{http_client.cpp}::curl_inited(), and corespace::anonymous_namespace{http_client.cpp}::global_curl.

Here is the call graph for this function:

Member Function Documentation

◆ apply_server_retry_hint()

void corespace::http_client::apply_server_retry_hint ( long long & sleep_ms) const
private

Apply server-provided retry hint if present.

If CURLINFO_RETRY_AFTER yields a non-negative value, interpret it as seconds and raise sleep_ms to at least that many milliseconds.

Parameters
sleep_msIn/out: proposed client backoff in milliseconds.

Definition at line 376 of file http_client.cpp.

376 {
377 curl_off_t retry_after = -1;
378 if (curl_easy_getinfo(curl.get(), CURLINFO_RETRY_AFTER, &retry_after)
379 == CURLE_OK
380 && retry_after >= 0) {
381 const long long server_hint_ms
382 = std::chrono::duration_cast<std::chrono::milliseconds>(
383 std::chrono::seconds(retry_after)
384 )
385 .count();
386 sleep_ms = std::max(sleep_ms, server_hint_ms);
387 }
388}

Referenced by get(), post_form(), and post_raw().

Here is the caller graph for this function:

◆ build_form_body()

std::string corespace::http_client::build_form_body ( const parameter_list & form) const
private

Definition at line 308 of file http_client.cpp.

308 {
309 std::string body;
310 bool first = true;
311 for (const auto& [key, value] : form) {
312 char* ekey = curl_easy_escape(
313 curl.get(), key.c_str(), static_cast<int>(key.size())
314 );
315 char* evalue = curl_easy_escape(
316 curl.get(), value.data(), static_cast<int>(value.size())
317 );
318 if (!first) {
319 body.push_back('&');
320 }
321 body.append(ekey ? ekey : "");
322 body.push_back('=');
323 body.append(evalue ? evalue : "");
324 if (ekey) {
325 curl_free(ekey);
326 }
327 if (evalue) {
328 curl_free(evalue);
329 }
330 first = false;
331 }
332 return body;
333}
@ form
Lexeme form IDs such as "L<lexeme>-F<form>".
Definition utils.hpp:53

◆ build_url()

http_client::curl_url_ptr corespace::http_client::build_url ( std::string_view url,
const parameter_list & params )
staticprivate

Construct a CURLU handle from url and append params.

Each parameter is URL-encoded and appended via CURLU_APPENDQUERY.

Parameters
urlBase URL.
paramsQuery parameters.
Returns
Owning smart pointer to a configured CURLU handle.
Exceptions
std::runtime_errorif allocation or URL assembly fails.

Definition at line 181 of file http_client.cpp.

183 {
184 curl_url_ptr url_handle(curl_url(), &curl_url_cleanup);
185 if (!url_handle) {
186 throw std::runtime_error("curl_url failed");
187 }
188
189 const std::string url_copy(url);
190 if (curl_url_set(url_handle.get(), CURLUPART_URL, url_copy.c_str(), 0)
191 != CURLUE_OK) {
192 throw std::runtime_error("failed to set request url");
193 }
194
195 for (const auto& [key, value] : params) {
196 std::string parameter = key + "=" + std::string(value);
197 if (curl_url_set(
198 url_handle.get(), CURLUPART_QUERY, parameter.c_str(),
199 CURLU_APPENDQUERY | CURLU_URLENCODE
200 )
201 != CURLUE_OK) {
202 throw std::runtime_error("failed to append query parameter");
203 }
204 }
205
206 return url_handle;
207}
std::unique_ptr< CURLU, decltype(&curl_url_cleanup)> curl_url_ptr
Unique pointer type for CURLU with proper deleter.
std::pair< std::string, std::string > parameter
Single query parameter: key=value (pre-encoding is handled by libcurl).
Definition utils.hpp:61

◆ get()

http_response corespace::http_client::get ( std::string_view url,
const parameter_list & params = {},
std::string_view accept = {},
int timeout_sec = -1 )

Perform an HTTP GET to url with optional query params.

Behavior:

  • Builds a CURLU URL with params URL-encoded and appended.
  • Executes the request; on non-2xx or transport errors, applies the retry policy up to opt.max_retries with jittered backoff and an optional server Retry-After hint.
  • On success (2xx + CURLE_OK) returns the populated response.

Failure:

  • If all attempts fail with a libcurl error, throws std::runtime_error("curl error: ...").
  • If all attempts return non-success HTTP codes, throws std::runtime_error("http error: <status>").

Metrics:

  • Updates metrics after each attempt (including retries).
Parameters
urlAbsolute or base URL.
paramsOptional list of query parameters to append.
acceptOptional Accept header value to override the default; empty string uses the client's configured accept header.
timeout_secOptional per-request timeout in seconds; if negative the client's default timeout is used.
Returns
http_response on success (2xx).
Exceptions
std::runtime_erroron terminal failure as described above.

Definition at line 74 of file http_client.cpp.

77 {
78 std::lock_guard lk(mu);
79 const auto url_handle = build_url(url, params);
80 for (int attempt = 1;; ++attempt) {
81 std::chrono::milliseconds elapsed { 0l };
82 http_response response
83 = request_get(url_handle.get(), elapsed, accept, timeout_sec);
84
85 update_metrics(response, elapsed);
86
87 const bool net_ok = (response.error_code == CURLE_OK);
88 if (status_good(response)) {
89 return response;
90 }
91 if (attempt <= opt.max_retries && status_retry(response, net_ok)) {
92 ++metrics.retries;
93 long long sleep_ms = next_delay(attempt);
95 metrics.sleep_ms += sleep_ms;
96 std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
97 continue;
98 }
99
100 if (!net_ok) {
101 throw std::runtime_error("curl error: " + response.error_message);
102 }
103 throw std::runtime_error(
104 "http error: " + std::to_string(response.status_code)
105 );
106 }
107}
static bool status_retry(const http_response &response, bool net_ok)
Retry predicate for transient outcomes.
http_response request_get(CURLU *url_handle, std::chrono::milliseconds &elapsed, std::string_view accept={}, int timeout_sec=-1) const
Execute a single HTTP GET using the prepared URL handle.
network_metrics metrics
Aggregated metrics (atomic counters).
long long next_delay(int attempt) const
Compute the next backoff delay for attempt (1-based).
static curl_url_ptr build_url(std::string_view url, const parameter_list &params)
Construct a CURLU handle from url and append params.
static bool status_good(const http_response &response)
Success predicate: transport OK and HTTP 2xx.
void apply_server_retry_hint(long long &sleep_ms) const
Apply server-provided retry hint if present.
void update_metrics(const http_response &response, std::chrono::milliseconds elapsed)
Update counters and histograms after an attempt.

References apply_server_retry_hint(), corespace::http_response::error_code, next_delay(), status_good(), and update_metrics().

Here is the call graph for this function:

◆ metrics_info()

const network_metrics & corespace::http_client::metrics_info ( ) const
nodiscard

Access aggregated network metrics.

Returns
Const reference to the metrics snapshot.

Definition at line 72 of file http_client.cpp.

72{ return metrics; }

◆ next_delay()

long long corespace::http_client::next_delay ( int attempt) const
nodiscardprivate

Compute the next backoff delay for attempt (1-based).

Strategy: exponential backoff with full jitter. The base grows as retry_base_ms * 2^(attempt-1) and a uniform random component in [0, base] is added; the result is capped at retry_max_ms.

Parameters
attemptAttempt number starting from 1.
Returns
Milliseconds to sleep before the next attempt.

Definition at line 370 of file http_client.cpp.

370 {
371 const long long base = opt.retry_base_ms * (1 << (attempt - 1));
372 std::uniform_int_distribution<long long> d(0, base);
373 return std::min(base + d(rng()), opt.retry_max_ms);
374}
std::mt19937_64 & rng()
Shared PRNG seeded on first use.
Definition rng.cpp:28

Referenced by get(), post_form(), and post_raw().

Here is the caller graph for this function:

◆ post_form()

http_response corespace::http_client::post_form ( std::string_view url,
const parameter_list & form,
const parameter_list & query = {},
std::string_view accept = {},
int timeout_sec = -1 )

Perform an HTTP POST with form-encoded body.

Builds a URL from url and query, serializes form as application/x-www-form-urlencoded and posts it. Retry behaviour and metrics follow the same semantics as get().

Parameters
urlEndpoint URL.
formForm key/value pairs to serialize in the body.
queryOptional query parameters appended to the URL.
acceptOptional Accept header override.
timeout_secOptional per-request timeout in seconds; negative to use default.
Returns
http_response on success (2xx).
Exceptions
std::runtime_erroron terminal failure.

Definition at line 109 of file http_client.cpp.

113 {
114 std::lock_guard lk(mu);
115 const auto url_handle = build_url(url, query);
116 const std::string body = build_form_body(form);
117 for (int attempt = 1;; ++attempt) {
118 std::chrono::milliseconds elapsed { 0l };
119 http_response response = request_post(
120 url_handle.get(), elapsed, "application/x-www-form-urlencoded",
121 body, accept, timeout_sec
122 );
123 update_metrics(response, elapsed);
124 const bool net_ok = (response.error_code == CURLE_OK);
125 if (status_good(response)) {
126 return response;
127 }
128 if (attempt <= opt.max_retries && status_retry(response, net_ok)) {
129 ++metrics.retries;
130 long long sleep_ms = next_delay(attempt);
131 apply_server_retry_hint(sleep_ms);
132 metrics.sleep_ms += sleep_ms;
133 std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
134 continue;
135 }
136 if (!net_ok) {
137 throw std::runtime_error("curl error: " + response.error_message);
138 }
139 throw std::runtime_error(
140 "http error: " + std::to_string(response.status_code)
141 );
142 }
143}
http_response request_post(CURLU *url_handle, std::chrono::milliseconds &elapsed, std::string_view content_type, std::string_view body, std::string_view accept={}, int timeout_sec=-1) const
Execute a single HTTP POST with given body and content type.
std::string build_form_body(const parameter_list &form) const

References apply_server_retry_hint(), corespace::http_response::error_code, next_delay(), status_good(), and update_metrics().

Here is the call graph for this function:

◆ post_raw()

http_response corespace::http_client::post_raw ( std::string_view url,
std::string_view body,
std::string_view content_type,
const parameter_list & query = {},
std::string_view accept = {},
int timeout_sec = -1 )

Perform an HTTP POST with a raw body.

Builds a URL from url and query and posts the raw body with Content-Type set to content_type. Retry behaviour and metrics follow the same semantics as get().

Parameters
urlEndpoint URL.
bodyRaw request body to send.
content_typeContent-Type header value for the body.
queryOptional query parameters appended to the URL.
acceptOptional Accept header override.
timeout_secOptional per-request timeout in seconds; negative to use default.
Returns
http_response on success (2xx).
Exceptions
std::runtime_erroron terminal failure.

Definition at line 145 of file http_client.cpp.

149 {
150 std::lock_guard lk(mu);
151 const auto url_handle = build_url(url, query);
152 const std::string body_copy(body);
153 for (int attempt = 1;; ++attempt) {
154 std::chrono::milliseconds elapsed { 0l };
155 http_response response = request_post(
156 url_handle.get(), elapsed, content_type, body_copy, accept,
157 timeout_sec
158 );
159 update_metrics(response, elapsed);
160 const bool net_ok = (response.error_code == CURLE_OK);
161 if (status_good(response)) {
162 return response;
163 }
164 if (attempt <= opt.max_retries && status_retry(response, net_ok)) {
165 ++metrics.retries;
166 long long sleep_ms = next_delay(attempt);
167 apply_server_retry_hint(sleep_ms);
168 metrics.sleep_ms += sleep_ms;
169 std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms));
170 continue;
171 }
172 if (!net_ok) {
173 throw std::runtime_error("curl error: " + response.error_message);
174 }
175 throw std::runtime_error(
176 "http error: " + std::to_string(response.status_code)
177 );
178 }
179}

References apply_server_retry_hint(), corespace::http_response::error_code, next_delay(), status_good(), and update_metrics().

Here is the call graph for this function:

◆ request_get()

http_response corespace::http_client::request_get ( CURLU * url_handle,
std::chrono::milliseconds & elapsed,
std::string_view accept = {},
int timeout_sec = -1 ) const
private

Execute a single HTTP GET using the prepared URL handle.

Side effects:

  • Installs write callback to accumulate the response body.
  • Measures elapsed steady-clock time and returns it via elapsed.
  • Reads HTTP status and headers after the transfer.
Parameters
url_handlePrepared CURLU handle (owned by caller).
elapsedOut: time spent in curl_easy_perform.
acceptOptional Accept header override; empty means use client default.
timeout_secOptional per-request timeout in seconds; negative to use client default.
Returns
Populated http_response (may carry a libcurl error).

Definition at line 209 of file http_client.cpp.

212 {
213 using namespace std::chrono;
214
215 http_response response;
216
217 curl_slist* tmp_headers = nullptr;
218 if (!accept.empty()) {
219 const std::string h = "Accept: " + std::string(accept);
220 tmp_headers = curl_slist_append(nullptr, h.c_str());
221 curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, tmp_headers);
222 } else {
223 curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header_list.get());
224 }
225
226 curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 1L);
227 const long timeout_ms = (timeout_sec >= 0)
228 ? std::max(0L, timeout_sec * 1000L)
229 : opt.timeout_ms;
230 curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT_MS, timeout_ms);
231 curl_easy_setopt(curl.get(), CURLOPT_CURLU, url_handle);
232 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_callback);
233 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response.text);
234
235 const auto t0 = steady_clock::now();
236 response.error_code = curl_easy_perform(curl.get());
237 curl_easy_setopt(curl.get(), CURLOPT_CURLU, nullptr);
238 const auto t1 = steady_clock::now();
239 elapsed = duration_cast<milliseconds>(t1 - t0);
240
241 curl_easy_getinfo(
242 curl.get(), CURLINFO_RESPONSE_CODE, &response.status_code
243 );
244 update_headers(response);
245
246 if (response.error_code != CURLE_OK) {
247 response.error_message = curl_easy_strerror(response.error_code);
248 }
249
250 if (tmp_headers) {
251 curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header_list.get());
252 curl_slist_free_all(tmp_headers);
253 }
254
255 return response;
256}
void update_headers(http_response &response) const
Refresh the header multimap from the last transfer.
static size_t write_callback(const char *ptr, size_t size, size_t n, void *data)
libcurl write callback: append chunk to response body.

References corespace::http_response::error_code, corespace::http_response::error_message, and update_headers().

Here is the call graph for this function:

◆ request_post()

http_response corespace::http_client::request_post ( CURLU * url_handle,
std::chrono::milliseconds & elapsed,
std::string_view content_type,
std::string_view body,
std::string_view accept = {},
int timeout_sec = -1 ) const
private

Execute a single HTTP POST with given body and content type.

Sets temporary headers (Content-Type and Accept), posts the body, measures elapsed time in elapsed, reads status and headers, and records any libcurl error message.

Parameters
url_handlePrepared CURLU handle (owned by caller).
elapsedOut: time spent in curl_easy_perform.
content_typeContent-Type header value.
bodyBody bytes to send.
acceptOptional Accept header override; empty means use client default.
timeout_secOptional per-request timeout in seconds; negative to use client default.
Returns
Populated http_response (may carry a libcurl error).

Definition at line 258 of file http_client.cpp.

262 {
263 using namespace std::chrono;
264 http_response response;
265 curl_slist* tmp_headers = nullptr;
266
267 const std::string ct = "Content-Type: " + std::string(content_type);
268 tmp_headers = curl_slist_append(tmp_headers, ct.c_str());
269 const std::string acc = "Accept: "
270 + std::string(accept.empty() ? opt.accept : std::string(accept));
271 tmp_headers = curl_slist_append(tmp_headers, acc.c_str());
272 curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, tmp_headers);
273
274 curl_easy_setopt(curl.get(), CURLOPT_CURLU, url_handle);
275 const long timeout_ms = (timeout_sec >= 0)
276 ? std::max(0L, timeout_sec * 1000L)
277 : opt.timeout_ms;
278 curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT_MS, timeout_ms);
279 curl_easy_setopt(curl.get(), CURLOPT_POST, 1L);
280 curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, body.data());
281 curl_easy_setopt(
282 curl.get(), CURLOPT_POSTFIELDSIZE, static_cast<long>(body.size())
283 );
284 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_callback);
285 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response.text);
286 const auto t0 = steady_clock::now();
287 response.error_code = curl_easy_perform(curl.get());
288 curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, nullptr);
289 curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE, 0L);
290 curl_easy_setopt(curl.get(), CURLOPT_POST, 0L);
291 curl_easy_setopt(curl.get(), CURLOPT_CURLU, nullptr);
292 const auto t1 = steady_clock::now();
293 elapsed = duration_cast<milliseconds>(t1 - t0);
294 curl_easy_getinfo(
295 curl.get(), CURLINFO_RESPONSE_CODE, &response.status_code
296 );
297 update_headers(response);
298 if (response.error_code != CURLE_OK) {
299 response.error_message = curl_easy_strerror(response.error_code);
300 }
301 curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header_list.get());
302 if (tmp_headers) {
303 curl_slist_free_all(tmp_headers);
304 }
305 return response;
306}

References corespace::http_response::error_code, corespace::http_response::error_message, and update_headers().

Here is the call graph for this function:

◆ status_good()

bool corespace::http_client::status_good ( const http_response & response)
staticnodiscardprivate

Success predicate: transport OK and HTTP 2xx.

Parameters
responseResponse to check.
Returns
true if CURLE_OK and 200 <= status < 300.

Definition at line 358 of file http_client.cpp.

358 {
359 return response.error_code == CURLE_OK && response.status_code >= 200
360 && response.status_code < 300;
361}

References corespace::http_response::error_code.

Referenced by get(), post_form(), and post_raw().

Here is the caller graph for this function:

◆ status_retry()

bool corespace::http_client::status_retry ( const http_response & response,
bool net_ok )
staticnodiscardprivate

Retry predicate for transient outcomes.

Retries on:

  • any libcurl error (i.e., !net_ok),
  • HTTP 408 (Request Timeout),
  • HTTP 429 (Too Many Requests),
  • HTTP 5xx.
Parameters
responseResponse to inspect.
net_okWhether the transport completed without libcurl error.
Returns
true if another attempt should be made.

Definition at line 363 of file http_client.cpp.

365 {
366 return !net_ok || response.status_code == 429 || response.status_code == 408
367 || (response.status_code >= 500 && response.status_code < 600);
368}

◆ update_headers()

void corespace::http_client::update_headers ( http_response & response) const
private

Refresh the header multimap from the last transfer.

Enumerates headers via curl_easy_nextheader and fills response.header.

Parameters
responseResponse object to update.

Definition at line 335 of file http_client.cpp.

335 {
336 response.header.clear();
337 for (curl_header* header = nullptr;;) {
338 header = curl_easy_nextheader(curl.get(), CURLH_HEADER, 0, header);
339 if (!header) {
340 break;
341 }
342 response.header.emplace(header->name, header->value);
343 }
344}

Referenced by request_get(), and request_post().

Here is the caller graph for this function:

◆ update_metrics()

void corespace::http_client::update_metrics ( const http_response & response,
std::chrono::milliseconds elapsed )
private

Update counters and histograms after an attempt.

Increments requests, accumulates network_ms, bumps status histogram (if within bounds), and adds to bytes_received.

Parameters
responseResult of the attempt.
elapsedDuration spent in libcurl during the attempt.

Definition at line 346 of file http_client.cpp.

348 {
349 ++metrics.requests;
350 metrics.network_ms += elapsed.count();
351
352 if (response.status_code < metrics.statuses.size()) {
353 ++metrics.statuses[response.status_code];
354 }
355 metrics.bytes_received += response.text.size();
356}

Referenced by get(), post_form(), and post_raw().

Here is the caller graph for this function:

◆ write_callback()

size_t corespace::http_client::write_callback ( const char * ptr,
size_t size,
size_t n,
void * data )
staticprivate

libcurl write callback: append chunk to response body.

Parameters
ptrPointer to received data.
sizeElement size.
nNumber of elements.
datastd::string* accumulator (response body).
Returns
Number of bytes consumed (size * n).

Definition at line 390 of file http_client.cpp.

392 {
393 const size_t total = size * n;
394 auto* text = static_cast<std::string*>(data);
395 text->append(ptr, total);
396 return total;
397}

Member Data Documentation

◆ curl

std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> corespace::http_client::curl
private
Initial value:
{
nullptr, &curl_easy_cleanup
}

Reused easy handle (not thread-safe).

Definition at line 284 of file http_client.hpp.

284 {
285 nullptr, &curl_easy_cleanup
286 };

◆ header_list

std::unique_ptr<curl_slist, decltype(&curl_slist_free_all)> corespace::http_client::header_list
private
Initial value:
{
nullptr, &curl_slist_free_all
}

Owned request header list.

Definition at line 287 of file http_client.hpp.

287 {
288 nullptr, &curl_slist_free_all
289 };

◆ metrics

network_metrics corespace::http_client::metrics
private

Aggregated metrics (atomic counters).

Definition at line 282 of file http_client.hpp.

◆ mu

std::mutex corespace::http_client::mu
mutableprivate

Definition at line 283 of file http_client.hpp.

◆ opt

const network_options corespace::http_client::opt {}
private

Fixed options installed at construction.

Definition at line 281 of file http_client.hpp.

281{};

The documentation for this class was generated from the following files: