Http client via libcurl
This commit is contained in:
parent
bcc68ca450
commit
14929e9d15
59
cmake/modules/FindCURL.cmake
Normal file
59
cmake/modules/FindCURL.cmake
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||||
|
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||||
|
|
||||||
|
#.rst:
|
||||||
|
# FindCURL
|
||||||
|
# --------
|
||||||
|
#
|
||||||
|
# Find curl
|
||||||
|
#
|
||||||
|
# Find the native CURL headers and libraries.
|
||||||
|
#
|
||||||
|
# ::
|
||||||
|
#
|
||||||
|
# CURL_INCLUDE_DIRS - where to find curl/curl.h, etc.
|
||||||
|
# CURL_LIBRARIES - List of libraries when using curl.
|
||||||
|
# CURL_FOUND - True if curl found.
|
||||||
|
# CURL_VERSION_STRING - the version of curl found (since CMake 2.8.8)
|
||||||
|
|
||||||
|
# Look for the header file.
|
||||||
|
find_path(CURL_INCLUDE_DIR NAMES curl/curl.h)
|
||||||
|
mark_as_advanced(CURL_INCLUDE_DIR)
|
||||||
|
|
||||||
|
# Look for the library (sorted from most current/relevant entry to least).
|
||||||
|
find_library(CURL_LIBRARY NAMES
|
||||||
|
curl
|
||||||
|
# Windows MSVC Makefile:
|
||||||
|
libcurl_a
|
||||||
|
# Windows MSVC prebuilts:
|
||||||
|
curllib
|
||||||
|
libcurl_imp
|
||||||
|
curllib_static
|
||||||
|
# Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip):
|
||||||
|
libcurl
|
||||||
|
)
|
||||||
|
mark_as_advanced(CURL_LIBRARY)
|
||||||
|
|
||||||
|
if(CURL_INCLUDE_DIR)
|
||||||
|
foreach(_curl_version_header curlver.h curl.h)
|
||||||
|
if(EXISTS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}")
|
||||||
|
file(STRINGS "${CURL_INCLUDE_DIR}/curl/${_curl_version_header}" curl_version_str REGEX "^#define[\t ]+LIBCURL_VERSION[\t ]+\".*\"")
|
||||||
|
|
||||||
|
string(REGEX REPLACE "^#define[\t ]+LIBCURL_VERSION[\t ]+\"([^\"]*)\".*" "\\1" CURL_VERSION_STRING "${curl_version_str}")
|
||||||
|
unset(curl_version_str)
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package_handle_standard_args(CURL
|
||||||
|
REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR
|
||||||
|
VERSION_VAR CURL_VERSION_STRING)
|
||||||
|
|
||||||
|
if(CURL_FOUND)
|
||||||
|
set(CURL_LIBRARIES ${CURL_LIBRARY})
|
||||||
|
set(CURL_INCLUDE_DIRS ${CURL_INCLUDE_DIR})
|
||||||
|
|
||||||
|
message(STATUS " Curl libraries: = ${CURL_LIBRARIES}")
|
||||||
|
message(STATUS " Curl include dirs: = ${CURL_INCLUDE_DIRS}")
|
||||||
|
endif()
|
@ -199,6 +199,8 @@ add_library(libslic3r_gui STATIC
|
|||||||
${LIBDIR}/slic3r/GUI/2DBed.hpp
|
${LIBDIR}/slic3r/GUI/2DBed.hpp
|
||||||
${LIBDIR}/slic3r/GUI/wxExtensions.cpp
|
${LIBDIR}/slic3r/GUI/wxExtensions.cpp
|
||||||
${LIBDIR}/slic3r/GUI/wxExtensions.hpp
|
${LIBDIR}/slic3r/GUI/wxExtensions.hpp
|
||||||
|
${LIBDIR}/slic3r/Utils/Http.cpp
|
||||||
|
${LIBDIR}/slic3r/Utils/Http.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(admesh STATIC
|
add_library(admesh STATIC
|
||||||
@ -522,6 +524,11 @@ if (SLIC3R_PRUSACONTROL)
|
|||||||
target_link_libraries(XS ${wxWidgets_LIBRARIES})
|
target_link_libraries(XS ${wxWidgets_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(CURL REQUIRED)
|
||||||
|
include_directories(${CURL_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(XS ${CURL_LIBRARIES})
|
||||||
|
|
||||||
|
|
||||||
## OPTIONAL packages
|
## OPTIONAL packages
|
||||||
|
|
||||||
# Find eigen3 or use bundled version
|
# Find eigen3 or use bundled version
|
||||||
|
261
xs/src/slic3r/Utils/Http.cpp
Normal file
261
xs/src/slic3r/Utils/Http.cpp
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
#include "Http.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <iostream>
|
||||||
|
#include <tuple>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "../../libslic3r/libslic3r.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
// Private
|
||||||
|
|
||||||
|
class CurlGlobalInit
|
||||||
|
{
|
||||||
|
static const CurlGlobalInit instance;
|
||||||
|
|
||||||
|
CurlGlobalInit() { ::curl_global_init(CURL_GLOBAL_DEFAULT); }
|
||||||
|
~CurlGlobalInit() { ::curl_global_cleanup(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Http::priv
|
||||||
|
{
|
||||||
|
enum {
|
||||||
|
DEFAULT_SIZE_LIMIT = 5 * 1024 * 1024,
|
||||||
|
};
|
||||||
|
|
||||||
|
::CURL *curl;
|
||||||
|
::curl_httppost *form;
|
||||||
|
::curl_httppost *form_end;
|
||||||
|
::curl_slist *headerlist;
|
||||||
|
std::string buffer;
|
||||||
|
size_t limit;
|
||||||
|
|
||||||
|
std::thread io_thread;
|
||||||
|
Http::CompleteFn completefn;
|
||||||
|
Http::ErrorFn errorfn;
|
||||||
|
|
||||||
|
priv(const std::string &url);
|
||||||
|
~priv();
|
||||||
|
|
||||||
|
static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
|
||||||
|
std::string body_size_error();
|
||||||
|
void http_perform();
|
||||||
|
};
|
||||||
|
|
||||||
|
Http::priv::priv(const std::string &url) :
|
||||||
|
curl(::curl_easy_init()),
|
||||||
|
form(nullptr),
|
||||||
|
form_end(nullptr),
|
||||||
|
headerlist(nullptr)
|
||||||
|
{
|
||||||
|
if (curl == nullptr) {
|
||||||
|
throw std::runtime_error(std::string("Could not construct Curl object"));
|
||||||
|
}
|
||||||
|
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
Http::priv::~priv()
|
||||||
|
{
|
||||||
|
::curl_easy_cleanup(curl);
|
||||||
|
::curl_formfree(form);
|
||||||
|
::curl_slist_free_all(headerlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Http::priv::writecb(void *data, size_t size, size_t nmemb, void *userp)
|
||||||
|
{
|
||||||
|
auto self = static_cast<priv*>(userp);
|
||||||
|
const char *cdata = static_cast<char*>(data);
|
||||||
|
const size_t realsize = size * nmemb;
|
||||||
|
|
||||||
|
const size_t limit = self->limit > 0 ? self->limit : DEFAULT_SIZE_LIMIT;
|
||||||
|
if (self->buffer.size() + realsize > limit) {
|
||||||
|
// This makes curl_easy_perform return CURLE_WRITE_ERROR
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->buffer.append(cdata, realsize);
|
||||||
|
|
||||||
|
return realsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Http::priv::body_size_error()
|
||||||
|
{
|
||||||
|
return (boost::format("HTTP body data size exceeded limit (%1% bytes)") % limit).str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Http::priv::http_perform()
|
||||||
|
{
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this));
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (headerlist != nullptr) {
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (form != nullptr) {
|
||||||
|
::curl_easy_setopt(curl, CURLOPT_HTTPPOST, form);
|
||||||
|
}
|
||||||
|
|
||||||
|
CURLcode res = ::curl_easy_perform(curl);
|
||||||
|
long http_status = 0;
|
||||||
|
::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status);
|
||||||
|
|
||||||
|
if (res != CURLE_OK) {
|
||||||
|
std::string error;
|
||||||
|
if (res == CURLE_WRITE_ERROR) {
|
||||||
|
error = std::move(body_size_error());
|
||||||
|
} else {
|
||||||
|
error = ::curl_easy_strerror(res);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (errorfn) {
|
||||||
|
errorfn(std::move(buffer), std::move(error), http_status);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (completefn) {
|
||||||
|
completefn(std::move(buffer), http_status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Http::Http(const std::string &url) : p(new priv(url)) {}
|
||||||
|
|
||||||
|
|
||||||
|
// Public
|
||||||
|
|
||||||
|
Http::Http(Http &&other) : p(std::move(other.p)) {}
|
||||||
|
|
||||||
|
Http::~Http()
|
||||||
|
{
|
||||||
|
if (p && p->io_thread.joinable()) {
|
||||||
|
p->io_thread.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Http& Http::size_limit(size_t sizeLimit)
|
||||||
|
{
|
||||||
|
if (p) { p->limit = sizeLimit; }
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::header(std::string name, const std::string &value)
|
||||||
|
{
|
||||||
|
if (!p) { return * this; }
|
||||||
|
|
||||||
|
if (name.size() > 0) {
|
||||||
|
name.append(": ").append(value);
|
||||||
|
} else {
|
||||||
|
name.push_back(':');
|
||||||
|
}
|
||||||
|
p->headerlist = curl_slist_append(p->headerlist, name.c_str());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::remove_header(std::string name)
|
||||||
|
{
|
||||||
|
if (p) {
|
||||||
|
name.push_back(':');
|
||||||
|
p->headerlist = curl_slist_append(p->headerlist, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::ca_file(const std::string &name)
|
||||||
|
{
|
||||||
|
if (p) {
|
||||||
|
::curl_easy_setopt(p->curl, CURLOPT_CAINFO, name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::form_add(const std::string &name, const std::string &contents)
|
||||||
|
{
|
||||||
|
if (p) {
|
||||||
|
::curl_formadd(&p->form, &p->form_end,
|
||||||
|
CURLFORM_COPYNAME, name.c_str(),
|
||||||
|
CURLFORM_COPYCONTENTS, contents.c_str(),
|
||||||
|
CURLFORM_END
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::form_add_file(const std::string &name, const std::string &filename)
|
||||||
|
{
|
||||||
|
if (p) {
|
||||||
|
::curl_formadd(&p->form, &p->form_end,
|
||||||
|
CURLFORM_COPYNAME, name.c_str(),
|
||||||
|
CURLFORM_FILE, filename.c_str(),
|
||||||
|
CURLFORM_CONTENTTYPE, "application/octet-stream",
|
||||||
|
CURLFORM_END
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::on_complete(CompleteFn fn)
|
||||||
|
{
|
||||||
|
if (p) { p->completefn = std::move(fn); }
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http& Http::on_error(ErrorFn fn)
|
||||||
|
{
|
||||||
|
if (p) { p->errorfn = std::move(fn); }
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Http::Ptr Http::perform()
|
||||||
|
{
|
||||||
|
auto self = std::make_shared<Http>(std::move(*this));
|
||||||
|
|
||||||
|
if (self->p) {
|
||||||
|
auto io_thread = std::thread([self](){
|
||||||
|
self->p->http_perform();
|
||||||
|
});
|
||||||
|
self->p->io_thread = std::move(io_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Http::perform_sync()
|
||||||
|
{
|
||||||
|
if (p) { p->http_perform(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
Http Http::get(std::string url)
|
||||||
|
{
|
||||||
|
return std::move(Http{std::move(url)});
|
||||||
|
}
|
||||||
|
|
||||||
|
Http Http::post(std::string url)
|
||||||
|
{
|
||||||
|
Http http{std::move(url)};
|
||||||
|
curl_easy_setopt(http.p->curl, CURLOPT_POST, 1L);
|
||||||
|
return http;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
53
xs/src/slic3r/Utils/Http.hpp
Normal file
53
xs/src/slic3r/Utils/Http.hpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#ifndef slic3r_Http_hpp_
|
||||||
|
#define slic3r_Http_hpp_
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
|
/// Represetns a Http request
|
||||||
|
class Http : public std::enable_shared_from_this<Http> {
|
||||||
|
private:
|
||||||
|
struct priv;
|
||||||
|
public:
|
||||||
|
typedef std::shared_ptr<Http> Ptr;
|
||||||
|
typedef std::function<void(std::string /* body */, unsigned /* http_status */)> CompleteFn;
|
||||||
|
typedef std::function<void(std::string /* body */, std::string /* error */, unsigned /* http_status */)> ErrorFn;
|
||||||
|
|
||||||
|
Http(Http &&other);
|
||||||
|
|
||||||
|
static Http get(std::string url);
|
||||||
|
static Http post(std::string url);
|
||||||
|
~Http();
|
||||||
|
|
||||||
|
Http(const Http &) = delete;
|
||||||
|
Http& operator=(const Http &) = delete;
|
||||||
|
Http& operator=(Http &&) = delete;
|
||||||
|
|
||||||
|
Http& size_limit(size_t sizeLimit);
|
||||||
|
Http& header(std::string name, const std::string &value);
|
||||||
|
Http& remove_header(std::string name);
|
||||||
|
Http& ca_file(const std::string &filename);
|
||||||
|
Http& form_add(const std::string &name, const std::string &contents);
|
||||||
|
Http& form_add_file(const std::string &name, const std::string &filename);
|
||||||
|
|
||||||
|
Http& on_complete(CompleteFn fn);
|
||||||
|
Http& on_error(ErrorFn fn);
|
||||||
|
|
||||||
|
Ptr perform();
|
||||||
|
void perform_sync();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Http(const std::string &url);
|
||||||
|
|
||||||
|
std::unique_ptr<priv> p;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user