LINUX.ORG.RU

REST через Boost.Asio

 , ,


0

1

Приветствую

Подскажите где у меня косяк, а смотрю в документацию - вижу фигу

Вызов Send(), чернового варианта класса описанного ниже, выполняется только один раз, в чем косяк у меня, а то смотрю в документацию - вижу фигу.

class CPush
{
private:
    boost::asio::io_service io_service;
    boost::asio::ip::tcp::resolver::iterator iterator;
    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> * psocket;
    boost::asio::streambuf response;

bool verify_certificate(bool preverified, boost::asio::ssl::verify_context & ctx)
{
    char subject_name[256];
    X509 * cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
    X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);

    std::cout << "Verifying " << subject_name << std::endl;

    return preverified;
}

void handle_connect(const boost::system::error_code & error)
{
    if (error)
        std::cerr << "Connect failed: " << error.message() << std::endl;
    else
    {
        psocket->async_handshake(boost::asio::ssl::stream_base::client,
                                boost::bind(&CPush::handle_handshake,
                                            this,
                                            boost::asio::placeholders::error));
        std::cout << "Connection OK!" << std::endl;
    }
}

void handle_handshake(const boost::system::error_code & error)
{
    if (error)
        std::cerr << "Handshake failed: " << error.message() << std::endl;
    else
    {
        std::string json("{\"registration_ids\":[\"" + token + "\"],"
                        +"\"notification\":null,"
.......
                        +"\"priority\":\"high\","
                        +"\"time_to_live\":15}");

        std::stringstream request;
        request << "POST " << GOOGLE_API << " HTTP/1.1\r\n"
                << "Host: " << GOOGLE_HOST << "\r\n" // << ":" << GOOGLE_PORT
                << "Content-Type: application/json; charset=utf-8\r\n"
                << "Content-Length: " << json.size() << "\r\n"
                << "Authorization: key=" << push_key << "\r\n"
                << "Connection: close\r\n"
                << "\r\n"
                << json << "\r\n\r\n";

        std::cout << "Sending request...\n" << request.str();


        boost::asio::async_write(*psocket,
                                boost::asio::buffer(request.str()),
                                boost::bind(&CPush::handle_write,
                                            this,
                                            boost::asio::placeholders::error));
    }
}

void handle_write(const boost::system::error_code & error)
{
    if (error)
      std::cerr << "Write failed: " << error.message() << std::endl;
    else
    {
        boost::asio::async_read_until(*psocket,
                                response, "\r\n",
                                boost::bind(&CPush::handle_read_status,
                                            this,
                                            boost::asio::placeholders::error));
        std::cout << "Sending request OK!" << std::endl;
    }
}

void handle_read_status(const boost::system::error_code & error)
{
    if (error)
        std::cout << "Error read status: " << error.message() << std::endl;
    else
    {
        // Check that response is OK.
        std::istream response_stream(&response);
        std::string http_version;
        response_stream >> http_version;
        unsigned int status_code;
        response_stream >> status_code;
        std::string status_message;
        std::getline(response_stream, status_message);

        if (!response_stream || http_version.substr(0, 5) != "HTTP/")
        {
            std::cout << "Invalid response\n";
            return;
        }

        if (status_code != 200)
        {
            std::cout << "Response returned with status code ";
            std::cout << status_code << "\n";
//          return;
        }

        // Read the response headers, which are terminated by a blank line.
        boost::asio::async_read_until(*psocket,
                                        response, "\r\n\r\n",
                                        boost::bind(&CPush::handle_read_header,
                                                    this,
                                                    boost::asio::placeholders::error));
    }
}

void handle_read_header(const boost::system::error_code & error)
{
    if (error)
        std::cout << "Error read header: " << error.message() << std::endl;
    else
    {
        // Process the response headers.
        std::istream response_stream(&response);
        std::string header;
        while (std::getline(response_stream, header) && header != "\r")
            std::cout << header << "\n";
        std::cout << "\n";

        // Write whatever content we already have to output.
        if (response.size() > 0)
            std::cout << &response;

        // Start reading remaining data until EOF.
        boost::asio::async_read(*psocket,
                                response,
                                boost::asio::transfer_at_least(1),
                                boost::bind(&CPush::handle_read_content,
                                            this,
                                            boost::asio::placeholders::error));
    }
}

void handle_read_content(const boost::system::error_code & error)
{
    if (error)
    {
        if (error != boost::asio::error::eof)
            std::cout << "Error read content: " << error.message() << std::endl;
    }
    else
    {
        // Write all of the data that has been read so far.
        std::cout << &response;

        // Continue reading remaining data until EOF.
        boost::asio::async_read(*psocket,
                                response,
                                boost::asio::transfer_at_least(1),
                                boost::bind(&CPush::handle_read_content,
                                            this,
                                            boost::asio::placeholders::error));
    }
}

public:

CPush(void)
{

    boost::asio::ip::tcp::resolver resolver(io_service);
    boost::asio::ip::tcp::resolver::query query(GOOGLE_HOST, GOOGLE_PORT);
    iterator = resolver.resolve(query);

    boost::asio::ssl::context context(boost::asio::ssl::context::sslv23);
    context.set_default_verify_paths();

    psocket = new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(io_service, context);
    psocket->set_verify_mode(boost::asio::ssl::context::verify_none);
    psocket->set_verify_callback(boost::bind(&CPush::verify_certificate, this, _1, _2));
}

void Send(void)
{
    boost::asio::async_connect(psocket->lowest_layer(),
                                iterator,
                                boost::bind(&CPush::handle_connect,
                                            this,
                                            boost::asio::placeholders::error));

    io_service.run();
}
};
★★★

Если жизнь без asio не мила, то посмотри https://github.com/Stiffstream/restinio. Автор тут на лоре рекламировался. c++14, header only. Я сам этот фреймворк не применял, дальше не подскажу.

Недосмотрел. Это сервер, а не клиент

ox55ff ★★★★★
()
Последнее исправление: ox55ff (всего исправлений: 1)
Ответ на: комментарий от ox55ff

В использовании boost::asio для rest запросов.

Персонажу еще в его предыдущей теме на это пытались намекнуть. Но он там бодренько отрапортовал, что у него все работает. Однако, не прошло и трех дней…

eao197 ★★★★★
()
Ответ на: комментарий от wolverin

На каждый запрос создаёшь контекст, а потом его сбрасываешь? Не очень эффективно. Запусти io_service.run() в отдельном потоке и подбрасывай ему команд для отправки/чтения когда нужно.

<< "Content-Length: " << json.size() << "\r\n"
<< "Authorization: key=" << push_key << "\r\n"
<< "Connection: close\r\n"
<< "\r\n"
<< json << "\r\n\r\n";

\r\n\r\n в конце не учитываешь в Content-Length.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Команд когда густо, а когда и пусто, поэтому ран завершается и приходится снова пнуть контекст.

Конечный не учитывается, потому что гугл с меня его не требует и так работает, сам он мне вообще заголовок без этого хедера шлёт… Надо попробовать просто не добавлять за жсоном ничего.

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от hatred

ну в смысле после первого, перед последующими. )

но тут другая проблема - после ресета события подключения и записи в сокет есть, а проверки сертификата CPush::verify_certificate нет

с одной стороны конечно пример буста говорит о том, что boost::asio::ssl::streamboost::asio::ip::tcp::socket нельзя использовать повторно и нужно пересоздавать объект… но зачем если уже все в классе проинициализированно не понимаю, лишь только обработчик за неимением команд завершился.

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)
Ответ на: комментарий от cvv

почему? как то же работает и преимущества по эффективности у буста существенные - можно держать сокет подключенным на время пиков рест-запросов, очередь запросов асинхронная, бинарник вырос на уровень погрешности, памяти так же почти не занимает клиент, опять же не маловажная вещь - это почти штатная либа, которую нигде качать и искать не надо.

…но хитрожопость ее работы конечно удивляет )) на идею переподключать сокет я плюнул и создаю его теперь каждый раз по новой, когда необходимо выполнить один или несколько подряд рест запросов - вроде в этом сцуть использования… НО выложил бинарник на целевой сервер - И НИФИГА проверка сертифика не проходит там, хз в чем причина, на другом проходит (правда в выхлопе почему то другая информация, но главное сам запрос выполняется), а на нужно мне нет, сначала думал версия дебина 8 старая, сегодня обновился до 9 - без изменений

wolverin ★★★
() автор топика
Последнее исправление: wolverin (всего исправлений: 1)