I have written a RESTClient using boost::beast. This is a refacotred version of the example given in the boost::beast website.
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
namespace http = boost::beast::http;
namespace asio = boost::asio;
namespace beast = boost::beast;
using tcp = boost::asio::ip::tcp;
using HttpRequest = http::request<http::string_body>;
using HttpResponse = http::response<http::string_body>;
using ResponseCallback = std::function<void(bool success, const std::string& message)>;
class RestClient : public std::enable_shared_from_this<RestClient>
{
public:
explicit RestClient(asio::io_context& ioc)
: m_resolver(ioc)
, m_socket(ioc)
{
}
void doHttpGet(const ResponseCallback &callback, std::string &host, std::string& port, std::string& target, int version)
{
CreateGetRequest(host, target, version);
std::cout << "calling async_resolve:" << std::endl;
m_resolver.async_resolve(host, port, [this, callback](beast::error_code ec, const tcp::resolver::results_type& results) {
this->onResolve(callback, ec, results);
});
}
void CreateGetRequest(std::string &host, std::string &target, int version)
{
m_httpRequest.version(version);
m_httpRequest.method(http::verb::get);
m_httpRequest.target(target);
m_httpRequest.set(http::field::host, host);
m_httpRequest.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
}
void
onResolve(const ResponseCallback& callback, boost::system::error_code ec, const tcp::resolver::results_type& results)
{
if(ec) {
callback(false, ec.message());
}
std::cout << "calling async_connect:" << std::endl;
auto self = shared_from_this();
asio::async_connect(m_socket, results, [callback, self](boost::system::error_code ec){
self->onConnect(callback, ec);
});
}
void onConnect(const ResponseCallback& callback, boost::system::error_code ec)
{
if(ec) {
callback(false, ec.message());
return;
}
std::cout << "calling async_write:" << std::endl;
auto self = shared_from_this();
http::async_write(m_socket, m_httpRequest, [callback, self](boost::system::error_code ec, std::size_t bytes_transferred){
self->onWrite(callback, ec, bytes_transferred);
});
}
void
onWrite(const ResponseCallback& callback, boost::system::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if(ec) {
callback(false, ec.message());
return;
}
std::cout << "calling async_read:" << std::endl;
auto self = shared_from_this();
http::async_read(m_socket, m_buffer, m_httpResponse, [callback, self](boost::system::error_code ec, std::size_t bytes_transferred){
self->onRead(callback, ec, bytes_transferred);
});
}
void
onRead(const ResponseCallback& callback, boost::system::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if(ec) {
callback(false, ec.message());
return;
}
std::cout << m_httpResponse << std::endl;
std::cout << "calling shutdown:" << std::endl;
m_socket.shutdown(tcp::socket::shutdown_both, ec);
if(ec && ec != boost::system::errc::not_connected) {
callback(false, ec.message());
return;
}
if(m_httpResponse.result() != http::status::ok){
callback(false, std::string(m_httpResponse.reason()));
return;
}else
{
callback(true, m_httpResponse.body());
}
}
private:
tcp::resolver m_resolver;
tcp::socket m_socket;
beast::flat_buffer m_buffer; // (Must persist between reads)
HttpRequest m_httpRequest;
HttpResponse m_httpResponse;
};
int main(int argc, char** argv)
{
bool callbackSuccess = false;
std::string callbackMessage;
ResponseCallback responseCb = [&callbackSuccess, &callbackMessage](bool success, const std::string& message) {
callbackSuccess = success;
callbackMessage = message;
};
// Check command line arguments.
if(argc != 4 && argc != 5)
{
std::cerr <<
"Usage: http-client-async <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
"Example:\n" <<
" http-client-async www.example.com 80 /\n" <<
" http-client-async www.example.com 80 / 1.0\n";
return EXIT_FAILURE;
}
std::string host = argv[1];
std::string port = argv[2];
std::string target = argv[3];
int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11;
// The io_context is required for all I/O
asio::io_context ioc;
// Launch the asynchronous operation
std::make_shared<RestClient>(ioc)->doHttpGet(responseCb, host, port, target, version);
// Run the I/O service. The call will return when
// the get operation is complete.
ioc.run();
std::cout << "CallBack Message : " << callbackMessage << std::endl;
return EXIT_SUCCESS;
}
When I compile the above program, I get the following error.
In file included from /usr/include/boost/asio/impl/execution_context.hpp:18,
from /usr/include/boost/asio/execution_context.hpp:408,
from /usr/include/boost/asio/detail/scheduler.hpp:21,
from /usr/include/boost/asio/system_context.hpp:19,
from /usr/include/boost/asio/impl/system_executor.hpp:22,
from /usr/include/boost/asio/system_executor.hpp:129,
from /usr/include/boost/asio/associated_executor.hpp:21,
from /usr/include/boost/beast/core/detail/bind_handler.hpp:15,
from /usr/include/boost/beast/core/bind_handler.hpp:15,
from /usr/include/boost/beast/core.hpp:15,
from /cygdrive/c/Users/karthik/Documents/Workspace/cpluspluspen/boostbeast/RESTClient/RestClient.cpp:1:
/usr/include/boost/asio/impl/connect.hpp: In instantiation of 'typename boost::asio::async_result<typename std::decay<WriteHandler>::type, void(boost::system::error_code, typename Protocol::endpoint)>::return_type boost::asio::async_connect(boost::asio::basic_socket<Protocol>&, const EndpointSequence&, RangeConnectHandler&&, typename std::enable_if<boost::asio::is_endpoint_sequence<EndpointSequence>::value>::type*) [with Protocol = boost::asio::ip::tcp; EndpointSequence = boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp>; RangeConnectHandler = RestClient::onResolve(const ResponseCallback&, boost::system::error_code, const results_type&)::<lambda(boost::system::error_code)>; typename boost::asio::async_result<typename std::decay<WriteHandler>::type, void(boost::system::error_code, typename Protocol::endpoint)>::return_type = void; typename Protocol::endpoint = boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>; typename std::decay<WriteHandler>::type = std::decay<RestClient::onResolve(const ResponseCallback&, boost::system::error_code, const results_type&)::<lambda(boost::system::error_code)> >::type; typename std::enable_if<boost::asio::is_endpoint_sequence<EndpointSequence>::value>::type = void]':
/cygdrive/c/Users/karthik/Documents/Workspace/cpluspluspen/boostbeast/RESTClient/RestClient.cpp:58:28: required from here
/usr/include/boost/asio/impl/connect.hpp:707:3: error: static assertion failed: RangeConnectHandler type requirements not met
707 | BOOST_ASIO_RANGE_CONNECT_HANDLER_CHECK(
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/asio/impl/connect.hpp:707:3: note: '(sizeof (boost::asio::detail::two_arg_handler_test<RestClient::onResolve(const ResponseCallback&, boost::system::error_code, const results_type&)::<lambda(boost::system::error_code)> >(boost::asio::detail::rvref() [with T = RestClient::onResolve(const ResponseCallback&, boost::system::error_code, const results_type&)::<lambda(boost::system::error_code)>](), 0, 0)) == 1)' evaluates to false
/usr/include/boost/asio/impl/connect.hpp:707:3: error: no match for call to '(RestClient::onResolve(const ResponseCallback&, boost::system::error_code, const results_type&)::<lambda(boost::system::error_code)>) (const boost::system::error_code&, const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&)'
707 | BOOST_ASIO_RANGE_CONNECT_HANDLER_CHECK(
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/cygdrive/c/Users/karthik/Documents/Workspace/cpluspluspen/boostbeast/RESTClient/RestClient.cpp:58:48: note: candidate: 'RestClient::onResolve(const ResponseCallback&, boost::system::error_code, const results_type&)::<lambda(boost::system::error_code)>'
58 | asio::async_connect(m_socket, results, [callback, self](boost::system::error_code ec){
| ^
/cygdrive/c/Users/karthik/Documents/Workspace/cpluspluspen/boostbeast/RESTClient/RestClient.cpp:58:48: note: candidate expects 1 argument, 2 provided
In file included from /usr/include/boost/asio/detail/handler_type_requirements.hpp:53,
from /usr/include/boost/asio/impl/execution_context.hpp:18,
from /usr/include/boost/asio/execution_context.hpp:408,
from /usr/include/boost/asio/detail/scheduler.hpp:21,
from /usr/include/boost/asio/system_context.hpp:19,
from /usr/include/boost/asio/impl/system_executor.hpp:22,
from /usr/include/boost/asio/system_executor.hpp:129,
from /usr/include/boost/asio/associated_executor.hpp:21,
from /usr/include/boost/beast/core/detail/bind_handler.hpp:15,
from /usr/include/boost/beast/core/bind_handler.hpp:15,
from /usr/include/boost/beast/core.hpp:15,
from /cygdrive/c/Users/karthik/Documents/Workspace/cpluspluspen/boostbeast/RESTClient/RestClient.cpp:1:
/usr/include/boost/asio/async_result.hpp:156:12: error: 'boost::asio::async_completion<CompletionToken, Signature>::async_completion(CompletionToken&) [with CompletionToken = RestClient::onResolve(const ResponseCallback&, boost::system::error_code, const results_type&)::<lambda(boost::system::error_code)>; Signature = void(boost::system::error_code, boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>)]', declared using local type 'RestClient::onResolve(const ResponseCallback&, boost::system::error_code, const results_type&)::<lambda(boost::system::error_code)>', is used but never defined [-fpermissive]
156 | explicit async_completion(CompletionToken& token)
| ^~~~~~~~~~~~~~~~
In file included from /usr/include/boost/asio/connect.hpp:1059,
from /cygdrive/c/Users/karthik/Documents/Workspace/cpluspluspen/boostbeast/RESTClient/RestClient.cpp:4:
/usr/include/boost/asio/impl/connect.hpp:304:5: error: 'boost::asio::detail::range_connect_op<Protocol, EndpointSequence, ConnectCondition, RangeConnectHandler>::range_connect_op(boost::asio::basic_socket<Protocol>&, const EndpointSequence&, const ConnectCondition&, RangeConnectHandler&) [with Protocol = boost::asio::ip::tcp; EndpointSequence = boost::asio::ip::basic_resolver_results<boost::asio::ip::tcp>; ConnectCondition = boost::asio::detail::default_connect_condition; RangeConnectHandler = RestClient::onResolve(const ResponseCallback&, boost::system::error_code, const results_type&)::<lambda(boost::system::error_code)>]', declared using local type 'RestClient::onResolve(const ResponseCallback&, boost::system::error_code, const results_type&)::<lambda(boost::system::error_code)>', is used but never defined [-fpermissive]
304 | range_connect_op(basic_socket<Protocol BOOST_ASIO_SVC_TARG>& sock,
| ^~~~~~~~~~~~~~~~
make[3]: *** [boostbeast/RESTClient/CMakeFiles/rest_client.dir/build.make:82: boostbeast/RESTClient/CMakeFiles/rest_client.dir/RestClient.cpp.o] Error 1
make[2]: *** [CMakeFiles/Makefile2:2766: boostbeast/RESTClient/CMakeFiles/rest_client.dir/all] Error 2
make[1]: *** [CMakeFiles/Makefile2:2773: boostbeast/RESTClient/CMakeFiles/rest_client.dir/rule] Error 2
make: *** [Makefile:1000: rest_client] Error 2
I can't understand what exactly is wrong with the error message. Can someone please explain. I am using cygwin on Windows 10 and CLion.
Aucun commentaire:
Enregistrer un commentaire