1 #include "httpclient.h"
6 #include "utilities/aktualizr_version.h"
7 #include "utilities/utils.h"
22 static size_t writeString(
void* contents,
size_t size,
size_t nmemb,
void* userp) {
28 if (arg->out.length() + size * nmemb >
static_cast<uint64_t
>(arg->limit)) {
32 (
static_cast<WriteStringArg*
>(userp))->out.append(
static_cast<char*
>(contents), size * nmemb);
38 HttpClient::HttpClient(
const std::vector<std::string>* extra_headers) {
39 curl = curl_easy_init();
40 if (curl ==
nullptr) {
41 throw std::runtime_error(
"Could not initialize curl");
45 curlEasySetoptWrapper(curl, CURLOPT_NOSIGNAL, 1L);
46 curlEasySetoptWrapper(curl, CURLOPT_TIMEOUT, 60L);
47 curlEasySetoptWrapper(curl, CURLOPT_CONNECTTIMEOUT, 60L);
48 curlEasySetoptWrapper(curl, CURLOPT_CAPATH, Utils::getCaPath());
51 curlEasySetoptWrapper(curl, CURLOPT_WRITEFUNCTION, writeString);
52 curlEasySetoptWrapper(curl, CURLOPT_WRITEDATA, NULL);
54 curlEasySetoptWrapper(curl, CURLOPT_VERBOSE, get_curlopt_verbose());
56 headers = curl_slist_append(headers,
"Accept: */*");
58 if (extra_headers !=
nullptr) {
59 for (
const auto& header : *extra_headers) {
60 headers = curl_slist_append(headers, header.c_str());
63 curlEasySetoptWrapper(curl, CURLOPT_USERAGENT, Utils::getUserAgent());
66 HttpClient::HttpClient(
const HttpClient& curl_in) : pkcs11_key(curl_in.pkcs11_key), pkcs11_cert(curl_in.pkcs11_key) {
67 curl = curl_easy_duphandle(curl_in.curl);
68 headers = curl_slist_dup(curl_in.headers);
73 HttpClient::~HttpClient() {
74 curl_slist_free_all(headers);
75 curl_easy_cleanup(curl);
78 void HttpClient::setCerts(
const std::string& ca, CryptoSource ca_source,
const std::string& cert,
79 CryptoSource cert_source,
const std::string& pkey, CryptoSource pkey_source) {
80 curlEasySetoptWrapper(curl, CURLOPT_SSL_VERIFYPEER, 1);
81 curlEasySetoptWrapper(curl, CURLOPT_SSL_VERIFYHOST, 2);
82 curlEasySetoptWrapper(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
84 if (ca_source == CryptoSource::kPkcs11) {
85 throw std::runtime_error(
"Accessing CA certificate on PKCS11 devices isn't currently supported");
87 std::unique_ptr<TemporaryFile> tmp_ca_file = std_::make_unique<TemporaryFile>(
"tls-ca");
88 tmp_ca_file->PutContents(ca);
89 curlEasySetoptWrapper(curl, CURLOPT_CAINFO, tmp_ca_file->Path().c_str());
90 tls_ca_file = std::move_if_noexcept(tmp_ca_file);
92 if (cert_source == CryptoSource::kPkcs11) {
93 curlEasySetoptWrapper(curl, CURLOPT_SSLCERT, cert.c_str());
94 curlEasySetoptWrapper(curl, CURLOPT_SSLCERTTYPE,
"ENG");
96 std::unique_ptr<TemporaryFile> tmp_cert_file = std_::make_unique<TemporaryFile>(
"tls-cert");
97 tmp_cert_file->PutContents(cert);
98 curlEasySetoptWrapper(curl, CURLOPT_SSLCERT, tmp_cert_file->Path().c_str());
99 curlEasySetoptWrapper(curl, CURLOPT_SSLCERTTYPE,
"PEM");
100 tls_cert_file = std::move_if_noexcept(tmp_cert_file);
102 pkcs11_cert = (cert_source == CryptoSource::kPkcs11);
104 if (pkey_source == CryptoSource::kPkcs11) {
105 curlEasySetoptWrapper(curl, CURLOPT_SSLENGINE,
"pkcs11");
106 curlEasySetoptWrapper(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
107 curlEasySetoptWrapper(curl, CURLOPT_SSLKEY, pkey.c_str());
108 curlEasySetoptWrapper(curl, CURLOPT_SSLKEYTYPE,
"ENG");
110 std::unique_ptr<TemporaryFile> tmp_pkey_file = std_::make_unique<TemporaryFile>(
"tls-pkey");
111 tmp_pkey_file->PutContents(pkey);
112 curlEasySetoptWrapper(curl, CURLOPT_SSLKEY, tmp_pkey_file->Path().c_str());
113 curlEasySetoptWrapper(curl, CURLOPT_SSLKEYTYPE,
"PEM");
114 tls_pkey_file = std::move_if_noexcept(tmp_pkey_file);
116 pkcs11_key = (pkey_source == CryptoSource::kPkcs11);
119 HttpResponse HttpClient::get(
const std::string& url, int64_t maxsize) {
120 CURL* curl_get = Utils::curlDupHandleWrapper(curl, pkcs11_key);
122 curlEasySetoptWrapper(curl_get, CURLOPT_HTTPHEADER, headers);
125 curlEasySetoptWrapper(curl_get, CURLOPT_SSLCERTTYPE,
"ENG");
130 curlEasySetoptWrapper(curl_get, CURLOPT_POSTFIELDS,
"");
131 curlEasySetoptWrapper(curl_get, CURLOPT_URL, url.c_str());
132 curlEasySetoptWrapper(curl_get, CURLOPT_HTTPGET, 1L);
133 LOG_DEBUG <<
"GET " << url;
134 HttpResponse response = perform(curl_get, RETRY_TIMES, maxsize);
135 curl_easy_cleanup(curl_get);
139 HttpResponse HttpClient::post(
const std::string& url,
const std::string& content_type,
const std::string&
data) {
140 CURL* curl_post = Utils::curlDupHandleWrapper(curl, pkcs11_key);
141 curl_slist* req_headers = curl_slist_dup(headers);
142 req_headers = curl_slist_append(req_headers, (std::string(
"Content-Type: ") + content_type).c_str());
143 curlEasySetoptWrapper(curl_post, CURLOPT_HTTPHEADER, req_headers);
144 curlEasySetoptWrapper(curl_post, CURLOPT_URL, url.c_str());
145 curlEasySetoptWrapper(curl_post, CURLOPT_POST, 1);
146 curlEasySetoptWrapper(curl_post, CURLOPT_POSTFIELDS,
data.c_str());
147 auto result = perform(curl_post, RETRY_TIMES, HttpInterface::kPostRespLimit);
148 curl_easy_cleanup(curl_post);
149 curl_slist_free_all(req_headers);
153 HttpResponse HttpClient::post(
const std::string& url,
const Json::Value&
data) {
154 std::string data_str = Utils::jsonToCanonicalStr(
data);
155 LOG_TRACE <<
"post request body:" <<
data;
156 return post(url,
"application/json", data_str);
159 HttpResponse HttpClient::put(
const std::string& url,
const std::string& content_type,
const std::string&
data) {
160 CURL* curl_put = Utils::curlDupHandleWrapper(curl, pkcs11_key);
161 curl_slist* req_headers = curl_slist_dup(headers);
162 req_headers = curl_slist_append(req_headers, (std::string(
"Content-Type: ") + content_type).c_str());
163 curlEasySetoptWrapper(curl_put, CURLOPT_HTTPHEADER, req_headers);
164 curlEasySetoptWrapper(curl_put, CURLOPT_URL, url.c_str());
165 curlEasySetoptWrapper(curl_put, CURLOPT_POSTFIELDS,
data.c_str());
166 curlEasySetoptWrapper(curl_put, CURLOPT_CUSTOMREQUEST,
"PUT");
168 curl_easy_cleanup(curl_put);
169 curl_slist_free_all(req_headers);
173 HttpResponse HttpClient::put(
const std::string& url,
const Json::Value&
data) {
174 std::string data_str = Utils::jsonToCanonicalStr(
data);
175 LOG_TRACE <<
"put request body:" <<
data;
176 return put(url,
"application/json", data_str);
179 HttpResponse HttpClient::perform(CURL* curl_handler,
int retry_times, int64_t size_limit) {
180 if (size_limit >= 0) {
183 curlEasySetoptWrapper(curl_handler, CURLOPT_MAXFILESIZE_LARGE, size_limit);
185 curlEasySetoptWrapper(curl_handler, CURLOPT_LOW_SPEED_TIME, speed_limit_time_interval_);
186 curlEasySetoptWrapper(curl_handler, CURLOPT_LOW_SPEED_LIMIT, speed_limit_bytes_per_sec_);
189 response_arg.limit = size_limit;
190 curlEasySetoptWrapper(curl_handler, CURLOPT_WRITEDATA,
static_cast<void*
>(&response_arg));
191 CURLcode
result = curl_easy_perform(curl_handler);
193 curl_easy_getinfo(curl_handler, CURLINFO_RESPONSE_CODE, &http_code);
195 if (response.curl_code != CURLE_OK || response.http_status_code >= 500) {
196 std::ostringstream error_message;
197 error_message <<
"curl error " << response.curl_code <<
" (http code " << response.http_status_code
198 <<
"): " << response.error_message;
199 LOG_ERROR << error_message.str();
200 if (retry_times != 0) {
202 response = perform(curl_handler, --retry_times, size_limit);
205 LOG_TRACE <<
"response http code: " << response.http_status_code;
206 LOG_TRACE <<
"response: " << response.body;
210 HttpResponse HttpClient::download(
const std::string& url, curl_write_callback write_cb,
211 curl_xferinfo_callback progress_cb,
void* userp, curl_off_t from) {
212 return downloadAsync(url, write_cb, progress_cb, userp, from,
nullptr).get();
215 std::future<HttpResponse> HttpClient::downloadAsync(
const std::string& url, curl_write_callback write_cb,
216 curl_xferinfo_callback progress_cb,
void* userp, curl_off_t from,
217 CurlHandler* easyp) {
218 CURL* curl_download = Utils::curlDupHandleWrapper(curl, pkcs11_key);
220 CurlHandler curlp = CurlHandler(curl_download, curl_easy_cleanup);
222 if (easyp !=
nullptr) {
226 curlEasySetoptWrapper(curl_download, CURLOPT_URL, url.c_str());
227 curlEasySetoptWrapper(curl_download, CURLOPT_HTTPGET, 1L);
228 curlEasySetoptWrapper(curl_download, CURLOPT_FOLLOWLOCATION, 1L);
229 curlEasySetoptWrapper(curl_download, CURLOPT_MAXREDIRS, 10L);
230 curlEasySetoptWrapper(curl_download, CURLOPT_WRITEFUNCTION, write_cb);
231 curlEasySetoptWrapper(curl_download, CURLOPT_WRITEDATA, userp);
232 if (progress_cb !=
nullptr) {
233 curlEasySetoptWrapper(curl_download, CURLOPT_NOPROGRESS, 0);
234 curlEasySetoptWrapper(curl_download, CURLOPT_XFERINFOFUNCTION, progress_cb);
235 curlEasySetoptWrapper(curl_download, CURLOPT_XFERINFODATA, userp);
237 curlEasySetoptWrapper(curl_download, CURLOPT_TIMEOUT, 0);
238 curlEasySetoptWrapper(curl_download, CURLOPT_LOW_SPEED_TIME, speed_limit_time_interval_);
239 curlEasySetoptWrapper(curl_download, CURLOPT_LOW_SPEED_LIMIT, speed_limit_bytes_per_sec_);
240 curlEasySetoptWrapper(curl_download, CURLOPT_RESUME_FROM_LARGE, from);
242 std::promise<HttpResponse> resp_promise;
243 auto resp_future = resp_promise.get_future();
245 [curlp](std::promise<HttpResponse> promise) {
246 CURLcode
result = curl_easy_perform(curlp.get());
248 curl_easy_getinfo(curlp.get(), CURLINFO_RESPONSE_CODE, &http_code);
250 promise.set_value(response);
252 std::move(resp_promise))
257 bool HttpClient::updateHeader(
const std::string& name,
const std::string& value) {
258 curl_slist* item = headers;
259 std::string lookfor(name +
": ");
261 while (item !=
nullptr) {
262 if (strncmp(lookfor.c_str(), item->data, lookfor.length()) == 0) {
265 item->data = strdup(lookfor.c_str());
273 curl_slist* HttpClient::curl_slist_dup(curl_slist* sl) {
274 curl_slist* new_list =
nullptr;
276 for (curl_slist* item = sl; item !=
nullptr; item = item->next) {
277 new_list = curl_slist_append(new_list, item->data);