https, connect, handshake
От: niXman Ниоткуда https://github.com/niXman
Дата: 28.01.18 10:19
Оценка:
привет!

рукоблужу тут некоторого робота для 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 failure
void
fail(boost::system::error_code ec, char const* what)
{
    std::cerr << what << ": " << ec.message() << "\n";
}

// Performs an HTTP GET and prints the response
class 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_context
    explicit
    session(boost::asio::io_context& ioc, ssl::context& ctx)
        : resolver_(ioc)
        , stream_(ioc, ctx)
    {
    }

    // Start the asynchronous operation
    void
    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 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: https, connect, handshake
От: reversecode google
Дата: 28.01.18 11:22
Оценка: +1
что означает — каждый запрос ?
резолв коннект хендснейк на каждую созданную сессию айпи+порт
дальше после создания канала, только рид/врайт

если со стороны сервера то сессия выполняется после каждого аксепта
Re: https, connect, handshake
От: Pzz Россия https://github.com/alexpevzner
Дата: 28.01.18 11:51
Оценка: +2
Здравствуйте, 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 лезть нет особого смысла. Все равно, ответ не пригодится.
Re[2]: https, connect, handshake
От: niXman Ниоткуда https://github.com/niXman
Дата: 28.01.18 14:23
Оценка:
Здравствуйте, reversecode, Вы писали:


R>что означает — каждый запрос ?

клиент.
создал я объект коннекшна, resolve+connect+handshake. write+read я далее могу звать многократно, или перед каждым write+read я должен делать это все заново?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: https, connect, handshake
От: reversecode google
Дата: 28.01.18 15:20
Оценка:
судя по коду
если убрать
        // Gracefully close the stream
        stream_.async_shutdown(

то можно звать многократно пока не закроется канал
Re[4]: https, connect, handshake
От: niXman Ниоткуда https://github.com/niXman
Дата: 28.01.18 15:28
Оценка:
Здравствуйте, reversecode, Вы писали:

R>то можно звать многократно пока не закроется канал

мой вопрос был не про код, а вообще про http/https.
нужно ли перед каждым http-GET запросом выполнять resolve+connect+handshake? или достаточно это сделать один раз и далее можно многократно write+read?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[5]: https, connect, handshake
От: reversecode google
Дата: 28.01.18 15:52
Оценка:
если каждый https GET на разный хост, то да, нужно делать
если каждый https GET делается на один хост, то завит от проектирования софта
спецификация не запрещает повторно делать хендснейк,
все зависит от всяких http keep-alive
но если все write это https GET, то возможно я бы делал каждый раз handsnake

зы у меня свое образная аллергия на keep-alive, после реалтайм доставок
Re[6]: https, connect, handshake
От: std.denis Россия  
Дата: 28.01.18 16:57
Оценка: :)
R>спецификация не запрещает повторно делать хендснейк,
R>но если все write это https GET, то возможно я бы делал каждый раз handsnake

извините, handshake, хэндшейк – рукопожатие
Re[2]: https, connect, handshake
От: MTD https://github.com/mtrempoltsev
Дата: 29.01.18 19:29
Оценка: +1 -1 :)
Здравствуйте, Pzz, Вы писали:

Pzz>Я прошу прощения, это, конечно, очень познавательно, написать HTTP-клиента пользуясь примитивами уровнем чуть выше сокетов, а что, для C++ нету готового HTTP-клиента, который все эти детали реализации прячет у себя под капотом?


Это же секта свидетелей сферического коня в вакууме С++ сообщество — все должно быть гибко, настраиваемо, а если на каком-то утюге это не работает, значит это вообще не надо. Через это сил хватает только на изучение набора запчастей для велосипеда, на решение практической задачи сил не остается. А еще С++ программист уверен, что ему ни к чему 10-20 лет чужого опыта в виде готовой либы — он сам умный, сейчас сядет и запилит еще один мега-фреймворк лишенный фатального недостатка
Re[3]: https, connect, handshake
От: Pzz Россия https://github.com/alexpevzner
Дата: 29.01.18 19:49
Оценка:
Здравствуйте, MTD, Вы писали:


MTD>Это же секта свидетелей сферического коня в вакууме С++ сообщество — все должно быть гибко, настраиваемо, а если на каком-то утюге это не работает, значит это вообще не надо.


C++ — полезная штука. На нем люди учатся писать хорошие компиляторы для плохих языков.
Re[2]: https, connect, handshake
От: so5team https://stiffstream.com
Дата: 30.01.18 08:27
Оценка: +1
Здравствуйте, Pzz, Вы писали:

Pzz>а что, для C++ нету готового HTTP-клиента, который все эти детали реализации прячет у себя под капотом?


Например, в POCO была простая и довольно удобная реализация HTTP-клиента.
Но вообще в C++ замечательно работает libcurl, особенно если ее предварительно обернуть удобным C++ным интерфейсом.
Re[3]: https, connect, handshake
От: Pzz Россия https://github.com/alexpevzner
Дата: 30.01.18 08:36
Оценка:
Здравствуйте, so5team, Вы писали:

S>Например, в POCO была простая и довольно удобная реализация HTTP-клиента.

S>Но вообще в C++ замечательно работает libcurl, особенно если ее предварительно обернуть удобным C++ным интерфейсом.

Про CURL я, разумеется знаю. Но удивляюсь, что его до сих пор к бусту не привинтили. Ну или свое не наклепали.
Re[4]: https, connect, handshake
От: so5team https://stiffstream.com
Дата: 30.01.18 08:46
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Но удивляюсь, что его до сих пор к бусту не привинтили. Ну или свое не наклепали.


К Boost-у прикрутили Beast. Но не столько для того, чтобы использовать его там, где прикладным разработчикам нужно работать с HTTP, сколько для того, чтобы Beast стал базой, на которой кто-нибудь построит удобные в использовании инструменты. Вроде как сам автор Beast-а что-то такое делает, но до публичного релиза эта надстройка над Beast-ом пока еще не доросла.

Да и какой смысл клепать свое, чисто плюсовое, когда libcurl без проблем используется из плюсов?
Re[5]: https, connect, handshake
От: MTD https://github.com/mtrempoltsev
Дата: 30.01.18 09:15
Оценка: -1 :)
Здравствуйте, so5team, Вы писали:

S>Да и какой смысл клепать свое, чисто плюсовое, когда libcurl без проблем используется из плюсов?


Без проблем, если это не хелловорлд, а асинхронная работа с запросами и поддержка https, то будет порядка 1000 строк и пару сторонних библиотек, например openssl и libevent — пара недель работы разработчика в офисе. Без проблем
Re[7]: https, connect, handshake
От: Ops Россия  
Дата: 03.02.18 17:05
Оценка: :)
Здравствуйте, std.denis, Вы писали:

SD>извините, handshake, хэндшейк – рукопожатие


Рукотрясение
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[6]: https, connect, handshake
От: Pzz Россия https://github.com/alexpevzner
Дата: 05.02.18 15:34
Оценка:
Здравствуйте, MTD, Вы писали:

S>>Да и какой смысл клепать свое, чисто плюсовое, когда libcurl без проблем используется из плюсов?


MTD>Без проблем, если это не хелловорлд, а асинхронная работа с запросами и поддержка https, то будет порядка 1000 строк и пару сторонних библиотек, например openssl и libevent — пара недель работы разработчика в офисе. Без проблем


Пара дней, ты хотел сказать?

С опенэсэсэлем оно само дружит, а что до либевента, курлю он, как таковой, не нужен, но если у тебя и так подразумевается какой-то event loop, то курль относительно несложно в него встроить, и если твой собственный цикл делается вокруг либевента, то и курль можно туда припаять.
Re[7]: https, connect, handshake
От: MTD https://github.com/mtrempoltsev
Дата: 05.02.18 17:29
Оценка:
Здравствуйте, Pzz, Вы писали:

Pzz>Пара дней, ты хотел сказать?


Пара дней — это если уже писал и четко знаешь где брать и куда класть. Вот я для аськи писал, что и как делать хорошо представляю. Ну и оценки программистов, такие оценки. Работал в одной конторе, там все такие бодрые были — оценки на задачу давали от 15 минут
Re[8]: https, connect, handshake
От: Pzz Россия https://github.com/alexpevzner
Дата: 05.02.18 19:13
Оценка:
Здравствуйте, MTD, Вы писали:

Pzz>>Пара дней, ты хотел сказать?


MTD>Пара дней — это если уже писал и четко знаешь где брать и куда класть. Вот я для аськи писал, что и как делать хорошо представляю. Ну и оценки программистов, такие оценки. Работал в одной конторе, там все такие бодрые были — оценки на задачу давали от 15 минут


Ну, я с курлем возился, уровень сложности представляю. Ничего особо волшебного в нем нет, но документация порою невнятная.

P.S. При наличии Go надо иметь ну очень серьезные причины, чтобы делать такие штучки на Си, что с курлем, что без.
Re[9]: https, connect, handshake
От: MTD https://github.com/mtrempoltsev
Дата: 05.02.18 19:48
Оценка: +1
Здравствуйте, Pzz, Вы писали:

Pzz>Ну, я с курлем возился, уровень сложности представляю. Ничего особо волшебного в нем нет, но документация порою невнятная.


Так и есть, но везде есть нюанс. Пока только соберешь libopenssl (а это уже само по себе непросто, так как надо четко понимать какие флаги включать, а он еще и зависимости тянет) уже день легко пройдет. А еще собирать libcurl, zlib, libevent, да под разные платформы (это я все про продакшн говорю, а не про на скачал бинарники из интернета непонятно как и кем собранные или поставил из репозитория на Линуксе — у меня все работает). Плюсы — не Ява, Мавена нет.

Pzz>P.S. При наличии Go надо иметь ну очень серьезные причины, чтобы делать такие штучки на Си, что с курлем, что без.


Тут вообще возражений нет. Что-то для работы с сетью пилить сейчас на плюсах — это очень странно.
Re[3]: https, connect, handshake
От: wl. Россия  
Дата: 22.02.18 06:03
Оценка:
Здравствуйте, so5team, Вы писали:

S>Но вообще в C++ замечательно работает libcurl, особенно если ее предварительно обернуть удобным C++ным интерфейсом.


https://github.com/whoshuu/cpr
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.