79100446

Date: 2024-10-18 03:31:05
Score: 1.5
Natty:
Report link

I reproduced it on my end, with a GET to google.com. I get the stream_truncated error as expectable in the wild. You already deal with that:

enter image description here

Replacing www.google.com with api.mailgun.net just removes that symptom for me:

enter image description here

Can you compare your implementation and check back with your own tests?

Live On Coliru

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/url.hpp>
#include <iostream>
namespace beast       = boost::beast;
namespace http        = boost::beast::http;
namespace net         = boost::asio;
namespace ssl         = net::ssl;
using url             = boost::urls::url;
using tcp             = net::ip::tcp;
using http_ssl_stream = boost::beast::ssl_stream<boost::beast::tcp_stream>;
using RequestType     = http::request<http::string_body>;
using ResponseType    = http::response<http::string_body>;
using BufferType      = boost::beast::flat_buffer;

/**
 * Do the session
 */
struct Session {
    net::coroutine                     coroutine_; // by value
    boost::shared_ptr<http_ssl_stream> stream_;
    boost::shared_ptr<RequestType>     request_;
    uint                               timeout_;     // by value
    std::string                        host_, port_; // ephemeral

    std::unique_ptr<tcp::resolver> resolver_ = {}; // stable
    std::unique_ptr<ResponseType>  response_ = {}; // stable
    std::unique_ptr<BufferType>    buffer_   = {}; // stable

    /**
     * First call
     */
    template <typename Self> void operator()(Self& self) {
        std::cout << __LINE__ << ": " << std::endl;
        // Set SNI Hostname (many hosts need this to handshake successfully)
        if (!SSL_set_tlsext_host_name(stream_->native_handle(), host_.c_str())) {
            // Callback with error
            return self.complete(
                beast::error_code(static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()),
                boost::none);
        }

        resolver_ = std::make_unique<tcp::resolver>(stream_->get_executor());
        resolver_->async_resolve(host_, port_, std::move(self));
    }

    /**
     * On resolved call
     */
    template <typename Self>
    void operator()(Self& self, beast::error_code error, tcp::resolver::results_type results) {
        std::cout << __LINE__ << ": " << error.message() << std::endl;
        // Resolve error, quit
        if (error) {
            return self.complete(error, boost::none);
        }

        // Set the expiration
        beast::get_lowest_layer(*stream_).expires_after(std::chrono::seconds(timeout_));

        // Do a connnect
        beast::get_lowest_layer(*stream_).async_connect(results, std::move(self));
    }

    /**
     * On connected
     */
    template <typename Self>
    void operator()(Self& self, beast::error_code error,
                    tcp::resolver::results_type::endpoint_type /*results*/) {
        std::cout << __LINE__ << ": " << error.message() << std::endl;
        // Connect error
        if (error) {
            return self.complete(error, boost::none);
        }

        // Set the expiration
        beast::get_lowest_layer(*stream_).expires_after(std::chrono::seconds(timeout_));

        // Do a handshake
        stream_->async_handshake(ssl::stream_base::client, std::move(self));
    }

    /**
     * After handshake
     */
    template <typename Self>
    void operator()(Self& self, beast::error_code error, size_t /*bytes_transferred*/ = 0) {
        // Coroutine for easy state knowing
        BOOST_ASIO_CORO_REENTER(coroutine_) {
            /*
            // Do the handshake
            BOOST_ASIO_CORO_YIELD
            {
                // Connect error
                if( error )
                    return self.complete( error, boost::none );


                // Set the expiration
                beast::get_lowest_layer( *stream_ ).expires_after( std::chrono::seconds( timeout_ ) );

                // Do a handshake
                stream_->async_handshake(
                            ssl::stream_base::client,
                            std::move( self )
                        );
            }
            */

            std::cout << __LINE__ << ": " << error.message() << std::endl;
            // Do the write
            BOOST_ASIO_CORO_YIELD {
                // Handshake error
                if (error) {
                    return self.complete(error, boost::none);
                }

                // Set up an HTTP GET request message
                request_->version(11);
                // request_->body() = body_;

                // Set the expiration
                beast::get_lowest_layer(*stream_).expires_after(std::chrono::seconds(timeout_));

                // Write the request
                http::async_write(*stream_, *request_, std::move(self));
            }

            std::cout << __LINE__ << ": " << error.message() << std::endl;
            // Execute a read
            BOOST_ASIO_CORO_YIELD {
                // Write error
                if (error) {
                    return self.complete(error, boost::none);
                }

                // Create the response
                response_ = std::make_unique<ResponseType>();

                // Create the buffa
                buffer_ = std::make_unique<BufferType>();

                // Set the expiration
                beast::get_lowest_layer(*stream_).expires_after(std::chrono::seconds(timeout_));

                // Receive the HTTP response
                http::async_read(*stream_, *buffer_, *response_, std::move(self));
            }

            std::cout << __LINE__ << ": " << error.message() << std::endl;
            // Shutdown the socket
            BOOST_ASIO_CORO_YIELD {
                // Read error
                if (error) {
                    return self.complete(error, boost::none);
                }

                // Set the expiration
                beast::get_lowest_layer(*stream_).expires_after(std::chrono::seconds(timeout_));

                // Receive the HTTP response
                stream_->async_shutdown(std::move(self));
            }

            std::cout << __LINE__ << ": " << error.message() << std::endl;
            // Shutdown error
            if (error == net::error::eof or error == ssl::error::stream_truncated) {

                // Rationale:
                // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error
                error = {};
            }

            std::cout << __LINE__ << ": " << error.message() << std::endl;
            // Did we get it?
            if (error) {
                self.complete(error, boost::none);
                return;
            }

            // Return no error and the buffer
            self.complete(error, *response_);
        }
    }
};

template <typename Executor, typename Token> auto async_https_get(Executor ex, url what, Token&& token) {
    static ssl::context ctx(ssl::context::tlsv12_client);

    std::string port     = what.has_port() ? what.port() : "https";
    std::string resource = what.encoded_resource().decode();
    std::cout << "Host: " << what.host() << " Port: " << port << " Resource: " << resource << std::endl;

    auto request = boost::make_shared<RequestType>(http::verb::get, resource, 11, "{}");
    request->set(http::field::host, what.host());

    Session session{
        net::coroutine(),
        boost::make_shared<http_ssl_stream>(ex, ctx),
        request,
        5, // seconds
        what.host(),
        port,
    };

    return net::async_compose<Token, void(beast::error_code, boost::optional<ResponseType>)>(
        std::move(session), token, ex);
}

int main() {
    net::thread_pool ioc(1);

    // url what("https://www.google.com/");
    url what("https://api.mailgun.net/");

    async_https_get(make_strand(ioc), what,
                    [&](beast::error_code ec, boost::optional<ResponseType> response) {
                        std::cout << "--\nCompleting with error: " << ec.message() << std::endl;
                        if (!ec)
                            std::cout << "--\nResponse: " << response->base() << std::endl;
                    });

    ioc.join();
}
Reasons:
  • Blacklisted phrase (1): stackoverflow
  • RegEx Blacklisted phrase (1): I get the stream_truncated error
  • Probably link only (1):
  • Long answer (-1):
  • Has code block (-0.5):
  • Ends in question mark (2):
  • High reputation (-2):
Posted by: sehe