mirror of
https://github.com/Xaymar/obs-StreamFX
synced 2024-11-28 22:33:00 +00:00
util/curl: Add C++ wrapper for CURL
This commit is contained in:
parent
d3c6472526
commit
34ae5dd64b
3 changed files with 389 additions and 2 deletions
|
@ -295,6 +295,7 @@ configure_file(
|
|||
"${PROJECT_BINARY_DIR}/generated/config.hpp"
|
||||
)
|
||||
|
||||
# libOBS
|
||||
if(NOT TARGET libobs)
|
||||
# Packaging
|
||||
if("${CMAKE_PACKAGE_SUFFIX_OVERRIDE}" STREQUAL "")
|
||||
|
@ -442,6 +443,30 @@ if(HAVE_OBS_FRONTEND)
|
|||
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
|
||||
endif()
|
||||
|
||||
# CURL
|
||||
if(REQUIRE_CURL)
|
||||
find_package(CURL QUIET)
|
||||
if(NOT CURL_FOUND)
|
||||
# CURL built by OBS Project is not compatible with find modules.
|
||||
if(${PropertyPrefix}OBS_DOWNLOAD)
|
||||
set(CURL_LIBRARIES "${obsdeps_SOURCE_DIR}/win${BITS}/bin/libcurl.lib")
|
||||
set(CURL_INCLUDE_DIRS "${obsdeps_SOURCE_DIR}/win${BITS}/include")
|
||||
endif()
|
||||
if(${PropertyPrefix}OBS_NATIVE) # Already defined by OBS
|
||||
set(CURL_LIBRARIES "${CURL_LIB}")
|
||||
set(CURL_INCLUDE_DIRS "${CURL_INCLUDE_DIR}")
|
||||
endif()
|
||||
if((${PropertyPrefix}OBS_REFERENCE) OR (${PropertyPrefix}OBS_PACKAGE))
|
||||
set(CURL_LIBRARIES "${OBS_DEPENDENCIES_DIR}/bin/libcurl.lib")
|
||||
set(CURL_INCLUDE_DIRS "${OBS_DEPENDENCIES_DIR}/include")
|
||||
endif()
|
||||
CacheSet(CURL_LIBRARY_DEBUG ${CURL_LIBRARIES})
|
||||
CacheSet(CURL_LIBRARY_RELEASE ${CURL_LIBRARIES})
|
||||
CacheSet(CURL_INCLUDE_DIR ${CURL_INCLUDE_DIRS})
|
||||
set(CURL_FOUND ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
################################################################################
|
||||
# Code
|
||||
################################################################################
|
||||
|
@ -456,9 +481,10 @@ set(PROJECT_PUBLIC )
|
|||
set(PROJECT_PRIVATE_GENERATED )
|
||||
set(PROJECT_PRIVATE_SOURCE )
|
||||
set(PROJECT_UI )
|
||||
set(PROJECT_DEFINES )
|
||||
set(PROJECT_DEFINITIONS )
|
||||
|
||||
## OBS Studio
|
||||
# Dependencies
|
||||
## Dependency: libOBS
|
||||
if(${PropertyPrefix}OBS_NATIVE)
|
||||
list(APPEND PROJECT_LIBRARIES
|
||||
libobs
|
||||
|
@ -483,6 +509,20 @@ elseif(${PropertyPrefix}OBS_DOWNLOAD)
|
|||
)
|
||||
endif()
|
||||
|
||||
## Dependency: CURL
|
||||
if(REQUIRE_CURL)
|
||||
list(APPEND PROJECT_PRIVATE_SOURCE
|
||||
"source/util/util-curl.hpp"
|
||||
"source/util/util-curl.cpp"
|
||||
)
|
||||
list(APPEND PROJECT_LIBRARIES
|
||||
${CURL_LIBRARY_RELEASE}
|
||||
)
|
||||
list(APPEND PROJECT_INCLUDE_DIRS
|
||||
${CURL_INCLUDE_DIR}
|
||||
)
|
||||
endif()
|
||||
|
||||
## Data & Source
|
||||
list(APPEND PROJECT_DATA
|
||||
"data/locale/en-US.ini"
|
||||
|
|
215
source/util/util-curl.cpp
Normal file
215
source/util/util-curl.cpp
Normal file
|
@ -0,0 +1,215 @@
|
|||
// Copyright (c) 2020 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#include "util-curl.hpp"
|
||||
#include <sstream>
|
||||
|
||||
int32_t util::curl::debug_helper(CURL* handle, curl_infotype type, char* data, size_t size, util::curl* self)
|
||||
{
|
||||
if (self->_debug_callback) {
|
||||
self->_debug_callback(handle, type, data, size);
|
||||
} else {
|
||||
#ifdef _DEBUG_CURL
|
||||
std::stringstream hd;
|
||||
for (size_t n = 0; n < size; n++) {
|
||||
hd << std::uppercase << std::setfill('0') << std::setw(2) << std::hex << static_cast<int32_t>(data[n])
|
||||
<< " ";
|
||||
if (n % 16 == 15) {
|
||||
hd << "\n ";
|
||||
}
|
||||
}
|
||||
std::string hds = hd.str();
|
||||
|
||||
switch (type) {
|
||||
case CURLINFO_TEXT:
|
||||
DLOG_DEBUG("<CURL> %.*s", size - 1, data);
|
||||
break;
|
||||
case CURLINFO_HEADER_IN:
|
||||
DLOG_DEBUG("<CURL> << Header: %.*s", size - 1, data);
|
||||
break;
|
||||
case CURLINFO_HEADER_OUT:
|
||||
DLOG_DEBUG("<CURL> >> Header: %.*s", size - 1, data);
|
||||
break;
|
||||
case CURLINFO_DATA_IN:
|
||||
DLOG_DEBUG("<CURL> << %lld bytes of data:\n %s", size, hds.c_str());
|
||||
break;
|
||||
case CURLINFO_DATA_OUT:
|
||||
DLOG_DEBUG("<CURL> >> %lld bytes of data:\n %s", size, hds.c_str());
|
||||
break;
|
||||
case CURLINFO_SSL_DATA_IN:
|
||||
DLOG_DEBUG("<CURL> << %lld bytes of SSL data:\n %s", size, hds.c_str());
|
||||
break;
|
||||
case CURLINFO_SSL_DATA_OUT:
|
||||
DLOG_DEBUG("<CURL> >> %lld bytes of SSL data:\n %s", size, hds.c_str());
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t util::curl::read_helper(void* ptr, size_t size, size_t count, util::curl* self)
|
||||
{
|
||||
if (self->_read_callback) {
|
||||
return self->_read_callback(ptr, size, count);
|
||||
} else {
|
||||
return size * count;
|
||||
}
|
||||
}
|
||||
|
||||
size_t util::curl::write_helper(void* ptr, size_t size, size_t count, util::curl* self)
|
||||
{
|
||||
if (self->_write_callback) {
|
||||
return self->_write_callback(ptr, size, count);
|
||||
} else {
|
||||
return size * count;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t util::curl::xferinfo_callback(util::curl* self, curl_off_t dlt, curl_off_t dln, curl_off_t ult, curl_off_t uln)
|
||||
{
|
||||
if (self->_xferinfo_callback) {
|
||||
return self->_xferinfo_callback(static_cast<uint64_t>(dlt), static_cast<uint64_t>(dln),
|
||||
static_cast<uint64_t>(ult), static_cast<uint64_t>(uln));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
util::curl::curl() : _curl(), _read_callback(), _write_callback(), _headers()
|
||||
{
|
||||
_curl = curl_easy_init();
|
||||
set_read_callback(nullptr);
|
||||
set_write_callback(nullptr);
|
||||
set_xferinfo_callback(nullptr);
|
||||
set_debug_callback(nullptr);
|
||||
|
||||
// Default settings.
|
||||
set_option(CURLOPT_NOPROGRESS, false);
|
||||
set_option(CURLOPT_PATH_AS_IS, false);
|
||||
set_option(CURLOPT_CRLF, false);
|
||||
#ifdef _DEBUG
|
||||
set_option(CURLOPT_VERBOSE, true);
|
||||
#else
|
||||
set_option(CURLOPT_VERBOSE, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
util::curl::~curl()
|
||||
{
|
||||
curl_easy_cleanup(_curl);
|
||||
}
|
||||
|
||||
void util::curl::clear_headers()
|
||||
{
|
||||
_headers.clear();
|
||||
}
|
||||
|
||||
void util::curl::clear_header(std::string header)
|
||||
{
|
||||
_headers.erase(header);
|
||||
}
|
||||
|
||||
void util::curl::set_header(std::string header, std::string value)
|
||||
{
|
||||
_headers.insert_or_assign(header, value);
|
||||
}
|
||||
|
||||
size_t perform_get_kv_size(std::string a, std::string b)
|
||||
{
|
||||
return a.size() + 2 + b.size() + 1;
|
||||
};
|
||||
|
||||
CURLcode util::curl::perform()
|
||||
{
|
||||
std::vector<char> buffer;
|
||||
struct curl_slist* headers = nullptr;
|
||||
|
||||
if (_headers.size() > 0) {
|
||||
// Calculate full buffer size.
|
||||
{
|
||||
size_t buffer_size = 0;
|
||||
for (auto kv : _headers) {
|
||||
buffer_size += perform_get_kv_size(kv.first, kv.second);
|
||||
}
|
||||
buffer.resize(buffer_size * 2);
|
||||
}
|
||||
// Create HTTP Headers.
|
||||
{
|
||||
size_t buffer_offset = 0;
|
||||
for (auto kv : _headers) {
|
||||
size_t size = perform_get_kv_size(kv.first, kv.second);
|
||||
|
||||
snprintf(&buffer.at(buffer_offset), size, "%s: %s", kv.first.c_str(), kv.second.c_str());
|
||||
|
||||
headers = curl_slist_append(headers, &buffer.at(buffer_offset));
|
||||
|
||||
buffer_offset += size;
|
||||
}
|
||||
}
|
||||
set_option<struct curl_slist*>(CURLOPT_HTTPHEADER, headers);
|
||||
}
|
||||
|
||||
CURLcode res = curl_easy_perform(_curl);
|
||||
|
||||
if (headers) {
|
||||
set_option<struct curl_slist*>(CURLOPT_HTTPHEADER, nullptr);
|
||||
curl_slist_free_all(headers);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void util::curl::reset()
|
||||
{
|
||||
curl_easy_reset(_curl);
|
||||
}
|
||||
|
||||
CURLcode util::curl::set_read_callback(curl_io_callback_t cb)
|
||||
{
|
||||
_read_callback = cb;
|
||||
if (CURLcode res = curl_easy_setopt(_curl, CURLOPT_READDATA, this); res != CURLE_OK)
|
||||
return res;
|
||||
return curl_easy_setopt(_curl, CURLOPT_READFUNCTION, &read_helper);
|
||||
}
|
||||
|
||||
CURLcode util::curl::set_write_callback(curl_io_callback_t cb)
|
||||
{
|
||||
_write_callback = cb;
|
||||
if (CURLcode res = curl_easy_setopt(_curl, CURLOPT_WRITEDATA, this); res != CURLE_OK)
|
||||
return res;
|
||||
return curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, &write_helper);
|
||||
}
|
||||
|
||||
CURLcode util::curl::set_xferinfo_callback(curl_xferinfo_callback_t cb)
|
||||
{
|
||||
_xferinfo_callback = cb;
|
||||
if (CURLcode res = curl_easy_setopt(_curl, CURLOPT_XFERINFODATA, this); res != CURLE_OK)
|
||||
return res;
|
||||
return curl_easy_setopt(_curl, CURLOPT_XFERINFOFUNCTION, &xferinfo_callback);
|
||||
}
|
||||
|
||||
CURLcode util::curl::set_debug_callback(curl_debug_callback_t cb)
|
||||
{
|
||||
_debug_callback = cb;
|
||||
if (CURLcode res = curl_easy_setopt(_curl, CURLOPT_DEBUGDATA, this); res != CURLE_OK)
|
||||
return res;
|
||||
return curl_easy_setopt(_curl, CURLOPT_DEBUGFUNCTION, &debug_helper);
|
||||
}
|
132
source/util/util-curl.hpp
Normal file
132
source/util/util-curl.hpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
// Copyright (c) 2020 Michael Fabian Dirks <info@xaymar.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
#include <cinttypes>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
extern "C" {
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <curl/curl.h>
|
||||
}
|
||||
|
||||
namespace util {
|
||||
typedef std::function<size_t(void*, size_t, size_t)> curl_io_callback_t;
|
||||
typedef std::function<int32_t(uint64_t, uint64_t, uint64_t, uint64_t)> curl_xferinfo_callback_t;
|
||||
typedef std::function<void(CURL*, curl_infotype, char*, size_t)> curl_debug_callback_t;
|
||||
|
||||
class curl {
|
||||
CURL* _curl;
|
||||
curl_io_callback_t _read_callback;
|
||||
curl_io_callback_t _write_callback;
|
||||
curl_xferinfo_callback_t _xferinfo_callback;
|
||||
curl_debug_callback_t _debug_callback;
|
||||
std::map<std::string, std::string> _headers;
|
||||
|
||||
static int32_t debug_helper(CURL* handle, curl_infotype type, char* data, size_t size, util::curl* userptr);
|
||||
static size_t read_helper(void*, size_t, size_t, util::curl*);
|
||||
static size_t write_helper(void*, size_t, size_t, util::curl*);
|
||||
static int32_t xferinfo_callback(util::curl*, curl_off_t, curl_off_t, curl_off_t, curl_off_t);
|
||||
|
||||
public:
|
||||
curl();
|
||||
~curl();
|
||||
|
||||
template<typename _Ty1>
|
||||
CURLcode set_option(CURLoption opt, _Ty1 value)
|
||||
{
|
||||
return curl_easy_setopt(_curl, opt, value);
|
||||
};
|
||||
|
||||
template<>
|
||||
CURLcode set_option(CURLoption opt, const bool value)
|
||||
{
|
||||
// CURL does not seem to accept boolean, so we err on the side of safety here.
|
||||
return curl_easy_setopt(_curl, opt, value ? 1 : 0);
|
||||
};
|
||||
|
||||
template<>
|
||||
CURLcode set_option(CURLoption opt, const std::string value)
|
||||
{
|
||||
return curl_easy_setopt(_curl, opt, value.c_str());
|
||||
};
|
||||
|
||||
template<>
|
||||
CURLcode set_option(CURLoption opt, const std::string_view value)
|
||||
{
|
||||
return curl_easy_setopt(_curl, opt, value.data());
|
||||
};
|
||||
|
||||
template<typename _Ty1>
|
||||
CURLcode get_info(CURLINFO info, _Ty1& value)
|
||||
{
|
||||
return curl_easy_getinfo(_curl, info, &value);
|
||||
};
|
||||
|
||||
template<>
|
||||
CURLcode get_info(CURLINFO info, std::vector<char>& value)
|
||||
{
|
||||
char* buffer;
|
||||
if (CURLcode res = curl_easy_getinfo(_curl, info, &buffer); res != CURLE_OK) {
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t buffer_len = strnlen(buffer, size_t(0xFFFF));
|
||||
value.resize(buffer_len);
|
||||
|
||||
memcpy(value.data(), buffer, value.size());
|
||||
return CURLE_OK;
|
||||
};
|
||||
|
||||
template<>
|
||||
CURLcode get_info(CURLINFO info, std::string& value)
|
||||
{
|
||||
std::vector<char> buffer;
|
||||
if (CURLcode res = get_info(info, buffer); res != CURLE_OK) {
|
||||
return res;
|
||||
}
|
||||
value = std::string(buffer.data(), buffer.data() + strlen(buffer.data()));
|
||||
return CURLE_OK;
|
||||
};
|
||||
|
||||
void clear_headers();
|
||||
|
||||
void clear_header(std::string header);
|
||||
|
||||
void set_header(std::string header, std::string value);
|
||||
|
||||
CURLcode perform();
|
||||
|
||||
void reset();
|
||||
|
||||
public /* Helpers */:
|
||||
CURLcode set_read_callback(curl_io_callback_t cb);
|
||||
|
||||
CURLcode set_write_callback(curl_io_callback_t cb);
|
||||
|
||||
CURLcode set_xferinfo_callback(curl_xferinfo_callback_t cb);
|
||||
|
||||
CURLcode set_debug_callback(curl_debug_callback_t cb);
|
||||
};
|
||||
} // namespace util
|
Loading…
Reference in a new issue