Replaced boost::asio with curlpp
This commit is contained in:
parent
0a81597e3d
commit
ba84233569
|
@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.7)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
project (mastodon-cpp
|
project (mastodon-cpp
|
||||||
VERSION 0.2.1
|
VERSION 0.2.10
|
||||||
LANGUAGES CXX
|
LANGUAGES CXX
|
||||||
)
|
)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||||
|
@ -24,6 +24,9 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
add_definitions(-DDEBUG=1)
|
add_definitions(-DDEBUG=1)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
include(FindCURL)
|
||||||
|
find_package(CURL REQUIRED)
|
||||||
|
|
||||||
# Library
|
# Library
|
||||||
file(GLOB sources src/*.cpp src/*.hpp)
|
file(GLOB sources src/*.cpp src/*.hpp)
|
||||||
add_library(mastodon-cpp SHARED ${sources})
|
add_library(mastodon-cpp SHARED ${sources})
|
||||||
|
@ -31,7 +34,7 @@ set_target_properties(mastodon-cpp PROPERTIES
|
||||||
VERSION ${PROJECT_VERSION}
|
VERSION ${PROJECT_VERSION}
|
||||||
SOVERSION ${mastodon-cpp_VERSION_MAJOR}
|
SOVERSION ${mastodon-cpp_VERSION_MAJOR}
|
||||||
)
|
)
|
||||||
target_link_libraries(mastodon-cpp boost_system ssl crypto)
|
target_link_libraries(mastodon-cpp ${CURL_LIBRARIES} ssl crypto curlpp)
|
||||||
install(TARGETS mastodon-cpp LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
install(TARGETS mastodon-cpp LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
install(FILES ${PROJECT_SOURCE_DIR}/src/mastodon-cpp.hpp
|
install(FILES ${PROJECT_SOURCE_DIR}/src/mastodon-cpp.hpp
|
||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||||
|
|
|
@ -17,201 +17,87 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <istream>
|
|
||||||
#include <ostream>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <boost/asio.hpp>
|
#include <curlpp/cURLpp.hpp>
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <curlpp/Easy.hpp>
|
||||||
|
#include <curlpp/Options.hpp>
|
||||||
|
#include <curlpp/Exception.hpp>
|
||||||
#include "macros.hpp"
|
#include "macros.hpp"
|
||||||
#include "mastodon-cpp.hpp"
|
#include "mastodon-cpp.hpp"
|
||||||
|
|
||||||
using namespace Mastodon;
|
using namespace Mastodon;
|
||||||
|
namespace curlopts = curlpp::options;
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::cerr;
|
using std::cerr;
|
||||||
|
|
||||||
using boost::asio::ip::tcp;
|
|
||||||
namespace ssl = boost::asio::ssl;
|
|
||||||
typedef ssl::stream<tcp::socket> ssl_socket;
|
|
||||||
|
|
||||||
API::http::http(const API &api, const string &instance,
|
API::http::http(const API &api, const string &instance,
|
||||||
const string &access_token)
|
const string &access_token)
|
||||||
: parent(api)
|
: parent(api)
|
||||||
, _instance(instance)
|
, _instance(instance)
|
||||||
, _access_token(access_token)
|
, _access_token(access_token)
|
||||||
, _ctx(ssl::context::tlsv12)
|
|
||||||
, _resolver(_io_service)
|
|
||||||
, _socket(_io_service, _ctx)
|
|
||||||
{
|
{
|
||||||
_ctx.set_options(ssl::context::tlsv12 | ssl::context::tlsv11 |
|
curlpp::initialize();
|
||||||
ssl::context::no_sslv3 | ssl::context::no_sslv2 |
|
|
||||||
ssl::context::no_tlsv1);
|
|
||||||
_ctx.set_default_verify_paths();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::uint16_t API::http::request_sync(const method &meth,
|
const std::uint16_t API::http::request_sync(const method &meth,
|
||||||
const string &path,
|
const string &path,
|
||||||
string &answer)
|
string &answer)
|
||||||
{
|
{
|
||||||
return request_sync(meth, path, "", answer);
|
return request_sync(meth, path, curlpp::Forms(), answer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::uint16_t API::http::request_sync(const method &meth,
|
const std::uint16_t API::http::request_sync(const method &meth,
|
||||||
const string &path,
|
const string &path,
|
||||||
const string &formdata,
|
const curlpp::Forms &formdata,
|
||||||
string &answer)
|
string &answer)
|
||||||
{
|
{
|
||||||
ttdebug << "Path is: " << path << '\n';
|
ttdebug << "Path is: " << path << '\n';
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tcp::resolver::query query(_instance, "https");
|
std::ostringstream oss;
|
||||||
tcp::resolver::iterator endpoint_iterator = _resolver.resolve(query);
|
curlpp::Easy request;
|
||||||
boost::asio::connect(_socket.lowest_layer(), endpoint_iterator);
|
request.setOpt<curlopts::Url>("https://" + _instance + path);
|
||||||
_socket.lowest_layer().set_option(tcp::no_delay(true));
|
request.setOpt<curlopts::UserAgent>(parent.get_useragent());
|
||||||
}
|
request.setOpt<curlopts::HttpHeader>(
|
||||||
catch (const std::exception &e)
|
{
|
||||||
{
|
"Connection: close",
|
||||||
ttdebug << "ERROR: " << e.what() << "\n";
|
"Authorization: Bearer " + _access_token
|
||||||
return 16;
|
});
|
||||||
}
|
if (!formdata.empty())
|
||||||
|
{
|
||||||
|
request.setOpt<curlopts::HttpPost>(formdata);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Server Name Indication (SNI)
|
|
||||||
SSL_set_tlsext_host_name(_socket.native_handle(), _instance.c_str());
|
|
||||||
|
|
||||||
_socket.set_verify_mode(ssl::verify_peer);
|
|
||||||
_socket.set_verify_callback(ssl::rfc2818_verification(_instance));
|
|
||||||
|
|
||||||
_socket.handshake(ssl_socket::client);
|
|
||||||
}
|
|
||||||
catch (const std::exception &e)
|
|
||||||
{
|
|
||||||
ttdebug << "ERROR: " << e.what() << "\n";
|
|
||||||
return 17;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
boost::asio::streambuf request;
|
|
||||||
std::ostream request_stream(&request);
|
|
||||||
switch (meth)
|
switch (meth)
|
||||||
{
|
{
|
||||||
case http::method::GET:
|
case http::method::GET:
|
||||||
request_stream << "GET";
|
|
||||||
ttdebug << "Method is GET\n";
|
|
||||||
break;
|
break;
|
||||||
case http::method::PATCH:
|
case http::method::PATCH:
|
||||||
request_stream << "PATCH";
|
request.setOpt<curlopts::CustomRequest>("PATCH");
|
||||||
ttdebug << "Method is PATCH\n";
|
|
||||||
break;
|
break;
|
||||||
case http::method::POST:
|
case http::method::POST:
|
||||||
request_stream << "POST";
|
request.setOpt<curlopts::CustomRequest>("POST");
|
||||||
ttdebug << "Method is POST\n";
|
|
||||||
break;
|
break;
|
||||||
case http::method::PUT:
|
case http::method::PUT:
|
||||||
request_stream << "PUT";
|
request.setOpt<curlopts::CustomRequest>("PUT");
|
||||||
ttdebug << "Method is PUT\n";
|
|
||||||
break;
|
|
||||||
case http::method::DELETE:
|
case http::method::DELETE:
|
||||||
request_stream << "DELETE";
|
request.setOpt<curlopts::CustomRequest>("DELETE");
|
||||||
ttdebug << "Method is DELETE\n";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ttdebug << "ERROR: Not implemented\n";
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
request_stream << " " << path;
|
|
||||||
request_stream << " HTTP/1.0\r\n";
|
|
||||||
request_stream << "Host: " << _instance << "\r\n";
|
|
||||||
request_stream << "Accept: */*\r\n";
|
|
||||||
request_stream << "Connection: close\r\n";
|
|
||||||
request_stream << "User-Agent: " << parent.get_useragent() << "\r\n";
|
|
||||||
if (!_access_token.empty())
|
|
||||||
{
|
|
||||||
request_stream << "Authorization: Bearer "
|
|
||||||
<< _access_token << "\r\n";
|
|
||||||
}
|
|
||||||
switch (meth)
|
|
||||||
{
|
|
||||||
case http::method::GET:
|
|
||||||
request_stream << "\r\n";
|
|
||||||
break;
|
|
||||||
case http::method::PATCH:
|
|
||||||
request_stream << formdata;
|
|
||||||
break;
|
|
||||||
case http::method::POST:
|
|
||||||
case http::method::PUT:
|
|
||||||
case http::method::DELETE:
|
|
||||||
if (formdata.empty())
|
|
||||||
{
|
|
||||||
request_stream << "\r\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
request_stream << formdata;
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
boost::asio::write(_socket, request);
|
|
||||||
|
oss << request;
|
||||||
boost::asio::streambuf response;
|
|
||||||
boost::asio::read_until(_socket, response, "\r\n");
|
|
||||||
|
|
||||||
// Check that response is OK.
|
|
||||||
std::istream response_stream(&response);
|
|
||||||
std::string http_version;
|
|
||||||
std::uint16_t status_code;
|
|
||||||
std::string status_message;
|
|
||||||
response_stream >> http_version;
|
|
||||||
response_stream >> status_code;
|
|
||||||
std::getline(response_stream, status_message);
|
|
||||||
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
|
|
||||||
{
|
|
||||||
ttdebug << "ERROR: Invalid response from server\n";
|
|
||||||
ttdebug << "Response was: " << http_version << " " << status_code
|
|
||||||
<< " " << status_message << '\n';
|
|
||||||
return 18;
|
|
||||||
}
|
|
||||||
if (status_code != 200)
|
|
||||||
{
|
|
||||||
ttdebug << "ERROR: Response returned with status code "
|
|
||||||
<< status_code << ": " << status_message << "\n";
|
|
||||||
return status_code;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read headers
|
|
||||||
boost::asio::read_until(_socket, response, "\r\n\r\n");
|
|
||||||
std::string header;
|
|
||||||
// ttdebug << "Header: \n";
|
|
||||||
while (std::getline(response_stream, header) && header != "\r")
|
|
||||||
{
|
|
||||||
// ttdebug << header << '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read body
|
|
||||||
boost::system::error_code error;
|
|
||||||
answer = "";
|
|
||||||
std::ostringstream oss;
|
|
||||||
while (boost::asio::read(_socket, response,
|
|
||||||
boost::asio::transfer_at_least(1), error))
|
|
||||||
{
|
|
||||||
oss << &response;
|
|
||||||
}
|
|
||||||
if (error != boost::asio::error::eof)
|
|
||||||
{
|
|
||||||
// TODO: Find out why the "short read" error occurs
|
|
||||||
// with PATCH and POST
|
|
||||||
//throw boost::system::system_error(error);
|
|
||||||
ttdebug << "ERROR: " << error.message() << '\n';
|
|
||||||
ttdebug << "The preceding error is ignored.\n";
|
|
||||||
}
|
|
||||||
answer = oss.str();
|
answer = oss.str();
|
||||||
ttdebug << "Answer from server: " << oss.str() << '\n';
|
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (curlpp::RuntimeError &e)
|
||||||
{
|
{
|
||||||
ttdebug << "Exception: " << e.what() << "\n";
|
cerr << "RUNTIME ERROR: " << e.what() << std::endl;
|
||||||
|
return 0xffff;
|
||||||
|
}
|
||||||
|
catch (curlpp::LogicError &e)
|
||||||
|
{
|
||||||
|
cerr << "LOGIC ERROR: " << e.what() << std::endl;
|
||||||
return 0xffff;
|
return 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <curlpp/cURLpp.hpp>
|
||||||
|
#include <curlpp/Easy.hpp>
|
||||||
#include "version.hpp"
|
#include "version.hpp"
|
||||||
#include "macros.hpp"
|
#include "macros.hpp"
|
||||||
#include "mastodon-cpp.hpp"
|
#include "mastodon-cpp.hpp"
|
||||||
|
@ -104,58 +107,34 @@ const string API::maptostr(const parametermap &map, const bool &firstparam)
|
||||||
ttdebug << "Constructed parameter string: " << result << '\n';
|
ttdebug << "Constructed parameter string: " << result << '\n';
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
const string API::maptoformdata(const parametermap &map)
|
|
||||||
|
const curlpp::Forms API::maptoformdata(const parametermap &map)
|
||||||
{
|
{
|
||||||
|
curlpp::Forms formdata;
|
||||||
|
|
||||||
if (map.size() == 0)
|
if (map.size() == 0)
|
||||||
{
|
{
|
||||||
return "";
|
return formdata;
|
||||||
}
|
}
|
||||||
|
|
||||||
const string boundary = "MEEP";
|
|
||||||
string header;
|
|
||||||
string body;
|
|
||||||
|
|
||||||
header = "Content-type: multipart/form-data, boundary=" + boundary + "\r\n";
|
|
||||||
header += "Content-Length: ";
|
|
||||||
body = "--" + boundary;
|
|
||||||
|
|
||||||
for (const auto &it : map)
|
for (const auto &it : map)
|
||||||
{
|
{
|
||||||
// This is directly after the last boundary
|
|
||||||
body += "\r\n";
|
|
||||||
if (it.second.size() == 1)
|
if (it.second.size() == 1)
|
||||||
{
|
{
|
||||||
if (it.first == "avatar" ||
|
formdata.push_back(new curlpp::FormParts::Content(it.first,
|
||||||
it.first == "header" ||
|
it.second.front()));
|
||||||
it.first == "file")
|
|
||||||
{
|
|
||||||
body += "Content-Transfer-Encoding: base64\r\n";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
body += "Content-Transfer-Encoding: 8bit\r\n";
|
|
||||||
}
|
|
||||||
body += ("Content-Disposition: form-data; name=\"" +
|
|
||||||
it.first + "\"\r\n\r\n");
|
|
||||||
body += (it.second.front() + "\r\n--" + boundary);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (const string &str : it.second)
|
for (const string &str : it.second)
|
||||||
{
|
{
|
||||||
body += ("Content-Disposition: form-data; name=\"" +
|
formdata.push_back(new curlpp::FormParts::Content(it.first + "[]",
|
||||||
it.first + "[]\"\r\n\r\n");
|
str));
|
||||||
body += (str + "\r\n--" + boundary);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The last segment has to have "--" after the boundary
|
|
||||||
body += "--\r\n";
|
|
||||||
|
|
||||||
header += (std::to_string(body.length()) + "\r\n\r\n");
|
return formdata;
|
||||||
|
|
||||||
ttdebug << "Form data: \n" << header << body;
|
|
||||||
return header + body;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// const string API::register_app(const std::string &instance,
|
// const string API::register_app(const std::string &instance,
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <boost/asio.hpp>
|
#include <curlpp/cURLpp.hpp>
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <curlpp/Easy.hpp>
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @example example1_dump_json.cpp
|
* @example example1_dump_json.cpp
|
||||||
|
@ -436,7 +436,7 @@ private:
|
||||||
*/
|
*/
|
||||||
const std::string maptostr(const parametermap &map,
|
const std::string maptostr(const parametermap &map,
|
||||||
const bool &firstparam = true);
|
const bool &firstparam = true);
|
||||||
const std::string maptoformdata(const parametermap &map);
|
const curlpp::Forms maptoformdata(const parametermap &map);
|
||||||
|
|
||||||
class http
|
class http
|
||||||
{
|
{
|
||||||
|
@ -468,17 +468,13 @@ private:
|
||||||
*/
|
*/
|
||||||
const std::uint16_t request_sync(const method &meth,
|
const std::uint16_t request_sync(const method &meth,
|
||||||
const std::string &path,
|
const std::string &path,
|
||||||
const std::string &formdata,
|
const curlpp::Forms &formdata,
|
||||||
std::string &answer);
|
std::string &answer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const API &parent;
|
const API &parent;
|
||||||
const std::string _instance;
|
const std::string _instance;
|
||||||
const std::string _access_token;
|
const std::string _access_token;
|
||||||
boost::asio::ssl::context _ctx;
|
|
||||||
boost::asio::io_service _io_service;
|
|
||||||
boost::asio::ip::tcp::resolver _resolver;
|
|
||||||
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> _socket;
|
|
||||||
} _http;
|
} _http;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Reference in New Issue