118 const std::string& body) {
120 return kcenon::common::make_error<http_response>(
122 "http_options.endpoint is empty", std::string(
error_source));
128 std::lock_guard<std::mutex> lock(override_mutex());
129 fn_copy = override_fn();
148 static std::once_flag s_curl_once;
149 std::call_once(s_curl_once, [] {
150 (void)curl_global_init(CURL_GLOBAL_DEFAULT);
153 curl_ptr curl(curl_easy_init());
155 return kcenon::common::make_error<http_response>(
162 curl_slist* raw =
nullptr;
163 const std::string ct_header =
"Content-Type: " +
content_type;
164 raw = curl_slist_append(raw, ct_header.c_str());
165 const std::string action_header =
167 raw = curl_slist_append(raw, action_header.c_str());
168 raw = curl_slist_append(raw,
"Accept: multipart/related, application/xop+xml, application/soap+xml");
169 raw = curl_slist_append(raw,
"Expect:");
173 response_sink response;
175 curl_easy_setopt(curl.get(), CURLOPT_URL, opts.
endpoint.c_str());
176 curl_easy_setopt(curl.get(), CURLOPT_POST, 1L);
177 curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, headers.get());
178 curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, opts.
user_agent.c_str());
179 curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, body.data());
180 curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDSIZE_LARGE,
181 static_cast<curl_off_t
>(body.size()));
182 curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, write_callback);
183 curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response);
184 curl_easy_setopt(curl.get(), CURLOPT_HEADERFUNCTION, header_callback);
185 curl_easy_setopt(curl.get(), CURLOPT_HEADERDATA, &response);
186 curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 0L);
187 curl_easy_setopt(curl.get(), CURLOPT_NOSIGNAL, 1L);
189 curl_easy_setopt(curl.get(), CURLOPT_CONNECTTIMEOUT_MS,
191 curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT_MS,
194 curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYPEER,
196 curl_easy_setopt(curl.get(), CURLOPT_SSL_VERIFYHOST,
199 curl_easy_setopt(curl.get(), CURLOPT_CAINFO,
203 curl_easy_setopt(curl.get(), CURLOPT_SSLCERT,
207 curl_easy_setopt(curl.get(), CURLOPT_SSLKEY,
211 const CURLcode rc = curl_easy_perform(curl.get());
212 if (rc != CURLE_OK) {
217 if (rc == CURLE_WRITE_ERROR && response.oversize) {
218 return kcenon::common::make_error<http_response>(
220 "registry response exceeds " +
221 std::to_string(kMaxResponseBytes) +
222 " byte cap; transfer aborted",
226 rc == CURLE_PEER_FAILED_VERIFICATION || rc == CURLE_SSL_CONNECT_ERROR ||
227 rc == CURLE_SSL_CERTPROBLEM || rc == CURLE_SSL_CIPHER ||
228 rc == CURLE_SSL_ISSUER_ERROR || rc == CURLE_SSL_CACERT_BADFILE;
230 : (rc == CURLE_OPERATION_TIMEDOUT
233 return kcenon::common::make_error<http_response>(
234 static_cast<int>(
code),
235 std::string(
"libcurl error ") + std::to_string(
static_cast<int>(rc)) +
236 ": " + curl_easy_strerror(rc),
241 curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &
out.status_code);
242 out.content_type = std::move(response.content_type);
243 out.body = std::move(response.buffer);
245 if (
out.status_code < 200 ||
out.status_code >= 300) {
246 return kcenon::common::make_error<http_response>(
248 std::string(
"unexpected HTTP status ") +
249 std::to_string(
out.status_code),