curl_wrapper/src/curl_wrapper.cpp

219 lines
5.9 KiB
C++
Raw Normal View History

2020-11-03 22:53:20 +01:00
/* 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"
2020-11-03 22:53:20 +01:00
#include <curl/curl.h>
#include <atomic>
#include <cstdint>
#include <exception>
#include <stdexcept>
#include <string>
namespace curl_wrapper
{
2020-11-08 15:50:14 +01:00
inline static std::atomic<std::uint64_t> curlwrapper_instances{0};
2020-11-03 22:53:20 +01:00
CURLWrapper::CURLWrapper()
2020-11-03 22:53:20 +01:00
{
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();
}
}
void CURLWrapper::set_useragent(string_view useragent)
{
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
check(curl_easy_setopt(_connection, CURLOPT_USERAGENT, useragent.data()));
}
2020-11-13 18:18:22 +01:00
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)
2020-11-03 22:53:20 +01:00
{
_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;
}
2020-11-03 22:53:20 +01:00
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)
2020-11-03 22:53:20 +01:00
// 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};
2020-11-03 22:53:20 +01:00
}
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() noexcept
{
if (!_error_message.empty())
{
_error_message = ": " + _error_message;
}
_error_message = "libcurl_error: " + std::to_string(error_code)
+ _error_message;
return _error_message.c_str();
}
} // namespace curl_wrapper