Program Listing for File http_socket.hpp

Return to documentation for file (stream-client/stream/http_socket.hpp)

#pragma once

#include "detail/static_allocator.hpp"
#include "dgram_socket.hpp"
#include "ssl_stream_socket.hpp"
#include "stream_socket.hpp"

#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/http/message.hpp>
#include <boost/beast/http/parser.hpp>
#include <boost/beast/http/read.hpp>
#include <boost/beast/http/string_body.hpp>
#include <boost/beast/http/write.hpp>

namespace stream_client {
namespace http {

template <typename Stream>
class base_socket
{
public:
    static const size_t kHeaderLimit;
    static const size_t kBodyLimit;

    using allocator_type = std::allocator<char>;
    using next_layer_type = typename std::remove_reference<Stream>::type;
    using protocol_type = typename next_layer_type::protocol_type;
    using endpoint_type = typename next_layer_type::endpoint_type;
    using clock_type = typename next_layer_type::clock_type;
    using time_duration_type = typename next_layer_type::time_duration_type;
    using time_point_type = typename next_layer_type::time_point_type;

    template <class Arg1, class... ArgN,
              class = typename std::enable_if<
                  !std::is_same<typename std::decay<Arg1>::type, base_socket<Stream>>::value>::type>
    base_socket(Arg1&& arg1, ArgN&&... argn)
        : stream_(std::forward<Arg1>(arg1), std::forward<ArgN>(argn)...)
        , buffer_(kBodyLimit + kHeaderLimit)
    {
    }

    base_socket(const base_socket<Stream>& other) = delete;
    base_socket<Stream>& operator=(const base_socket<Stream>& other) = delete;
    base_socket(base_socket<Stream>&& other) = default;
    base_socket<Stream>& operator=(base_socket<Stream>&& other) = default;

    virtual ~base_socket() = default;

    template <typename Body, typename Fields>
    boost::optional<boost::beast::http::response<Body, Fields>>
    perform(const boost::beast::http::request<Body, Fields>& request, boost::system::error_code& ec,
            const time_point_type& deadline);

    template <typename Body, typename Fields>
    inline boost::optional<boost::beast::http::response<Body, Fields>>
    perform(const boost::beast::http::request<Body, Fields>& request, boost::system::error_code& ec,
            const time_duration_type& timeout)
    {
        return perform(request, ec, clock_type::now() + timeout);
    }

    template <typename Body, typename Fields>
    inline boost::optional<boost::beast::http::response<Body, Fields>>
    perform(const boost::beast::http::request<Body, Fields>& request, boost::system::error_code& ec)
    {
        return perform(request, ec, stream_.io_timeout());
    }

    template <typename Body, typename Fields>
    inline boost::beast::http::response<Body, Fields> perform(const boost::beast::http::request<Body, Fields>& request)
    {
        boost::system::error_code ec;
        auto response = perform(request, ec);
        if (ec) {
            throw boost::system::system_error{ec};
        }
        if (!response) {
            throw boost::system::system_error{boost::asio::error::operation_aborted};
        }
        return *response;
    }

    inline const next_layer_type& next_layer() const
    {
        return stream_;
    }

    inline next_layer_type& next_layer()
    {
        return stream_;
    }

    inline bool is_open() const
    {
        return next_layer().is_open();
    }

protected:
    template <typename Body, typename Fields>
    void send_request(const boost::beast::http::request<Body, Fields>& request, boost::system::error_code& ec,
                      const time_point_type& deadline);

    template <typename Parser, typename DynamicBuffer>
    void recv_response(Parser& response_parser, DynamicBuffer& buffer, boost::system::error_code& ec,
                       const time_point_type& deadline);

    template <typename Body, typename Fields>
    boost::optional<boost::beast::http::response<Body, Fields>> recv_response(boost::system::error_code& ec,
                                                                              const time_point_type& deadline);

private:
    Stream stream_;
    boost::beast::basic_flat_buffer<allocator_type> buffer_;
};

using http_client = base_socket<::stream_client::tcp_client>;
using https_client = base_socket<::stream_client::ssl::ssl_client>;

} // namespace http
} // namespace stream_client

#include "impl/http_socket.ipp"