remwharead/src/curl_wrapper/src/curl_wrapper.cpp

241 lines
6.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* This file is part of curl_wrapper.
* Copyright © 2020 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "curl_wrapper.hpp"
#include "types.hpp"
#include <curl/curl.h>
#include <atomic>
#include <cstdint>
#include <exception>
#include <stdexcept>
#include <string>
namespace curl_wrapper
{
inline static std::atomic<std::uint64_t> curlwrapper_instances{0};
CURLWrapper::CURLWrapper()
{
if (curlwrapper_instances == 0)
{
// NOLINTNEXTLINE(hicpp-signed-bitwise)
check(curl_global_init(CURL_GLOBAL_ALL));
}
++curlwrapper_instances;
_connection = curl_easy_init();
if (_connection == nullptr)
{
throw std::runtime_error{"Failed to initialize curl."};
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_ERRORBUFFER, _buffer_error);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_WRITEFUNCTION, writer_body_wrapper);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_WRITEDATA, this);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_HEADERFUNCTION,
writer_headers_wrapper);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_HEADERDATA, this);
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
check(curl_easy_setopt(_connection, CURLOPT_FOLLOWLOCATION, 1L));
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_MAXREDIRS, 5L);
}
CURLWrapper::~CURLWrapper() noexcept
{
curl_easy_cleanup(_connection);
--curlwrapper_instances;
if (curlwrapper_instances == 0)
{
curl_global_cleanup();
}
}
string CURLWrapper::escape_url(const string_view url) const
{
char *cbuf{curl_easy_escape(_connection, url.data(),
static_cast<int>(url.size()))};
string sbuf{cbuf};
curl_free(cbuf);
return sbuf;
}
string CURLWrapper::unescape_url(const string_view url) const
{
char *cbuf{curl_easy_unescape(_connection, url.data(),
static_cast<int>(url.size()), nullptr)};
string sbuf{cbuf};
curl_free(cbuf);
return sbuf;
}
void CURLWrapper::set_useragent(const string_view useragent)
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
check(curl_easy_setopt(_connection, CURLOPT_USERAGENT, useragent.data()));
}
void CURLWrapper::set_proxy(const string_view proxy)
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
check(curl_easy_setopt(_connection, CURLOPT_PROXY, proxy.data()));
}
answer CURLWrapper::make_http_request(http_method method, string_view uri)
{
_buffer_headers.clear();
_buffer_body.clear();
switch (method)
{
case http_method::DELETE:
{
// NOTE: Use CURLOPT_MIMEPOST, then set to DELETE to send data.
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
check(curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "DELETE"));
break;
}
case http_method::GET:
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_HTTPGET, 1L);
break;
}
case http_method::HEAD:
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
check(curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "HEAD"));
break;
}
case http_method::PATCH:
{
// NOTE: Use CURLOPT_MIMEPOST, then set to PATCH to send data.
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
check(curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "PATCH"));
break;
}
case http_method::POST:
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
curl_easy_setopt(_connection, CURLOPT_POST, 1L);
// NOTE: Use CURLOPT_MIMEPOST to send data.
break;
}
case http_method::PUT:
{
// NOTE: Use CURLOPT_MIMEPOST, then set to PUT to send data.
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
check(curl_easy_setopt(_connection, CURLOPT_CUSTOMREQUEST, "PUT"));
break;
}
}
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
check(curl_easy_setopt(_connection, CURLOPT_URL, uri.data()));
try
{
check(curl_easy_perform(_connection));
}
catch (const CURLException &e)
{
// PARTIAL_FILE error seems to be normal for HEAD requests.
if (!(method == http_method::HEAD
&& e.error_code == CURLE_PARTIAL_FILE))
{
std::rethrow_exception(std::current_exception());
}
}
long http_status{0}; // NOLINT(google-runtime-int)
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
check(curl_easy_getinfo(_connection, CURLINFO_RESPONSE_CODE, &http_status));
return {static_cast<std::uint16_t>(http_status), _buffer_headers,
_buffer_body};
}
size_t CURLWrapper::writer_body(char *data, size_t size, size_t nmemb)
{
if (data == nullptr)
{
return 0;
}
_buffer_body.append(data, size * nmemb);
return size * nmemb;
}
size_t CURLWrapper::writer_headers(char *data, size_t size, size_t nmemb)
{
if (data == nullptr)
{
return 0;
}
_buffer_headers.append(data, size * nmemb);
return size * nmemb;
}
void CURLWrapper::check(const CURLcode code)
{
if (code != CURLE_OK)
{
throw CURLException{code, _buffer_error};
}
}
const char *CURLException::what() const noexcept
{
// NOTE: The string has to be static, or it'll vanish before it can be
// used. Couldn't find good documentation on that.
static string error_string;
error_string = _error_message;
if (!error_string.empty())
{
error_string = " " + error_string;
}
error_string = "libcurl error: " + std::to_string(error_code)
+ error_string;
return error_string.c_str();
}
} // namespace curl_wrapper