mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-04 05:50:47 +08:00
File download. Http class temporarily copied.
This commit is contained in:
parent
dba3c95297
commit
ad5b0d0bed
@ -110,6 +110,7 @@ if (SLIC3R_GUI)
|
||||
message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")
|
||||
|
||||
add_subdirectory(slic3r)
|
||||
add_subdirectory(downloader)
|
||||
endif()
|
||||
|
||||
|
||||
|
21
src/downloader/CMakeLists.txt
Normal file
21
src/downloader/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
#cmake_minimum_required(VERSION 3.13)
|
||||
#project(PrusaSlicer_Downloader)
|
||||
|
||||
add_executable(PrusaSlicer_Downloader WIN32
|
||||
Downloader.cpp
|
||||
Downloader.hpp
|
||||
FileGet.cpp
|
||||
FileGet.hpp
|
||||
FromSlicer/Http.cpp
|
||||
FromSlicer/Http.hpp
|
||||
)
|
||||
|
||||
set_target_properties(PrusaSlicer_Downloader PROPERTIES OUTPUT_NAME "prusa-slicer-downloader")
|
||||
|
||||
#encoding_check(PrusaSlicer_Downloader)
|
||||
|
||||
|
||||
|
||||
target_link_libraries(PrusaSlicer_Downloader libslic3r_gui)
|
||||
|
||||
|
122
src/downloader/Downloader.cpp
Normal file
122
src/downloader/Downloader.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
#include "Downloader.hpp"
|
||||
#include "FileGet.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <wx/event.h>
|
||||
|
||||
namespace Downloader {
|
||||
|
||||
enum
|
||||
{
|
||||
ID_Hello = 1,
|
||||
};
|
||||
|
||||
|
||||
wxBEGIN_EVENT_TABLE(DownloadFrame, wxFrame)
|
||||
EVT_MENU(ID_Hello, DownloadFrame::OnHello)
|
||||
EVT_MENU(wxID_EXIT, DownloadFrame::OnExit)
|
||||
EVT_MENU(wxID_ABOUT, DownloadFrame::OnAbout)
|
||||
wxEND_EVENT_TABLE()
|
||||
|
||||
|
||||
bool DownloadApp::OnInit()
|
||||
{
|
||||
DownloadFrame* frame = new DownloadFrame("Hello World", wxPoint(50, 50), wxSize(450, 340));
|
||||
frame->Show(true);
|
||||
return true;
|
||||
}
|
||||
DownloadFrame::DownloadFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
|
||||
: wxFrame(NULL, wxID_ANY, title, pos, size)
|
||||
{
|
||||
wxMenu* menuFile = new wxMenu;
|
||||
menuFile->Append(ID_Hello, "&Hello...\tCtrl-H",
|
||||
"Help string shown in status bar for this menu item");
|
||||
menuFile->AppendSeparator();
|
||||
menuFile->Append(wxID_EXIT);
|
||||
wxMenu* menuHelp = new wxMenu;
|
||||
menuHelp->Append(wxID_ABOUT);
|
||||
wxMenuBar* menuBar = new wxMenuBar;
|
||||
menuBar->Append(menuFile, "&File");
|
||||
menuBar->Append(menuHelp, "&Help");
|
||||
SetMenuBar(menuBar);
|
||||
CreateStatusBar();
|
||||
SetStatusText("Welcome to wxWidgets!");
|
||||
|
||||
m_log_label = new wxStaticText(this, wxID_ANY, "Log:");
|
||||
auto* sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(m_log_label, wxEXPAND);
|
||||
SetSizer(sizer);
|
||||
|
||||
// Bind(EVT_FILE_COMPLETE, &on_complete, this);
|
||||
Bind(EVT_FILE_COMPLETE, &DownloadFrame::on_complete, this);
|
||||
Bind(EVT_FILE_PROGRESS, &DownloadFrame::on_progress, this);
|
||||
Bind(EVT_FILE_ERROR, &DownloadFrame::on_error, this);
|
||||
}
|
||||
void DownloadFrame::OnExit(wxCommandEvent& event)
|
||||
{
|
||||
Close(true);
|
||||
}
|
||||
void DownloadFrame::OnAbout(wxCommandEvent& event)
|
||||
{
|
||||
wxMessageBox("This is a wxWidgets' Hello world sample",
|
||||
"About Hello World", wxOK | wxICON_INFORMATION);
|
||||
}
|
||||
void DownloadFrame::OnHello(wxCommandEvent& event)
|
||||
{
|
||||
|
||||
std::string test_url = "https%3A%2F%2Fmedia.printables.com%2Fmedia%2Fprints%2F152208%2Fstls%2F1431590_8b8287b3-03b1-4cbe-82d0-268a0affa171%2Ff1_logo.stl";
|
||||
std::shared_ptr<FileGet> file_get = FileGet(get_next_id(), test_url, this, boost::filesystem::path("C:\\Users\\User\\Downloads")) .get();
|
||||
}
|
||||
|
||||
void DownloadFrame::log(const wxString& msg)
|
||||
{
|
||||
m_log_lines++;
|
||||
wxString old_log = m_log_label->GetLabel();
|
||||
if (m_log_lines > 10) {
|
||||
size_t endline = old_log.Find('\n');
|
||||
endline = old_log.find('\n', endline + 1);
|
||||
if (endline != wxString::npos) {
|
||||
old_log = "Log:\n" + old_log.substr(endline + 1);
|
||||
}
|
||||
}
|
||||
m_log_label->SetLabel(old_log +"\n"+ msg);
|
||||
m_full_log += +"\n" + msg;
|
||||
}
|
||||
|
||||
void DownloadFrame::on_progress(wxCommandEvent& event)
|
||||
{
|
||||
log(std::to_string(event.GetInt()) + ": " + event.GetString());
|
||||
//SetStatusText("Progress: " + std::to_string(event.GetInt()));
|
||||
}
|
||||
void DownloadFrame::on_error(wxCommandEvent& event)
|
||||
{
|
||||
log(std::to_string(event.GetInt()) + ": " + event.GetString());
|
||||
//SetStatusText(event.GetString().c_str());
|
||||
}
|
||||
void DownloadFrame::on_complete(wxCommandEvent& event)
|
||||
{
|
||||
log(std::to_string(event.GetInt()) + ": Download complete " + event.GetString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
//wxIMPLEMENT_APP_NO_MAIN(MyApp);
|
||||
|
||||
|
||||
//int main()
|
||||
//{
|
||||
// wxEntry();
|
||||
// return 0;
|
||||
//}
|
||||
//int APIENTRY WinMain(HINSTANCE /* hInstance */, HINSTANCE /* hPrevInstance */, PWSTR /* lpCmdLine */, int /* nCmdShow */)
|
||||
//{
|
||||
// wxEntry();
|
||||
// return 0;
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
wxIMPLEMENT_APP(Downloader::DownloadApp);
|
37
src/downloader/Downloader.hpp
Normal file
37
src/downloader/Downloader.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef slic3r_Downloader_hpp_
|
||||
#define slic3r_Downloader_hpp_
|
||||
#include <wx/wx.h>
|
||||
|
||||
namespace Downloader {
|
||||
class DownloadApp : public wxApp
|
||||
{
|
||||
public:
|
||||
virtual bool OnInit();
|
||||
};
|
||||
|
||||
class DownloadFrame : public wxFrame
|
||||
{
|
||||
public:
|
||||
DownloadFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
|
||||
private:
|
||||
void OnHello(wxCommandEvent& event);
|
||||
void OnExit(wxCommandEvent& event);
|
||||
void OnAbout(wxCommandEvent& event);
|
||||
|
||||
void on_progress(wxCommandEvent& event);
|
||||
void on_error(wxCommandEvent& event);
|
||||
void on_complete(wxCommandEvent& event);
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
|
||||
void log(const wxString& msg);
|
||||
|
||||
int m_next_id { 0 };
|
||||
int get_next_id() {return ++m_next_id; }
|
||||
|
||||
wxStaticText* m_log_label;
|
||||
size_t m_log_lines { 0 };
|
||||
wxString m_full_log;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
179
src/downloader/FileGet.cpp
Normal file
179
src/downloader/FileGet.cpp
Normal file
@ -0,0 +1,179 @@
|
||||
#include "FileGet.hpp"
|
||||
|
||||
|
||||
#include <thread>
|
||||
#include <curl/curl.h>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
|
||||
|
||||
namespace Downloader {
|
||||
|
||||
namespace {
|
||||
std::string escape_url(const std::string& unescaped)
|
||||
{
|
||||
std::string ret_val;
|
||||
CURL* curl = curl_easy_init();
|
||||
if (curl) {
|
||||
int decodelen;
|
||||
char* decoded = curl_easy_unescape(curl, unescaped.c_str(), unescaped.size(), &decodelen);
|
||||
if (decoded) {
|
||||
ret_val = std::string(decoded);
|
||||
curl_free(decoded);
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
std::string filename_from_url(const std::string& url)
|
||||
{
|
||||
// TODO: can it be done with curl?
|
||||
size_t slash = url.find_last_of("/");
|
||||
if (slash == std::string::npos && slash != url.size()-1)
|
||||
return std::string();
|
||||
return url.substr(slash + 1, url.size() - slash + 1);
|
||||
}
|
||||
unsigned get_current_pid()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return GetCurrentProcessId();
|
||||
#else
|
||||
return ::getpid();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// int = DOWNLOAD ID; string = file path
|
||||
wxDEFINE_EVENT(EVT_FILE_COMPLETE, wxCommandEvent);
|
||||
// int = DOWNLOAD ID; string = error msg
|
||||
wxDEFINE_EVENT(EVT_FILE_ERROR, wxCommandEvent);
|
||||
// int = DOWNLOAD ID; string = progress percent
|
||||
wxDEFINE_EVENT(EVT_FILE_PROGRESS, wxCommandEvent);
|
||||
|
||||
struct FileGet::priv
|
||||
{
|
||||
const int m_id;
|
||||
std::string m_url;
|
||||
std::string m_filename;
|
||||
std::thread m_io_thread;
|
||||
wxEvtHandler* m_evt_handler;
|
||||
boost::filesystem::path m_dest_folder;
|
||||
|
||||
priv(int ID, std::string&& url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder);
|
||||
|
||||
void get_perform();
|
||||
};
|
||||
|
||||
FileGet::priv::priv(int ID, std::string&& url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder)
|
||||
: m_id(ID)
|
||||
, m_url(std::move(url))
|
||||
, m_evt_handler(evt_handler)
|
||||
, m_dest_folder(dest_folder)
|
||||
{
|
||||
}
|
||||
|
||||
void FileGet::priv::get_perform()
|
||||
{
|
||||
assert(m_evt_handler);
|
||||
assert(!m_url.empty());
|
||||
m_url = escape_url(m_url);
|
||||
assert(!m_url.empty());
|
||||
m_filename = filename_from_url(m_url);
|
||||
assert(!m_filename.empty());
|
||||
assert(boost::filesystem::is_directory(m_dest_folder));
|
||||
|
||||
Downloader::Http::get(m_url)
|
||||
//.size_limit(size_limit)
|
||||
.on_progress([&](Downloader::Http::Progress progress, bool& cancel) {
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_FILE_PROGRESS);
|
||||
if (progress.dlnow == 0)
|
||||
evt->SetString("0");
|
||||
else
|
||||
evt->SetString(std::to_string((float)progress.dltotal / (float)progress.dlnow));
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned http_status) {
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_FILE_ERROR);
|
||||
evt->SetString(error);
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
})
|
||||
.on_complete([&](std::string body, unsigned /* http_status */) {
|
||||
|
||||
size_t body_size = body.size();
|
||||
// TODO:
|
||||
//if (body_size != expected_size) {
|
||||
// return;
|
||||
//}
|
||||
|
||||
boost::filesystem::path dest_path = m_dest_folder / m_filename;
|
||||
std::string extension = boost::filesystem::extension(dest_path);
|
||||
std::string just_filename = m_filename.substr(0, m_filename.size() - extension.size());
|
||||
std::string final_filename = just_filename;
|
||||
|
||||
size_t version = 0;
|
||||
while (boost::filesystem::exists(m_dest_folder / (final_filename + extension)))
|
||||
{
|
||||
++version;
|
||||
final_filename = just_filename + "("+std::to_string(version)+")";
|
||||
}
|
||||
m_filename = final_filename + extension;
|
||||
|
||||
boost::filesystem::path tmp_path = m_dest_folder / (m_filename + "." + std::to_string(get_current_pid()) + ".download");
|
||||
dest_path = m_dest_folder / m_filename;
|
||||
try
|
||||
{
|
||||
boost::nowide::fstream file(tmp_path.string(), std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
file.write(body.c_str(), body.size());
|
||||
file.close();
|
||||
boost::filesystem::rename(tmp_path, dest_path);
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
//TODO: report?
|
||||
//error_message = GUI::format("Failed to write and move %1% to %2%", tmp_path, dest_path);
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_FILE_ERROR);
|
||||
evt->SetString("Failed to write and move.");
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
return;
|
||||
}
|
||||
|
||||
wxCommandEvent* evt = new wxCommandEvent(EVT_FILE_COMPLETE);
|
||||
evt->SetString(dest_path.string());
|
||||
evt->SetInt(m_id);
|
||||
m_evt_handler->QueueEvent(evt);
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
}
|
||||
|
||||
FileGet::FileGet(int ID, std::string url, wxEvtHandler* evt_handler, const boost::filesystem::path& dest_folder)
|
||||
: p(new priv(ID, std::move(url), evt_handler, dest_folder))
|
||||
, m_ID(ID)
|
||||
{}
|
||||
|
||||
FileGet::FileGet(FileGet&& other) : p(std::move(other.p)), m_ID(other.get_ID()) {}
|
||||
|
||||
FileGet::~FileGet()
|
||||
{
|
||||
if (p && p->m_io_thread.joinable()) {
|
||||
p->m_io_thread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<FileGet> FileGet::get()
|
||||
{
|
||||
auto self = std::make_shared<FileGet>(std::move(*this));
|
||||
|
||||
if (self->p) {
|
||||
auto io_thread = std::thread([self]() {
|
||||
self->p->get_perform();
|
||||
});
|
||||
self->p->m_io_thread = std::move(io_thread);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
}
|
32
src/downloader/FileGet.hpp
Normal file
32
src/downloader/FileGet.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef slic3r_FileGet_hpp_
|
||||
#define slic3r_FileGet_hpp_
|
||||
|
||||
#include "FromSlicer/Http.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <wx/event.h>
|
||||
#include <wx/frame.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace Downloader {
|
||||
class FileGet : public std::enable_shared_from_this<FileGet> {
|
||||
private:
|
||||
struct priv;
|
||||
public:
|
||||
FileGet(int ID, std::string url, wxEvtHandler* evt_handler,const boost::filesystem::path& dest_folder);
|
||||
FileGet(FileGet&& other);
|
||||
~FileGet();
|
||||
|
||||
std::shared_ptr<FileGet> get();
|
||||
const int get_ID() const { return m_ID; }
|
||||
private:
|
||||
std::unique_ptr<priv> p;
|
||||
const int m_ID;
|
||||
};
|
||||
wxDECLARE_EVENT(EVT_FILE_COMPLETE, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_FILE_PROGRESS, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_FILE_ERROR, wxCommandEvent);
|
||||
|
||||
}
|
||||
#endif
|
668
src/downloader/FromSlicer/Http.cpp
Normal file
668
src/downloader/FromSlicer/Http.cpp
Normal file
@ -0,0 +1,668 @@
|
||||
#include "Http.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <deque>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#ifdef OPENSSL_CERT_OVERRIDE
|
||||
#include <openssl/x509.h>
|
||||
#endif
|
||||
|
||||
//#include <libslic3r/libslic3r.h>
|
||||
//#include <libslic3r/Utils.hpp>
|
||||
//#include <slic3r/GUI/I18N.hpp>
|
||||
//#include <slic3r/GUI/format.hpp>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
|
||||
namespace Downloader {
|
||||
|
||||
|
||||
// Private
|
||||
|
||||
struct CurlGlobalInit
|
||||
{
|
||||
static std::unique_ptr<CurlGlobalInit> instance;
|
||||
std::string message;
|
||||
|
||||
CurlGlobalInit()
|
||||
{
|
||||
#ifdef OPENSSL_CERT_OVERRIDE // defined if SLIC3R_STATIC=ON
|
||||
|
||||
// Look for a set of distro specific directories. Don't change the
|
||||
// order: https://bugzilla.redhat.com/show_bug.cgi?id=1053882
|
||||
static const char * CA_BUNDLES[] = {
|
||||
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
|
||||
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
||||
"/usr/share/ssl/certs/ca-bundle.crt",
|
||||
"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD
|
||||
"/etc/ssl/cert.pem",
|
||||
"/etc/ssl/ca-bundle.pem" // OpenSUSE Tumbleweed
|
||||
};
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
// Env var name for the OpenSSL CA bundle (SSL_CERT_FILE nomally)
|
||||
const char *const SSL_CA_FILE = X509_get_default_cert_file_env();
|
||||
const char * ssl_cafile = ::getenv(SSL_CA_FILE);
|
||||
|
||||
if (!ssl_cafile)
|
||||
ssl_cafile = X509_get_default_cert_file();
|
||||
|
||||
int replace = true;
|
||||
if (!ssl_cafile || !fs::exists(fs::path(ssl_cafile))) {
|
||||
const char * bundle = nullptr;
|
||||
for (const char * b : CA_BUNDLES) {
|
||||
if (fs::exists(fs::path(b))) {
|
||||
::setenv(SSL_CA_FILE, bundle = b, replace);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bundle)
|
||||
message = _u8L("Could not detect system SSL certificate store. "
|
||||
"PrusaSlicer will be unable to establish secure "
|
||||
"network connections.");
|
||||
else
|
||||
message = Slic3r::GUI::format(
|
||||
_L("PrusaSlicer detected system SSL certificate store in: %1%"),
|
||||
bundle);
|
||||
|
||||
message += "\n" + Slic3r::GUI::format(
|
||||
_L("To specify the system certificate store manually, please "
|
||||
"set the %1% environment variable to the correct CA bundle "
|
||||
"and restart the application."),
|
||||
SSL_CA_FILE);
|
||||
}
|
||||
|
||||
#endif // OPENSSL_CERT_OVERRIDE
|
||||
|
||||
if (CURLcode ec = ::curl_global_init(CURL_GLOBAL_DEFAULT)) {
|
||||
message += "CURL init has failed. PrusaSlicer will be unable to establish "
|
||||
"network connections. See logs for additional details.";
|
||||
|
||||
//BOOST_LOG_TRIVIAL(error) << ::curl_easy_strerror(ec);
|
||||
}
|
||||
}
|
||||
|
||||
~CurlGlobalInit() { ::curl_global_cleanup(); }
|
||||
};
|
||||
|
||||
std::unique_ptr<CurlGlobalInit> CurlGlobalInit::instance;
|
||||
|
||||
struct Http::priv
|
||||
{
|
||||
enum {
|
||||
DEFAULT_TIMEOUT_CONNECT = 10,
|
||||
DEFAULT_TIMEOUT_MAX = 0,
|
||||
DEFAULT_SIZE_LIMIT = 5 * 1024 * 1024,
|
||||
};
|
||||
|
||||
::CURL *curl;
|
||||
::curl_httppost *form;
|
||||
::curl_httppost *form_end;
|
||||
::curl_slist *headerlist;
|
||||
// Used for reading the body
|
||||
std::string buffer;
|
||||
// Used for storing file streams added as multipart form parts
|
||||
// Using a deque here because unlike vector it doesn't ivalidate pointers on insertion
|
||||
std::deque<fs::ifstream> form_files;
|
||||
std::string postfields;
|
||||
std::string error_buffer; // Used for CURLOPT_ERRORBUFFER
|
||||
size_t limit;
|
||||
bool cancel;
|
||||
std::unique_ptr<fs::ifstream> putFile;
|
||||
|
||||
std::thread io_thread;
|
||||
Http::CompleteFn completefn;
|
||||
Http::ErrorFn errorfn;
|
||||
Http::ProgressFn progressfn;
|
||||
Http::IPResolveFn ipresolvefn;
|
||||
|
||||
priv(const std::string &url);
|
||||
~priv();
|
||||
|
||||
static bool ca_file_supported(::CURL *curl);
|
||||
static size_t writecb(void *data, size_t size, size_t nmemb, void *userp);
|
||||
static int xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
|
||||
static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow);
|
||||
static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp);
|
||||
|
||||
void set_timeout_connect(long timeout);
|
||||
void set_timeout_max(long timeout);
|
||||
void form_add_file(const char *name, const fs::path &path, const char* filename);
|
||||
void set_post_body(const fs::path &path);
|
||||
void set_post_body(const std::string &body);
|
||||
void set_put_body(const fs::path &path);
|
||||
|
||||
std::string curl_error(CURLcode curlcode);
|
||||
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)
|
||||
, error_buffer(CURL_ERROR_SIZE + 1, '\0')
|
||||
, limit(0)
|
||||
, cancel(false)
|
||||
{
|
||||
Http::tls_global_init();
|
||||
|
||||
if (curl == nullptr) {
|
||||
throw std::exception();//Slic3r::RuntimeError(std::string("Could not construct Curl object"));
|
||||
}
|
||||
|
||||
set_timeout_connect(DEFAULT_TIMEOUT_CONNECT);
|
||||
set_timeout_max(DEFAULT_TIMEOUT_MAX);
|
||||
::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally
|
||||
::curl_easy_setopt(curl, CURLOPT_USERAGENT, "SLicer-Downloader");
|
||||
::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front());
|
||||
}
|
||||
|
||||
Http::priv::~priv()
|
||||
{
|
||||
::curl_easy_cleanup(curl);
|
||||
::curl_formfree(form);
|
||||
::curl_slist_free_all(headerlist);
|
||||
}
|
||||
|
||||
bool Http::priv::ca_file_supported(::CURL *curl)
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
bool res = false;
|
||||
#else
|
||||
bool res = true;
|
||||
#endif
|
||||
|
||||
if (curl == nullptr) { return res; }
|
||||
|
||||
#if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 48
|
||||
::curl_tlssessioninfo *tls;
|
||||
if (::curl_easy_getinfo(curl, CURLINFO_TLS_SSL_PTR, &tls) == CURLE_OK) {
|
||||
if (tls->backend == CURLSSLBACKEND_SCHANNEL || tls->backend == CURLSSLBACKEND_DARWINSSL) {
|
||||
// With Windows and OS X native SSL support, cert files cannot be set
|
||||
// DK: OSX is now not building CURL and links system one, thus we do not know which backend is installed. Still, false will be returned since the ifdef at the begining if this function.
|
||||
res = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int Http::priv::xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
|
||||
{
|
||||
auto self = static_cast<priv*>(userp);
|
||||
bool cb_cancel = false;
|
||||
|
||||
if (self->progressfn) {
|
||||
Progress progress(dltotal, dlnow, ultotal, ulnow);
|
||||
self->progressfn(progress, cb_cancel);
|
||||
}
|
||||
|
||||
if (cb_cancel) { self->cancel = true; }
|
||||
|
||||
return self->cancel;
|
||||
}
|
||||
|
||||
int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
{
|
||||
return xfercb(userp, dltotal, dlnow, ultotal, ulnow);
|
||||
}
|
||||
|
||||
size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp)
|
||||
{
|
||||
auto stream = reinterpret_cast<fs::ifstream*>(userp);
|
||||
|
||||
try {
|
||||
stream->read(buffer, size * nitems);
|
||||
} catch (const std::exception &) {
|
||||
return CURL_READFUNC_ABORT;
|
||||
}
|
||||
|
||||
return stream->gcount();
|
||||
}
|
||||
|
||||
void Http::priv::set_timeout_connect(long timeout)
|
||||
{
|
||||
::curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout);
|
||||
}
|
||||
|
||||
void Http::priv::set_timeout_max(long timeout)
|
||||
{
|
||||
::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
|
||||
}
|
||||
|
||||
void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename)
|
||||
{
|
||||
// We can't use CURLFORM_FILECONTENT, because curl doesn't support Unicode filenames on Windows
|
||||
// and so we use CURLFORM_STREAM with boost ifstream to read the file.
|
||||
|
||||
if (filename == nullptr) {
|
||||
filename = path.string().c_str();
|
||||
}
|
||||
|
||||
form_files.emplace_back(path, std::ios::in | std::ios::binary);
|
||||
auto &stream = form_files.back();
|
||||
stream.seekg(0, std::ios::end);
|
||||
size_t size = stream.tellg();
|
||||
stream.seekg(0);
|
||||
|
||||
if (filename != nullptr) {
|
||||
::curl_formadd(&form, &form_end,
|
||||
CURLFORM_COPYNAME, name,
|
||||
CURLFORM_FILENAME, filename,
|
||||
CURLFORM_CONTENTTYPE, "application/octet-stream",
|
||||
CURLFORM_STREAM, static_cast<void*>(&stream),
|
||||
CURLFORM_CONTENTSLENGTH, static_cast<long>(size),
|
||||
CURLFORM_END
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
//FIXME may throw! Is the caller aware of it?
|
||||
void Http::priv::set_post_body(const fs::path &path)
|
||||
{
|
||||
std::ifstream file(path.string());
|
||||
std::string file_content { std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() };
|
||||
postfields = std::move(file_content);
|
||||
}
|
||||
|
||||
void Http::priv::set_post_body(const std::string &body)
|
||||
{
|
||||
postfields = body;
|
||||
}
|
||||
|
||||
void Http::priv::set_put_body(const fs::path &path)
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
boost::uintmax_t filesize = file_size(path, ec);
|
||||
if (!ec) {
|
||||
putFile = std::make_unique<fs::ifstream>(path);
|
||||
::curl_easy_setopt(curl, CURLOPT_READDATA, (void *) (putFile.get()));
|
||||
::curl_easy_setopt(curl, CURLOPT_INFILESIZE, filesize);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Http::priv::curl_error(CURLcode curlcode)
|
||||
{
|
||||
return (boost::format("%1%:\n%2%\n[Error %3%]")
|
||||
% ::curl_easy_strerror(curlcode)
|
||||
% error_buffer.c_str()
|
||||
% curlcode
|
||||
).str();
|
||||
}
|
||||
|
||||
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_FOLLOWLOCATION, 1L);
|
||||
::curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
|
||||
::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writecb);
|
||||
::curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void*>(this));
|
||||
::curl_easy_setopt(curl, CURLOPT_READFUNCTION, form_file_read_cb);
|
||||
|
||||
::curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
|
||||
#if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32
|
||||
::curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xfercb);
|
||||
::curl_easy_setopt(curl, CURLOPT_XFERINFODATA, static_cast<void*>(this));
|
||||
#ifndef _WIN32
|
||||
(void)xfercb_legacy; // prevent unused function warning
|
||||
#endif
|
||||
#else
|
||||
::curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, xfercb);
|
||||
::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast<void*>(this));
|
||||
#endif
|
||||
|
||||
// ::curl_easy_setopt(curl, CURLOPT_VERBOSE, get_logging_level() >= 5);
|
||||
|
||||
if (headerlist != nullptr) {
|
||||
::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist);
|
||||
}
|
||||
|
||||
if (form != nullptr) {
|
||||
::curl_easy_setopt(curl, CURLOPT_HTTPPOST, form);
|
||||
}
|
||||
|
||||
if (!postfields.empty()) {
|
||||
::curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postfields.c_str());
|
||||
::curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE_LARGE, postfields.size());
|
||||
}
|
||||
|
||||
CURLcode res = ::curl_easy_perform(curl);
|
||||
|
||||
putFile.reset();
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
if (res == CURLE_ABORTED_BY_CALLBACK) {
|
||||
if (cancel) {
|
||||
// The abort comes from the request being cancelled programatically
|
||||
Progress dummyprogress(0, 0, 0, 0);
|
||||
bool cancel = true;
|
||||
if (progressfn) { progressfn(dummyprogress, cancel); }
|
||||
} else {
|
||||
// The abort comes from the CURLOPT_READFUNCTION callback, which means reading file failed
|
||||
if (errorfn) { errorfn(std::move(buffer), "Error reading file for file upload", 0); }
|
||||
}
|
||||
}
|
||||
else if (res == CURLE_WRITE_ERROR) {
|
||||
if (errorfn) { errorfn(std::move(buffer), body_size_error(), 0); }
|
||||
} else {
|
||||
if (errorfn) { errorfn(std::move(buffer), curl_error(res), 0); }
|
||||
};
|
||||
} else {
|
||||
long http_status = 0;
|
||||
::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status);
|
||||
|
||||
if (http_status >= 400) {
|
||||
if (errorfn) { errorfn(std::move(buffer), std::string(), http_status); }
|
||||
} else {
|
||||
if (completefn) { completefn(std::move(buffer), http_status); }
|
||||
if (ipresolvefn) {
|
||||
char* ct;
|
||||
res = curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP, &ct);
|
||||
if ((CURLE_OK == res) && ct) {
|
||||
ipresolvefn(ct);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Http::Http(const std::string &url) : p(new priv(url)) {}
|
||||
|
||||
|
||||
// Public
|
||||
|
||||
Http::Http(Http &&other) : p(std::move(other.p)) {}
|
||||
|
||||
Http::~Http()
|
||||
{
|
||||
assert(! p || ! p->putFile);
|
||||
if (p && p->io_thread.joinable()) {
|
||||
p->io_thread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Http& Http::timeout_connect(long timeout)
|
||||
{
|
||||
if (timeout < 1) { timeout = priv::DEFAULT_TIMEOUT_CONNECT; }
|
||||
if (p) { p->set_timeout_connect(timeout); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::timeout_max(long timeout)
|
||||
{
|
||||
if (timeout < 1) { timeout = priv::DEFAULT_TIMEOUT_MAX; }
|
||||
if (p) { p->set_timeout_max(timeout); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Authorization by HTTP digest, based on RFC2617.
|
||||
Http& Http::auth_digest(const std::string &user, const std::string &password)
|
||||
{
|
||||
curl_easy_setopt(p->curl, CURLOPT_USERNAME, user.c_str());
|
||||
curl_easy_setopt(p->curl, CURLOPT_PASSWORD, password.c_str());
|
||||
curl_easy_setopt(p->curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::auth_basic(const std::string &user, const std::string &password)
|
||||
{
|
||||
curl_easy_setopt(p->curl, CURLOPT_USERNAME, user.c_str());
|
||||
curl_easy_setopt(p->curl, CURLOPT_PASSWORD, password.c_str());
|
||||
curl_easy_setopt(p->curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::ca_file(const std::string &name)
|
||||
{
|
||||
if (p && priv::ca_file_supported(p->curl)) {
|
||||
::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 fs::path &path)
|
||||
{
|
||||
if (p) { p->form_add_file(name.c_str(), path.c_str(), nullptr); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::form_add_file(const std::string &name, const fs::path &path, const std::string &filename)
|
||||
{
|
||||
if (p) { p->form_add_file(name.c_str(), path.c_str(), filename.c_str()); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
// Tells libcurl to ignore certificate revocation checks in case of missing or offline distribution points for those SSL backends where such behavior is present.
|
||||
// This option is only supported for Schannel (the native Windows SSL library).
|
||||
Http& Http::ssl_revoke_best_effort(bool set)
|
||||
{
|
||||
if(p && set){
|
||||
::curl_easy_setopt(p->curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
#endif // WIN32
|
||||
|
||||
Http& Http::set_post_body(const fs::path &path)
|
||||
{
|
||||
if (p) { p->set_post_body(path);}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::set_post_body(const std::string &body)
|
||||
{
|
||||
if (p) { p->set_post_body(body); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::set_put_body(const fs::path &path)
|
||||
{
|
||||
if (p) { p->set_put_body(path);}
|
||||
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& Http::on_progress(ProgressFn fn)
|
||||
{
|
||||
if (p) { p->progressfn = std::move(fn); }
|
||||
return *this;
|
||||
}
|
||||
|
||||
Http& Http::on_ip_resolve(IPResolveFn fn)
|
||||
{
|
||||
if (p) { p->ipresolvefn = 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(); }
|
||||
}
|
||||
|
||||
void Http::cancel()
|
||||
{
|
||||
if (p) { p->cancel = true; }
|
||||
}
|
||||
|
||||
Http Http::get(std::string url)
|
||||
{
|
||||
return 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;
|
||||
}
|
||||
|
||||
Http Http::put(std::string url)
|
||||
{
|
||||
Http http{std::move(url)};
|
||||
curl_easy_setopt(http.p->curl, CURLOPT_UPLOAD, 1L);
|
||||
return http;
|
||||
}
|
||||
|
||||
bool Http::ca_file_supported()
|
||||
{
|
||||
::CURL *curl = ::curl_easy_init();
|
||||
bool res = priv::ca_file_supported(curl);
|
||||
if (curl != nullptr) { ::curl_easy_cleanup(curl); }
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string Http::tls_global_init()
|
||||
{
|
||||
if (!CurlGlobalInit::instance)
|
||||
CurlGlobalInit::instance = std::make_unique<CurlGlobalInit>();
|
||||
|
||||
return CurlGlobalInit::instance->message;
|
||||
}
|
||||
|
||||
std::string Http::tls_system_cert_store()
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
#ifdef OPENSSL_CERT_OVERRIDE
|
||||
ret = ::getenv(X509_get_default_cert_file_env());
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Http::url_encode(const std::string &str)
|
||||
{
|
||||
::CURL *curl = ::curl_easy_init();
|
||||
if (curl == nullptr) {
|
||||
return str;
|
||||
}
|
||||
char *ce = ::curl_easy_escape(curl, str.c_str(), str.length());
|
||||
std::string encoded = std::string(ce);
|
||||
|
||||
::curl_free(ce);
|
||||
::curl_easy_cleanup(curl);
|
||||
|
||||
return encoded;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream &os, const Http::Progress &progress)
|
||||
{
|
||||
os << "Http::Progress("
|
||||
<< "dltotal = " << progress.dltotal
|
||||
<< ", dlnow = " << progress.dlnow
|
||||
<< ", ultotal = " << progress.ultotal
|
||||
<< ", ulnow = " << progress.ulnow
|
||||
<< ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
|
||||
}
|
149
src/downloader/FromSlicer/Http.hpp
Normal file
149
src/downloader/FromSlicer/Http.hpp
Normal file
@ -0,0 +1,149 @@
|
||||
#ifndef slic3r_Http_hpp_
|
||||
#define slic3r_Http_hpp_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
|
||||
namespace Downloader {
|
||||
|
||||
|
||||
/// Represetns a Http request
|
||||
class Http : public std::enable_shared_from_this<Http> {
|
||||
private:
|
||||
struct priv;
|
||||
public:
|
||||
struct Progress
|
||||
{
|
||||
size_t dltotal; // Total bytes to download
|
||||
size_t dlnow; // Bytes downloaded so far
|
||||
size_t ultotal; // Total bytes to upload
|
||||
size_t ulnow; // Bytes uploaded so far
|
||||
|
||||
Progress(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) :
|
||||
dltotal(dltotal), dlnow(dlnow), ultotal(ultotal), ulnow(ulnow)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Http> Ptr;
|
||||
typedef std::function<void(std::string /* body */, unsigned /* http_status */)> CompleteFn;
|
||||
|
||||
// A HTTP request may fail at various stages of completeness (URL parsing, DNS lookup, TCP connection, ...).
|
||||
// If the HTTP request could not be made or failed before completion, the `error` arg contains a description
|
||||
// of the error and `http_status` is zero.
|
||||
// If the HTTP request was completed but the response HTTP code is >= 400, `error` is empty and `http_status` contains the response code.
|
||||
// In either case there may or may not be a body.
|
||||
typedef std::function<void(std::string /* body */, std::string /* error */, unsigned /* http_status */)> ErrorFn;
|
||||
|
||||
// See the Progress struct above.
|
||||
// Writing true to the `cancel` reference cancels the request in progress.
|
||||
typedef std::function<void(Progress, bool& /* cancel */)> ProgressFn;
|
||||
|
||||
typedef std::function<void(std::string/* address */)> IPResolveFn;
|
||||
|
||||
Http(Http &&other);
|
||||
|
||||
// Note: strings are expected to be UTF-8-encoded
|
||||
|
||||
// These are the primary constructors that create a HTTP object
|
||||
// for a GET and a POST request respectively.
|
||||
static Http get(std::string url);
|
||||
static Http post(std::string url);
|
||||
static Http put(std::string url);
|
||||
~Http();
|
||||
|
||||
Http(const Http &) = delete;
|
||||
Http& operator=(const Http &) = delete;
|
||||
Http& operator=(Http &&) = delete;
|
||||
|
||||
// Sets a maximum connection timeout in seconds
|
||||
Http& timeout_connect(long timeout);
|
||||
// Sets a maximum total request timeout in seconds
|
||||
Http& timeout_max(long timeout);
|
||||
// Sets a maximum size of the data that can be received.
|
||||
// A value of zero sets the default limit, which is is 5MB.
|
||||
Http& size_limit(size_t sizeLimit);
|
||||
// Sets a HTTP header field.
|
||||
Http& header(std::string name, const std::string &value);
|
||||
// Removes a header field.
|
||||
Http& remove_header(std::string name);
|
||||
// Authorization by HTTP digest, based on RFC2617.
|
||||
Http& auth_digest(const std::string &user, const std::string &password);
|
||||
// Basic HTTP authorization
|
||||
Http& auth_basic(const std::string &user, const std::string &password);
|
||||
// Sets a CA certificate file for usage with HTTPS. This is only supported on some backends,
|
||||
// specifically, this is supported with OpenSSL and NOT supported with Windows and OS X native certificate store.
|
||||
// See also ca_file_supported().
|
||||
Http& ca_file(const std::string &filename);
|
||||
// Add a HTTP multipart form field
|
||||
Http& form_add(const std::string &name, const std::string &contents);
|
||||
// Add a HTTP multipart form file data contents, `name` is the name of the part
|
||||
Http& form_add_file(const std::string &name, const boost::filesystem::path &path);
|
||||
// Same as above except also override the file's filename with a custom one
|
||||
Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename);
|
||||
|
||||
#ifdef WIN32
|
||||
// Tells libcurl to ignore certificate revocation checks in case of missing or offline distribution points for those SSL backends where such behavior is present.
|
||||
// This option is only supported for Schannel (the native Windows SSL library).
|
||||
Http& ssl_revoke_best_effort(bool set);
|
||||
#endif // WIN32
|
||||
|
||||
// Set the file contents as a POST request body.
|
||||
// The data is used verbatim, it is not additionally encoded in any way.
|
||||
// This can be used for hosts which do not support multipart requests.
|
||||
Http& set_post_body(const boost::filesystem::path &path);
|
||||
|
||||
// Set the POST request body.
|
||||
// The data is used verbatim, it is not additionally encoded in any way.
|
||||
// This can be used for hosts which do not support multipart requests.
|
||||
Http& set_post_body(const std::string &body);
|
||||
|
||||
// Set the file contents as a PUT request body.
|
||||
// The data is used verbatim, it is not additionally encoded in any way.
|
||||
// This can be used for hosts which do not support multipart requests.
|
||||
Http& set_put_body(const boost::filesystem::path &path);
|
||||
|
||||
// Callback called on HTTP request complete
|
||||
Http& on_complete(CompleteFn fn);
|
||||
// Callback called on an error occuring at any stage of the requests: Url parsing, DNS lookup,
|
||||
// TCP connection, HTTP transfer, and finally also when the response indicates an error (status >= 400).
|
||||
// Therefore, a response body may or may not be present.
|
||||
Http& on_error(ErrorFn fn);
|
||||
// Callback called on data download/upload prorgess (called fairly frequently).
|
||||
// See the `Progress` structure for description of the data passed.
|
||||
// Writing a true-ish value into the cancel reference parameter cancels the request.
|
||||
Http& on_progress(ProgressFn fn);
|
||||
// Callback called after succesful HTTP request (after on_complete callback)
|
||||
// Called if curl_easy_getinfo resolved just used IP address.
|
||||
Http& on_ip_resolve(IPResolveFn fn);
|
||||
|
||||
// Starts performing the request in a background thread
|
||||
Ptr perform();
|
||||
// Starts performing the request on the current thread
|
||||
void perform_sync();
|
||||
// Cancels a request in progress
|
||||
void cancel();
|
||||
|
||||
// Tells whether current backend supports seting up a CA file using ca_file()
|
||||
static bool ca_file_supported();
|
||||
|
||||
// Return empty string on success or error message on fail.
|
||||
static std::string tls_global_init();
|
||||
static std::string tls_system_cert_store();
|
||||
|
||||
// converts the given string to an url_encoded_string
|
||||
static std::string url_encode(const std::string &str);
|
||||
private:
|
||||
Http(const std::string &url);
|
||||
|
||||
std::unique_ptr<priv> p;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream &, const Http::Progress &);
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user