рукоблужу тут некоторого робота для REST-API, и в качестве экзампла по использованию boost.beast взял этот:
//
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/beast
//
//------------------------------------------------------------------------------
//
// Example: HTTP SSL client, asynchronous
//
//------------------------------------------------------------------------------#include"example/common/root_certificates.hpp"#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 <boost/asio/ssl/error.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>namespace http = boost::beast::http; // from <boost/beast/http.hpp>
//------------------------------------------------------------------------------
// Report a failurevoid
fail(boost::system::error_code ec, char const* what)
{
std::cerr << what << ": " << ec.message() << "\n";
}
// Performs an HTTP GET and prints the responseclass session : public std::enable_shared_from_this<session>
{
tcp::resolver resolver_;
ssl::stream<tcp::socket> stream_;
boost::beast::flat_buffer buffer_; // (Must persist between reads)
http::request<http::empty_body> req_;
http::response<http::string_body> res_;
public:
// Resolver and stream require an io_contextexplicit
session(boost::asio::io_context& ioc, ssl::context& ctx)
: resolver_(ioc)
, stream_(ioc, ctx)
{
}
// Start the asynchronous operationvoid
run(
char const* host,
char const* port,
char const* target,
int version)
{
// Set SNI Hostname (many hosts need this to handshake successfully)if(! SSL_set_tlsext_host_name(stream_.native_handle(), host))
{
boost::system::error_code ec{static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
std::cerr << ec.message() << "\n";
return;
}
// Set up an HTTP GET request message
req_.version(version);
req_.method(http::verb::get);
req_.target(target);
req_.set(http::field::host, host);
req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
// Look up the domain name
resolver_.async_resolve(
host,
port,
std::bind(
&session::on_resolve,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2));
}
void
on_resolve(
boost::system::error_code ec,
tcp::resolver::results_type results)
{
if(ec)
return fail(ec, "resolve");
// Make the connection on the IP address we get from a lookup
boost::asio::async_connect(
stream_.next_layer(),
results.begin(),
results.end(),
std::bind(
&session::on_connect,
shared_from_this(),
std::placeholders::_1));
}
void
on_connect(boost::system::error_code ec)
{
if(ec)
return fail(ec, "connect");
// Perform the SSL handshake
stream_.async_handshake(
ssl::stream_base::client,
std::bind(
&session::on_handshake,
shared_from_this(),
std::placeholders::_1));
}
void
on_handshake(boost::system::error_code ec)
{
if(ec)
return fail(ec, "handshake");
// Send the HTTP request to the remote host
http::async_write(stream_, req_,
std::bind(
&session::on_write,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2));
}
void
on_write(
boost::system::error_code ec,
std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if(ec)
return fail(ec, "write");
// Receive the HTTP response
http::async_read(stream_, buffer_, res_,
std::bind(
&session::on_read,
shared_from_this(),
std::placeholders::_1,
std::placeholders::_2));
}
void
on_read(
boost::system::error_code ec,
std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if(ec)
return fail(ec, "read");
// Write the message to standard out
std::cout << res_ << std::endl;
// Gracefully close the stream
stream_.async_shutdown(
std::bind(
&session::on_shutdown,
shared_from_this(),
std::placeholders::_1));
}
void
on_shutdown(boost::system::error_code ec)
{
if(ec == boost::asio::error::eof)
{
// Rationale:
// http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
ec.assign(0, ec.category());
}
if(ec)
return fail(ec, "shutdown");
// If we get here then the connection is closed gracefully
}
};
//------------------------------------------------------------------------------int main(int argc, char** argv)
{
// Check command line arguments.if(argc != 4 && argc != 5)
{
std::cerr <<
"Usage: http-client-async-ssl <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" <<
"Example:\n" <<
" http-client-async-ssl www.example.com 443 /\n" <<
" http-client-async-ssl www.example.com 443 / 1.0\n";
return EXIT_FAILURE;
}
auto const host = argv[1];
auto const port = argv[2];
auto const target = argv[3];
int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11;
// The io_context is required for all I/O
boost::asio::io_context ioc;
// The SSL context is required, and holds certificates
ssl::context ctx{ssl::context::sslv23_client};
// This holds the root certificate used for verification
load_root_certificates(ctx);
// Launch the asynchronous operation
std::make_shared<session>(ioc, ctx)->run(host, port, target, version);
// Run the I/O service. The call will return when
// the get operation is complete.
ioc.run();
return EXIT_SUCCESS;
}
точка входа тут метод session::run().
вопрос мой заключается в том, должен ли я для кождого запроса выполнять resolve+connect+handshake, или же это достаточно сделать один раз и далее просто звать write+read?
т.е. я к примеру один раз создаю объект коннекшна, в конструкторе выполняю resolve+connect+handshake, и дальше просто зову write+read?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>вопрос мой заключается в том, должен ли я для кождого запроса выполнять resolve+connect+handshake, или же это достаточно сделать один раз и далее просто звать write+read?
Я прошу прощения, это, конечно, очень познавательно, написать HTTP-клиента пользуясь примитивами уровнем чуть выше сокетов, а что, для C++ нету готового HTTP-клиента, который все эти детали реализации прячет у себя под капотом?
Вот в частности, есть такая штука, как connection keep-alive (это когда мы не закрываем сокет, получив ответ, а используем его, при возможности, для следующего запроса). Надо следить за соединениями, которые вроде бы живые, но только уже полчаса, как туда ничего не уходит или оттуда ничего не приходит. Бывает компрессия данных, пересылаемых через HTTP, она неплохо жмет JSON. Бывает, что на запрос ответят redirect'ом, и неплохо бы сходить с этим запросом в другое место (или не ходить, если к другому месту доверия нет). У TLS/SSL бывает рестарт сессии, когда можно избежать при повторном установлении соединения дорогостоящего RSA/DHE, и сразу перейти к делу.
В обшем, много чего бывает. Ты все это собираешься своими руками написать?
X>т.е. я к примеру один раз создаю объект коннекшна, в конструкторе выполняю resolve+connect+handshake, и дальше просто зову write+read?
Соединения иногда рвутся, и их надо пересоединять. А так, пока TCP-соединение живо, то при повторных запросах в DNS лезть нет особого смысла. Все равно, ответ не пригодится.
R>что означает — каждый запрос ?
клиент.
создал я объект коннекшна, resolve+connect+handshake. write+read я далее могу звать многократно, или перед каждым write+read я должен делать это все заново?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, reversecode, Вы писали:
R>то можно звать многократно пока не закроется канал
мой вопрос был не про код, а вообще про http/https.
нужно ли перед каждым http-GET запросом выполнять resolve+connect+handshake? или достаточно это сделать один раз и далее можно многократно write+read?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
если каждый https GET на разный хост, то да, нужно делать
если каждый https GET делается на один хост, то завит от проектирования софта
спецификация не запрещает повторно делать хендснейк,
все зависит от всяких http keep-alive
но если все write это https GET, то возможно я бы делал каждый раз handsnake
зы у меня свое образная аллергия на keep-alive, после реалтайм доставок
Здравствуйте, Pzz, Вы писали:
Pzz>Я прошу прощения, это, конечно, очень познавательно, написать HTTP-клиента пользуясь примитивами уровнем чуть выше сокетов, а что, для C++ нету готового HTTP-клиента, который все эти детали реализации прячет у себя под капотом?
Это же секта свидетелей сферического коня в вакууме С++ сообщество — все должно быть гибко, настраиваемо, а если на каком-то утюге это не работает, значит это вообще не надо. Через это сил хватает только на изучение набора запчастей для велосипеда, на решение практической задачи сил не остается. А еще С++ программист уверен, что ему ни к чему 10-20 лет чужого опыта в виде готовой либы — он сам умный, сейчас сядет и запилит еще один мега-фреймворк лишенный фатального недостатка
MTD>Это же секта свидетелей сферического коня в вакууме С++ сообщество — все должно быть гибко, настраиваемо, а если на каком-то утюге это не работает, значит это вообще не надо.
C++ — полезная штука. На нем люди учатся писать хорошие компиляторы для плохих языков.
Здравствуйте, Pzz, Вы писали:
Pzz>а что, для C++ нету готового HTTP-клиента, который все эти детали реализации прячет у себя под капотом?
Например, в POCO была простая и довольно удобная реализация HTTP-клиента.
Но вообще в C++ замечательно работает libcurl, особенно если ее предварительно обернуть удобным C++ным интерфейсом.
Здравствуйте, so5team, Вы писали:
S>Например, в POCO была простая и довольно удобная реализация HTTP-клиента. S>Но вообще в C++ замечательно работает libcurl, особенно если ее предварительно обернуть удобным C++ным интерфейсом.
Про CURL я, разумеется знаю. Но удивляюсь, что его до сих пор к бусту не привинтили. Ну или свое не наклепали.
Здравствуйте, Pzz, Вы писали:
Pzz>Но удивляюсь, что его до сих пор к бусту не привинтили. Ну или свое не наклепали.
К Boost-у прикрутили Beast. Но не столько для того, чтобы использовать его там, где прикладным разработчикам нужно работать с HTTP, сколько для того, чтобы Beast стал базой, на которой кто-нибудь построит удобные в использовании инструменты. Вроде как сам автор Beast-а что-то такое делает, но до публичного релиза эта надстройка над Beast-ом пока еще не доросла.
Да и какой смысл клепать свое, чисто плюсовое, когда libcurl без проблем используется из плюсов?
Здравствуйте, so5team, Вы писали:
S>Да и какой смысл клепать свое, чисто плюсовое, когда libcurl без проблем используется из плюсов?
Без проблем, если это не хелловорлд, а асинхронная работа с запросами и поддержка https, то будет порядка 1000 строк и пару сторонних библиотек, например openssl и libevent — пара недель работы разработчика в офисе. Без проблем
Здравствуйте, MTD, Вы писали:
S>>Да и какой смысл клепать свое, чисто плюсовое, когда libcurl без проблем используется из плюсов?
MTD>Без проблем, если это не хелловорлд, а асинхронная работа с запросами и поддержка https, то будет порядка 1000 строк и пару сторонних библиотек, например openssl и libevent — пара недель работы разработчика в офисе. Без проблем
Пара дней, ты хотел сказать?
С опенэсэсэлем оно само дружит, а что до либевента, курлю он, как таковой, не нужен, но если у тебя и так подразумевается какой-то event loop, то курль относительно несложно в него встроить, и если твой собственный цикл делается вокруг либевента, то и курль можно туда припаять.
Здравствуйте, Pzz, Вы писали:
Pzz>Пара дней, ты хотел сказать?
Пара дней — это если уже писал и четко знаешь где брать и куда класть. Вот я для аськи писал, что и как делать хорошо представляю. Ну и оценки программистов, такие оценки. Работал в одной конторе, там все такие бодрые были — оценки на задачу давали от 15 минут
Здравствуйте, MTD, Вы писали:
Pzz>>Пара дней, ты хотел сказать?
MTD>Пара дней — это если уже писал и четко знаешь где брать и куда класть. Вот я для аськи писал, что и как делать хорошо представляю. Ну и оценки программистов, такие оценки. Работал в одной конторе, там все такие бодрые были — оценки на задачу давали от 15 минут
Ну, я с курлем возился, уровень сложности представляю. Ничего особо волшебного в нем нет, но документация порою невнятная.
P.S. При наличии Go надо иметь ну очень серьезные причины, чтобы делать такие штучки на Си, что с курлем, что без.
Здравствуйте, Pzz, Вы писали:
Pzz>Ну, я с курлем возился, уровень сложности представляю. Ничего особо волшебного в нем нет, но документация порою невнятная.
Так и есть, но везде есть нюанс. Пока только соберешь libopenssl (а это уже само по себе непросто, так как надо четко понимать какие флаги включать, а он еще и зависимости тянет) уже день легко пройдет. А еще собирать libcurl, zlib, libevent, да под разные платформы (это я все про продакшн говорю, а не про на скачал бинарники из интернета непонятно как и кем собранные или поставил из репозитория на Линуксе — у меня все работает). Плюсы — не Ява, Мавена нет.
Pzz>P.S. При наличии Go надо иметь ну очень серьезные причины, чтобы делать такие штучки на Си, что с курлем, что без.
Тут вообще возражений нет. Что-то для работы с сетью пилить сейчас на плюсах — это очень странно.
Здравствуйте, so5team, Вы писали:
S>Но вообще в C++ замечательно работает libcurl, особенно если ее предварительно обернуть удобным C++ным интерфейсом.
Здравствуйте, wl., Вы писали:
S>>Но вообще в C++ замечательно работает libcurl, особенно если ее предварительно обернуть удобным C++ным интерфейсом.
wl.>https://github.com/whoshuu/cpr
Да ну на фиг такие обертки. В файле cpr/session.cpp, такое ощущение, нет ни одной проверки кода возврата вызываемых curl_* функций.
Здравствуйте, so5team, Вы писали:
S>Здравствуйте, wl., Вы писали:
S>>>Но вообще в C++ замечательно работает libcurl, особенно если ее предварительно обернуть удобным C++ным интерфейсом.
wl.>>https://github.com/whoshuu/cpr
S>Да ну на фиг такие обертки. В файле cpr/session.cpp, такое ощущение, нет ни одной проверки кода возврата вызываемых curl_* функций.
Подскажите для дилетанта что такое GO, POCO, libcurl?
у меня программа на СИ (без плюсов), писал ее в VC6, теперь перешел на VisualStudio 2008
все на уровне сокетов, все вручную. протокол http 1.0
1.1 не освоил пока, в основном из-за keep alive)
zlib подключал. много нюансов конечно, серверы иногда такое гонят )) а обрабатывать надо
так вот, основной вопрос в том что хочу добавить поддержку для HTTPS и HTTP1.1
кодить все это вручную с самого нуля не очень хочется конечно. да и без библиотек типа ssl все равно не обойтись
что посоветуете? делаю для себя, поэтому время и объем кода не критичны. Приложению нужно активно взаимодействовать с сайтами по
http/https. что то вроде менеджера закачек и поисковика
заранее спасибо! по образованию не программист, просто дилетант-самоучка )) но программы писал, в том числе эта активно продавалась в свое время. как то так. из языков знаю собственно С и ассемблер для микроконтроллеров ) ну еще Фокал для БК ) поэтому не судите строго. спасибо!
да.. только не совсем понял, у нее поддержка SSL SSH встроена или нужно дополнительно компилить библиотеки?
уже установил, в curl_version_info пишет что версия 7.46.0
libz 1.2.8
OpenSSL/1.0.2e
libssh2/1.6.1_DEV
биты поддержки SSL установлены. значит дополнительно библиотеки не нужны?