Overview

This library is making http requests easy, yet still maintaining the full power of asio. As it’s basis, boost.beast, is very low-level, requests brings not only a high level interface but also an http library that makes creating your own high-level interfaces easy.

  auto r = requests::get(urls::url_view("https://httpbin.org/basic-auth/user/pass"),
                         requests::http::headers({requests::basic_auth("user", "pass")}));

  std::cout << r.result_code() << std::endl;
  // 200
  std::cout << r.headers["Content-Type"] << std::endl;
  // 'application/json; charset=utf8'

  std::cout << r.string_view() << std::endl;
  // {"authenticated": true, ...

  std::cout << requests::as_json(r) << std::endl;
  // {'authenticated': True, ...}

High level Interface

Method functions

The high level interface consists of free functions to perform requests. These will follow redirects & return the body as raw memory.

requests::response res = requests::get(urls::url_view("https://boost.org/index.html"));

The get does the same as the following call to request:

requests::response res = requests::request(
    requests::http::verb::get, // < method
    urls::url_view("https://boost.org/index.html"),
    requests::empty{}); // < request body

The request method requires users to specify the request body (which must empty for get), which can means the interface does allow ill-formed requests. request always returns a [response] object. The response response to request may contain an empty body, while the named function may use [response_base] to indicate at compile time that the method never has a response with a body.

requests::response_base res = requests::head(urls::url_view("https://boost.org/index.html"));

Every method-function has multiple overloads, as exemplified by the following overloads of get:

// default-session requests
auto get(urls::url_view target, http::fields req = {}) -> response;
// non-throwing overload
auto get(urls::url_view target, http::fields req, system::error_code & ec) -> response;

// request on a connection, session or pool
template<typename Connection>
auto get(Connection & conn,
         urls::url_view target,
         typename Connection::request_type req = {}) -> response;

// non-throwing overload
template<typename Connection>
auto get(Connection & conn,
         urls::url_view target,
         typename Connection::request_type req,
         system::error_code & ec) -> response

The overloads without a Connection parameter use the [default_session].

The req argument indicates how to perform the request. On a session request and one on a [session] those are the request headers, for connections & pools it’s a Request settings. This is because the non-header attributes in request_settings are part of the session.

If the method has a body it’s argument is passed between the target & req arguments:

template<typename RequestBody>
auto post(
    urls::url_view target,
    RequestBody && request_body,
    http::fields req = {}) -> response;

If the request-body is optional (as with delete), the function will provide overloads for either case, e.g.:

auto delete_(urls::url_view target, RequestBody && request_body, http::fields req = {}) -> response;
auto delete_(urls::url_view target,                              http::fields req = {}) -> response;

Additionally, every method has an asynchronous overload:

template< typename CompletionToken>
auto async_get(urls::url_view target, http::fields req, CompletionToken && completion_token);

template<typename Connection, typename CompletionToken>
auto async_get(Connection & conn,
                urls::url_view target,
                typename Connection::request_type req,
                CompletionToken && completion_token);

Which will have a signature of void(system::error_code, response) or void(system::error_code, response_base), depending on the http method. The version that takes a Connection parameter supports default completion tokens.

Below are all the http methods:

Http Method Name Async-Name Response body [1] Request Body Remarks

GET

get

async_get

always

never

HEAD

head

async_head

never

never

POST

post

async_post

always

always

PUT

put

async_put

optional

always

PATCH

patch

async_patch

optional

always

DELETE

delete_

async_delete

optional

optional

OPTIONS

options

async_options

optional

never

TRACE

trace

async_trace

never

never

CONNECT

connect

async_connect

never

always

Can only be used on a [connection]

Request Body

The body used for the requests requires specializations of [body_traits], which is an adaptor for the beast-bodies and provides a default mime-type.

Note
The traits mime-type will only be used for Content-Type if not already set in the headers.
requests::post(
    urls::url_view("https://httpbin.org/post")
        R"({"message" : "hello-world"})");

The above will use a span<char> to represent the data and set the data to "text/plain; charset=utf-8".

You can override the Content-Type by just setting it manually:

requests::post(
    urls::url_view("https://httpbin.org/post")
        R"({"message" : "hello-world"})",
    requests::headers({{"Content-Type", "application/json"}}));

Request settings

Request settings dictate how a request performs. It contains the headers, [request_options] and a pointer to the cookie_jar. When performing the request through a [session] the options & jar pointer will be injected from the session itself.

Redirects

Redirects are automatically handled, and every redirect response is stored in the [response_base.history] field.

The [request_options.redirect] sets the mode of redirection, which can be adjusted based on security concerns.

The default mode is [redirect_mode.private_domain], which allows redirect within one subdomain, such as boost.org to api.boost.org.

The private domain patterns are available through the [default_public_suffix_list].

Json

Since json is ubiquitous in http requests due to it’s usage in REST APIs, requests has special treatment for it.

The Json namespace provides http method similar to the ones in the [requests] namespace; although it ignores the methods that never have a return body. The resulting type is an instantiation of [json::response] which will contain a parse json body. If the method’s return body is optional, it’ll be wrapped in boost::optional.

This means, by default a function like [json::get] will return json::response<json::value> and [json::delete_] json::response<optional<json::value>>.

json::response<json::value>           res = json::get    ("https://httpbin.org/get");
json::response<optional<json::value>> oes = json::delete_("https://httpbin.org/delete");

It is also possible to directly convert the json into a struct, if try_value_to is valid for the type.

struct httpbin_res
{
    json::object args;
    unordered_map<json::string, json::string> headers;
    json::string origin;
    json::string url;
};

// let describe generate the json conversion
BOOST_DESCRIBE_STRUCT(httpbin_res, (), (args, headers, origin, url));

json::response<httpbin_res>           res = json::get    <httpbin_res>("https://httpbin.org/get");
json::response<optional<httpbin_res>> oes = json::delete_<httpbin_res>("https://httpbin.org/delete");

Similarly, the request_body will be treated as if it is json, i.e. it will attempt to use boost::json::value_from to send json data.

auto ptr = boost::json::make_shared_resource<boost::json::monotonic_resource>();

json::response<json::value> res = get(
    urls::url_view("https://httpbin.org/headers"),
    requests::headers({}, ptr.get()));

assert(ptr == res.value.storage());

Download

For big items, that should be directly transferred into files, requests provides the Download function. It will perform a GET request and directly write it to disk. If the path points to a directory the path will be deduced from the url-path.

requests::response_base res = requests::download(
    urls::url_view("https://boostorg.jfrog.io/artifactory/main/release/1.80.0/source/boost_1_80_0.tar.gz"),
    {}, filesystem::current_path());

Mid-level

Connections, Pools & Sessions

The basic building block is a [connection], which usually represents a single socket; this can be a tcp socket, a tcp socket or a domain socket. As such, a [connection] works on a single endpoint.

The pool is a dynamic collection of connections, working on a hostname & protocol pair, because a hostname can provide multiple endpoints. The pool will manage the amount of connections and distributing them over multiple endpoints.

A session represents a map of pools & hostname-protocol pairs, exclusively for http and https. It owns the cookie_jar & request settings, such as the [redirect_mode].

/---------\
| Session |
\---------/
    :
    :  hostname   /-------\  list   /------------\
    \------------>| pool  |-------->| connection |
                  \-------/         \------------/

requests brings a [default_session], which will be inserted into an asio::execution_context through use_service. By default that means the asio::system_executor is used. The async versions of the global function will however use the associated_executor to install the default session in.

stream

The fundamental building block used by high level functions is the stream, which is a response read that implements the asio concepts of SyncReadStream and AsyncReadStream.

That means that any read algorithm that works on classes like asio::ip::tcp::socket will also work on the requests::stream.

std::size_t read_line(requests::stream &str,
                      DynamicBuffer_v2 && buf)
{
    asio::read_until(str, buf, '\n');
}

Stream algorithms used by the high level functions are:

ropen

To obtain a stream session, connections & pools provide ropen and async_ropen functions.

These can either be used to construct a request on the fly, using the [body_traits], or can pass in an http::request by reference.

auto s = request_stream(requests::default_session(),
                        requests::http::get,
                        urls::url_view("https://boost.org/index.html"),
                        requests::empty{});

compile time optimization

The library optimizes compile times as much as possible without too much overhead. Because of that, everything uses a asio::generic::socket with an asio::any_io_executor, to avoid templating everything. # Reference

completion_handler_for and default_token are used instead of verbose asio macros.

connection.hpp

A connection pool represents a single http or domain socket connection to and endpoint.

namespace boost::requests
{

struct connection
{
    /// The type of the next layer.
    typedef asio::ssl::stream<asio::generic::stream_protocol::socket> next_layer_type;

    /// The type of the executor associated with the object.
    typedef typename next_layer_type::executor_type executor_type;

    /// The type of the executor associated with the object.
    typedef typename next_layer_type::lowest_layer_type lowest_layer_type;

    /// This helper type with a defaulted completion token.
    template<typename Token>
    struct defaulted;

    /// Get the executor
    executor_type get_executor() noexcept;

    /// Get the underlying stream
    const next_layer_type &next_layer() const noexcept;

    /// Get the underlying stream
    next_layer_type &next_layer() noexcept;

    /// The protocol-type of the lowest layer.
    using protocol_type = typename beast::lowest_layer_type<next_layer_type>::protocol_type;

    /// The endpoint of the lowest lowest layer.
    using endpoint_type = typename protocol_type::endpoint;

    /// Construct a stream that uses ssl
    template<typename ExecutorOrContext>
    explicit connection(ExecutorOrContext && exec_or_ctx, asio::ssl::context & ctx);

    /// Construct a stream that doesn't use ssl
    template<typename ExecutorOrContext>
    explicit connection(ExecutorOrContext && exec_or_ctx);

    /// Move construct a connection
    connection(connection && ) noexcept = default;

    /// Rebinding constructor;
    template<typename Other>
    connection(connection<Other> && lhs);

    /// Connect to an endpoint
    void connect(endpoint_type ep);
    void connect(endpoint_type ep, system::error_code & ec);

    template<asio::token_for<void (boost::system::error_code)> CompletionToken
                  asio::default_token_t<executor_type>>
    auto async_connect(endpoint_type ep,
                      CompletionToken && token = asio::default_token_t<executor_type>());

    /// Close the socket.
    void close();
    void close(system::error_code & ec);

    template<asio::token_for<void (boost::system::error_code)> CompletionToken =
                 asio::default_token_t<executor_type>>
    auto async_close(CompletionToken && token = asio::default_token_t<executor_type>());

    // Check if the connection is open.
    bool is_open() const final;

    // Get the Endpoint
    endpoint_type endpoint() const {return endpoint_;}

    // Timeout of the last connection-alive message
    std::chrono::system_clock::time_point timeout() const;
    // Amount of ongoing requests.
    std::size_t working_requests() const;

    // Reserve memory for the internal buffer.
    void reserve(std::size_t size);

    // Set the host of the endpoint
    void set_host(core::string_view sv);
    void set_host(core::string_view sv, system::error_code & ec);
    // Get the host name
    core::string_view host() const;

    /// The stream type returned by ropen.
    using stream = stream<executor_type>;

    /// Send a preexisting request.
    template<typename RequestBody>
    auto ropen(beast::http::verb method,
               urls::pct_string_view path,
               http::fields & headers,
               source & src,
               cookie_jar * jar,
               system::error_code & ec) -> stream;

    template<typename RequestBody>
    auto ropen(beast::http::verb method,
               urls::pct_string_view path,
               http::fields & headers,
               source & src,
               request_options opt,
               cookie_jar * jar) -> stream;

    template<typename RequestBody,
             typename CompletionToken
                  = asio::default_token_t<executor_type>>
    auto async_ropen(beast::http::verb method,
                     urls::pct_string_view path,
                     http::fields & headers,
                     source & src,
                     cookie_jar * jar = nullptr,
                     CompletionToken && token = asio::default_token_t<executor_type>());
};


}

connect

The connection function opens a connection to an endpoint. This will also perform the ssl handshake for ssl connections, which is why the host needs to be set previously with [connection::set_host] because the handshake will certify the hostname as well.

set_host

The set_host function sets the internal host-name and does apply it to ssl verification if used.

Note
You will also need to set this field for domain socket connection, as it’ll also be sent in the http request.

close

Closing a connection can involve an asynchronous operation if the socket is using ssl, which is why async_close is provided.

connection_pool.hpp

A connection pool represents a collection [connection] objects pointing to the same host & port.

struct connection_pool
{
    /// The type of the executor associated with the object.
    typedef typename Stream::executor_type executor_type;

    /// The type of the underlying connection.
    typedef connection<Stream> connection_type;

    /// Rebinds the socket type to another executor.
    template<typename Executor1>
    struct rebind_executor
    {
        /// The socket type when rebound to the specified executor.
        typedef connection_pool<typename Stream::template rebind_executor<Executor1>::other> other;
    };

    /// Get the executor
    executor_type get_executor() noexcept;

    /// The protocol-type of the lowest layer.
    using protocol_type = typename connection_type::protocol_type;

    /// The endpoint of the lowest lowest layer.
    using endpoint_type = typename connection_type::endpoint_type;

    /// The reolver_type of the lower layer.
    using resolver_type = typename protocol_type::resolver::template rebind_executor<executor_type>::other;


    /// Construct a pool from an executor.
    template<typename Exec, typename = std::enable_if_t<!detail::has_ssl_v<Stream>, Exec>>
    explicit connection_pool(Exec && exec,
                             std::size_t limit = BOOST_REQUESTS_DEFAULT_POOL_SIZE);

    /// Construct a pool from an executor and sslctx
    template<typename Exec>
    explicit connection_pool(Exec && exec,
                             asio::ssl::context & ctx,
                             std::size_t limit = BOOST_REQUESTS_DEFAULT_POOL_SIZE);

    /// Move constructor
    connection_pool(connection_pool && ) = default;

    /// rebind constructor.
    template<typename Exec>
    connection_pool(connection_pool<Exec> && lhs);

    /// Lookup the endpoints for an authority and set the host.
    void lookup(urls::authority_view av);
    void lookup(urls::authority_view sv, system::error_code & ec);
    template<completion_handler_for<void (boost::system::error_code)> CompletionToken
                = asio::default_token_t<executor_type>>
    auto async_lookup(urls::authority_view av, CompletionToken && token = asio::default_token_t<executor_type>());

    /// Get the maximum amount of active connections.
    std::size_t limit() const;
    /// Get the amount of active connections.
    std::size_t active() const;
    /// Get the number of connected connections available
    std::size_t free() const;

    /// Get an open connection that shall be returned later
    connection_type borrow_connection(error_code & ec);
    /// Get an open connection that shall be returned later
    connection_type borrow_connection();

    /// Get an open connection asynchronously that shall be returned later
    template<completion_handler_for<void (system::error_code, connection_type>>
                CompletionToken = default_token_type<CompletionToken>>
    auto async_borrow_connection(CompletionToken && completion_token = default_token<executor_type>);

    /// Get an open connection that  must not be returned later
    connection_type steal_connection(error_code & ec);
    /// Get an open connection that  must not be returned later
    connection_type steal_connection();

    /// Get an open connection asynchronously that must not be returned later
    template<completion_handler_for<void (system::error_code, connection_type>>
                CompletionToken = default_token_type<CompletionToken>>
    auto async_steal_connection(CompletionToken && completion_token = default_token<executor_type>);

    // Check if it's using ssl
    bool uses_ssl() cons;

    // Return a previously borrowed connection
    void return_connection(connection conn);

    // Remove a previously borrowed connection
    void remove_connection(const connection &conn);

    // List all the endpoints used by the pool
    const std::vector<endpoint_type> & endpoints() const;

};

async_lookup

Lookup uses a resolver for the given name and stores the endpoints associated with it. The pool will internally use all endpoints evenly when creating new connections. ## cookie.hpp

download.hpp

// Write all the content of a stream to a file.
template<typename Stream>
std::size_t write_to_file(Stream && str, const filesystem::path & file);
template<typename Stream>
std::size_t write_to_file(Stream && str, const filesystem::path & file, system::error_code & ec);

template<typename Stream,
          asio::completion_toke_for<void (boost::system::error_code, std::size_t)> CompletionToken
              asio::default_token_t<executor_type>>
auto async_write_to_file(Stream & str, const filesystem::path & file,
                         CompletionToken && token = asio::default_token_t<executor_type>());

/// Perform a get request and write the response body to a file
template<typename Connection>
inline auto download(Connection & conn,
                     /* target type of Connection */ target,
                     /* request type of Connection */ req,
                     filesystem::path download_path,
                     system::error_code & ec) -> download_response;

template<typename Connection>
inline auto download(Connection & conn,
                     /* target type of Connection */ target,
                     /* request type of Connection */ req,
                     filesystem::path download_path) -> download_response;

template<typename Connection,
         asio::completion_token_for<void (boost::system::error_code, download_response)> CompletionToken>
auto async_download(Connection & conn,
               /* target type of Connection */ target,
               /* request type of Connection */ req,
               filesystem::path download_path,
               CompletionToken && token = asio::default_token_t<executor_type>())

inline auto download(urls::url_view path,
                     http::fields req,
                     filesystem::path download_path,
                     system::error_code & ec) -> download_response;

inline auto download(urls::url_view path,
                     http::fields req,
                     filesystem::path download_path) -> download_response

template<asio::completion_token_for<void (boost::system::error_code, download_response)> CompletionToken>
auto async_download(urls::url_view path,
                    http::fields req,
                    filesystem::path download_path,
                    CompletionToken && completion_token);

The target type is urls::url_view for sessions, otherwise urls::pct_string_view. The request type is requests::http::headers for sessions, otherwise request_parameters. ## error.hpp

namespace boost::requests {

/// The type of error code used by the library
using error_code = boost::system::error_code;

/// The type of system error thrown by the library
using system_error = boost::system::system_error;

/// The type of error category used by the library
using error_category = boost::system::error_category;

error_category & http_status_category();

error_code make_error(beast::http::status stat);

/// Error codes returned from library operations
enum class error
{
  /// The redirect limit was exceeded
  too_many_redirects = 1,
  /// The redirect is disallowed by the settings
  forbidden_redirect,
  /// The redirect was invalid
  invalid_redirect,
  /// The request violates the tls requirement
  insecure,
  /// The target host is invalid
  wrong_host
};

error_category & request_category();

error_code
make_error_code(error e);

} // boost::requests

form.hpp

struct form
{
  form(std::initializer_list<urls::param_view> params);
  form(form && ) = default;
  form(const form & ) = default;
  form(form & rhs) : storage(rhs.storage) {}

  // construct the from any container
  template<typename Container>
  form(Container && ct);
};

The form class is a utility class to sent application/x-www-form-urlencoded data in a request.

requests::post("https://httpbin.org/post",
               requests::form{{"foo", "42"}, {"bar", "21"}});

http.hpp

namespace boost::requests::http
{

using boost::beast::http::field;
using boost::beast::http::status;
using boost::beast::http::status_class;
using boost::beast::http::to_status_class;
using boost::beast::http::to_string;
using boost::beast::http::verb;
using boost::beast::http::fields;


struct header
{
  http::field field = http::field::unknown;
  core::string_view key;
  core::string_view value;
  std::string buffer;

  header() = default;
  header(http::field field, core::string_view value) : field(field), value(value) {}
  header(core::string_view key, core::string_view value) : key(key), value(value) {}
};

// This class enables initializer_lists for requests.
struct headers : fields
{
  headers(std::initializer_list<header> fields)
  {
    for (const auto & init : fields)
      if (init.field != http::field::unknown)
        set(init.field, init.value);
      else
        set(init.key, init.value);
  }
  headers(fields && fl) : fields(std::move(fl)) {}
  using fields::fields;
  using fields::operator=;
};

}

The http header contains aliases for beast::http members.

json.hpp

namespace boost::requests::json
{


// read an entire body as json and convert the json value into a `json::value`.
json::value read_json(stream & str, json::storage_ptr ptr = {});

json::value read_json(stream & str, json::storage_ptr ptr, system::error_code & ec);

template<asio::completion_token_for<void (boost::system::error_code, json::value)> CompletionToken
              asio::default_token_t<typename Stream::executor_type>>
auto async_read_json(
        stream & str,
        json::storage_ptr ptr = {},
        CompletionToken && token asio::default_token_t<typename Stream::executor_type>());


// read an entire body as json and convert the json value into a `json::value`
// if the response has a body. otherwise the result is empty.
optional<json::value> read_optional_json(stream & str, json::storage_ptr ptr = {});

optional<json::value> read_optional_json(stream & str, json::storage_ptr ptr, system::error_code & ec);

template<asio::completion_token_for<void (boost::system::error_code, optional<json::value>)> CompletionToken
            asio::default_token_t<typename Stream::executor_type>>
auto async_read_optional_json(
        stream & str,
        json::storage_ptr ptr = {},
        CompletionToken && token = asio::default_token_t<typename Stream::executor_type>());


/// the response of a json request
struct response : response_base
{
  using allocator_type = std::allocator<char>;
  using fields_type = http::fields;

  response(allocator_type alloc,         history_type history, json::value && value);
  response(http::response_header header, history_type history, json::value && value);

  response(allocator_type alloc        , json::value && value = {});
  response(http::response_header header, json::value && value = {});

  using value_type = json::value;
  value_type value;
};

// http-methods are , they follow the pattern below:
template<typename RequestBody, typename Connection>
auto ~method~(Connection & conn,
              /* target type of Connection */  target,
              RequestBody && request_body, // omitted for some methods.
              /* request type of Connection */  req = {},
              json::storage_ptr ptr = {}) -> response;

template<typename RequestBody, typename Connection>
auto ~method~(Connection & conn,
              /* target type of Connection */  target,
              RequestBody && request_body, // omitted for some methods.
              /* request type of Connection */ req,
              json::storage_ptr ptr,
              system::error_code & ec) -> response;

template<typename Connection, typename RequestBody,
          asio::completion_token_for<void (boost::system::error_code, response)> CompletionToken
              = asio::default_token_t<typename Connection::executor_type>>
auto async_~method~(Connection & conn,
        /* target type of Connection */ target,
        RequestBody && request_body,
        /* request type of Connection */ req = {},
        json::storage_ptr ptr = {},
        CompletionToken && token = asio::default_token_t<typename Connection::executor_type>());

// requests on the default_session
template<typename RequestBody>
auto ~method~(urls::url_view target,
              RequestBody && request_body, // omitted for some methods.
              http::fields req,
              json::storage_ptr ptr = {}) -> response;

template<typename RequestBody>
auto ~method~(urls::url_view target,
              RequestBody && request_body, // omitted for some methods.
              http::fields req,
              json::storage_ptr ptr,
              system::error_code & ec) -> response;

template<typename RequestBody,
          asio::completion_token_for<void (boost::system::error_code, response)> CompletionToken>
auto async_~method~(
        urls::url_view target,
        RequestBody && request_body,
        http::fields req,
        json::storage_ptr ptr = {},
        CompletionToken && token);


}

The json header contains methods to send and receive json messages.

Below is a table of all available methods.

Methods with an optional request body are overloaded.

Http Method Name Async-Name Response body Request Body Remarks

GET

get

async_get

always

never

POST

post

async_post

always

always

PUT

put

async_put

optional

always

PATCH

patch

async_patch

optional

always

DELETE

delete_

async_delete

optional

optional

OPTIONS

options

async_options

optional

never

Note
The above functions are function objects, not functions.

The target type is urls::url_view for sessions, otherwise urls::pct_string_view. The request type is requests::http::headers for sessions, otherwise request_parameters. ## method.hpp

namespace boost::requests
{

// methods - those with optional request bodies are overloaded
template<typename Connection, typename RequestBody>
auto ~method~(
        Connection & conn,
        /* target type of Connection */ target,
        RequestBody && request_body, // omitted if not required
       /* request type of Connection */  req = {}) -> response;

template<typename Connection, typename RequestBody>
auto ~method~(
        Connection & conn,
        /* target type of Connection */ target,
        RequestBody && request_body, // omitted if not required
        /* request type of Connection */  req,
        system::error_code & ec) -> response;

template<typename Connection,
         typename RequestBody,
         asio::completion_token_for<void (boost::system::error_code, response)> CompletionToken
              asio::default_token_t<executor_type>>
auto
async_~method~(
        Connection & conn,
        /* target type of Connection */ target,
        RequestBody && request_body,
       /* request type of Connection */  req  = {},
        CompletionToken && completion_token asio::default_token_t<executor_type>());

template<typename RequestBody>
auto ~method~(
    urls::url_view target,
    RequestBody && request_body,
    http::fields req = {}) -> response;

template<typename RequestBody>
auto ~method~(
        urls::url_view target,
        RequestBody && request_body,
        http::fields req,
        system::error_code & ec) -> response;

template<typename RequestBody,
         asio::completion_token_for<void (boost::system::error_code, response)> CompletionToken>
auto async_~method~(
        urls::url_view target,
        RequestBody && request_body,
        http::fields req,
        CompletionToken && completion_token)

}

The method header contains methods to send and receive method messages.

Below is a table of all available methods.

Methods with an optional request body are overloaded.

Http Method Name Async-Name Response body [1] Request Body Remarks

GET

get

async_get

always

never

HEAD

head

async_head

never

never

POST

post

async_post

always

always

PUT

put

async_put

optional

always

PATCH

patch

async_patch

optional

always

DELETE

delete_

async_delete

optional

optional

OPTIONS

options

async_options

optional

never

TRACE

trace

async_trace

never

never

CONNECT

connect

async_connect

never

always

Can only be used on a [connection]

Note
The above functions are function objects, not functions.

The target type is urls::url_view for sessions, otherwise urls::pct_string_view. The request type is requests::http::headers for sessions, otherwise request_parameters.

mime_types.hpp

using mime_type_map =
    unordered_map<core::string_view,
                  core::string_view,
                  boost::urls::grammar::ci_hash,
                  boost::urls::grammar::ci_equal>;

const mime_type_map & default_mime_type_map();

The default mime types is a map of file suffixes to the most likely mime type as show below.

file ending Description Mime type

".3g2"

3GPP2 audio/video container

"video/3gpp2"

".7z"

7-zip archive

"application/x-7z-compressed"

".aac"

AAC audio

"audio/aac"

".abw"

AbiWord document

"application-x-abiword"

".arc"

Archive document (multiple files embedded)

"application-x-freearc"

".avif"

AVIF image

"image/avif"

".avi"

AVI: Audio Video Interleave

"videox-msvideo"

".azw"

Amazon Kindle eBook format

"application/vnd.amazon.ebook"

".bin"

Any kind of binary data

"application/octet-stream"

".bmp"

Windows OS/2 Bitmap Graphics

"image/bmp"

".bz"

BZip archive

"application/x-bzip"

".bz2"

BZip2 archive

"application/x-bzip2"

".cda"

CD audio

"application/x-cdf"

".csh"

C-Shell script

"application/x-csh"

".css"

Cascading Style Sheets (CSS)

"text/css"

".csv"

Comma-separated values (CSV)

"text/csv"

".doc"

Microsoft Word

"application/msword"

".docx"

Microsoft Word (OpenXML)

"application/vnd.openxmlformats-officedocument.wordprocessingml.document"

".eot"

MS Embedded OpenType fonts

"application/vnd.ms-fontobject"

".epub"

Electronic publication (EPUB)

"application/epub+zip"

".gz"

GZip Compressed Archive

"application/gzip"

".gif"

Graphics Interchange Format (GIF)

"image/gif"

".htm"

.html HyperText Markup Language (HTML)

"text/html"

".html"

.html HyperText Markup Language (HTML)

"text/html"

".ico"

Icon format

"image/vnd"

".ics"

iCalendar format

"text/calendar"

".jar"

Java Archive (JAR)

"application/java-archive"

".jpeg"

.jpg JPEG images

"image/jpeg"

".js"

JavaScript

"text/javascript"

".json"

JSON format

"application/json"

".jsonld"

JSON-LD format

"application/ld+json"

".mid"

.midi Musical Instrument Digital Interface

"audio/x-midi"

".mjs"

JavaScript module

"text/javascript"

".mp3"

MP3 audio

"audio/mpeg"

".mp4"

MP4 video

"video/mp4"

".mpeg"

MPEG Video

"video/mpeg"

".mpkg"

Apple Installer Package

"application/vnd.apple.installer+xml"

".odp"

OpenDocument presentation document

"application/vnd.oasis.opendocument.presentation"

".ods"

OpenDocument spreadsheet document

"application/vnd.oasis.opendocument.spreadsheet"

".odt"

OpenDocument text document

"application/vnd.oasis.opendocument.text"

".oga"

OGG audio

"audio/ogg"

".ogv"

OGG video

"video/ogg"

".ogx"

OGG

"application/ogg"

".opus"

Opus audio

"audio/opus"

".otf"

OpenType font

"font/otf"

".png"

Portable Network Graphics

"image/png"

".pdf"

Adobe Portable Document Format (PDF)

"application/pdf"

".php"

Hypertext Preprocessor (Personal Home Page)

"httpd-php"

".ppt"

Microsoft PowerPoint application/vnd

"ms-powerpoint"

".pptx"

Microsoft PowerPoint (OpenXML)

"application/vnd.openxmlformats-officedocument.presentationml.presentation"

".rar"

RAR archive

"application/vnd.rar"

".rtf"

Rich Text Format (RTF)

"application/rtf"

".sh"

Bourne shell script

"application/x-sh"

".svg"

Scalable Vector Graphics (SVG)

"image/svg+xml"

".tar"

Tape Archive (TAR)

"application/x-tar"

".tif"

.tiff Tagged Image File Format (TIFF)

"image/tiff"

".tiff"

.tiff Tagged Image File Format (TIFF)

"image/tiff"

".ts"

MPEG transport stream

"video/mp2t"

".ttf"

TrueType Font

"font/ttf"

".txt"

Text, (generally ASCII or ISO 8859-n)

"text/plain"

".vsd"

Microsoft Visio

"application/vnd.visio"

".wav"

Waveform Audio Format

"audio/wav"

".weba"

WEBM audio

"audio/webm"

".webm"

WEBM video

"video/webm"

".webp"

WEBP image

"image/webp"

".woff"

Web Open Font Format (WOFF)"

font/woff"

".woff2"

Web Open Font Format (WOFF)"

font/woff2"

".xhtml"

XHTML

"application/xhtml+xml"

".xls"

Microsoft Excel application/vnd

"ms-excel"

".xlsx"

Microsoft Excel (OpenXML)

"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"

".xml"

"application/xml"

public_suffix.hpp

namespace boost::request
{

// a public suffix list used for redirect restrictions.
struct public_suffix_list
{
    unordered_set<core::string_view> full_matches;
    unordered_set<core::string_view> whitelist;
    unordered_set<core::string_view> wildcards;
};

// the public suffix list generated from https://publicsuffix.org/list/public_suffix_list.dat,
const public_suffix_list & default_public_suffix_list();
// lot a public suffix list
public_suffix_list load_public_suffix_list(core::string_view map);

/// check if the suffix is public according to the list passed in parameter 2
bool is_public_suffix(core::string_view value,
                      const public_suffix_list & pse = default_public_suffix_list());

}

redirect.hpp

namespace boost::request
{

enum redirect_mode
{
    /// Follow no redirect  at all.
    none,
    /// Follow redirect on the same endpoint, i.e. different target
    endpoint,
    /// Follow redirects on the same domain, e.g. http -> https
    domain,
    /// Follow redirects to subdomains, e.g. boost.org -> www.boost.org but not vice versa
    subdomain,
    /// Follow redirects withing a non-public suffix, e.g.
    /// www.boost.org -> boost.org or api.boost.org, but not get-hacked.org.
    private_domain,
    /// Follow any redirect
    any
};

bool should_redirect(
        redirect_mode mode,
        urls::url_view current,
        urls::url_view target,
        const public_suffix_list & pse = default_public_suffix_list());

/// Get the port from a url-view
std::uint16_t get_port(urls::url_view domain);

/// Check if the endpoint is the same as the endpoint
bool same_endpoint_on_host(const urls::url_view current, const asio::ip::tcp::endpoint);

/// Check if the endpoint is the same as the endpoint
bool same_endpoint_on_host(const urls::url_view current, const asio::local::stream_protocol::endpoint);

/// Check if a status is a redirect
bool is_redirect(http::status rc);

}

request_options.hpp

namespace boost::requests
{
/// The basic options attached to any request
struct request_options
{
  /// Only allow SSL requests
  bool enforce_tls{true};
  /// The allowed redirect mode.
  redirect_mode redirect{private_domain};
  /// The maximum of allowed redirects.
  std::size_t max_redirects{12};
};

/// The default options used by sessions.
request_options & default_options();
}

request_parameters.hpp

namespace boost::requests
{

/// A pair describing a header entry
struct field_entry
{
  variant2::variant<http::field, core::string_view> key;
  core::string_view value;
  std::string buffer;
};

// helper to add basic authorization
field_entry basic_auth(core::string_view username,
                       core::string_view password);

// helper to add bearer authorization
field_entry bearer(core::string_view token);

// helper to create headers from an initializer list.
auto headers(std::initializer_list<field_entry> fields);

// The full request settings used in the connection.
struct request_parameters
{
  using fields_type = beast::http::fields;
  fields_type fields;
  request_options opts{};
  cookie_jar * jar = nullptr;
};

}

request.hpp

namespace boost::request
{

// Open a stream after following redirects
template<typename Connection, typename RequestBody>
auto request_stream(
    Connection & conn,
    http::verb method,
    /* target type of Connection */ path, // pct_string_view when connection
    RequestBody && body,
    /* request type of Connection */ req, // headers for conn,
    system::error_code & ec) -> std::pair<stream, history>;

template<typename Connection, typename RequestBody>
auto request_stream(
    Connection & conn,
    http::verb method,
    /* target type of Connection */ path, // pct_string_view when connection
    RequestBody && body,
    /* request type of Connection */ req /* headers for conn*/ ) -> std::pair<stream, history>;

template<typename RequestBody,
          BOOST_ASIO_COMPLETION_TOKEN_FOR(void (boost::system::error_code, stream, history)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
                                   void (boost::system::error_code, stream, history))
                                   void (boost::system::error_code, stream, history))
async_request_stream(
    http::verb method,
    core::string_view path,
    RequestBody && body,
    http::fields req,
    CompletionToken && completion_token);

template<typename Connection,
          typename RequestBody,
          BOOST_ASIO_COMPLETION_TOKEN_FOR( void (system::error_code, stream, history)) CompletionToken
          = typename Connection::default_token>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (system::error_code, stream, history))
async_request_stream(
    Connection & conn,
    http::verb method,
    /* target type of Connection */ path,
    RequestBody && body,
    /* request type of Connection */ req,
    CompletionToken && completion_token = typename Connection::default_token());


// Execute a request by using `request_stream` and read the body as raw memory.
template<typename Connection, typename RequestBody>
auto request(Connection & conn,
             http::verb method,
             urls::url_view path,
             RequestBody && body,
             /* request type of Connection */ req,
             system::error_code & ec) -> response;

template<typename Connection, typename RequestBody>
auto request(Connection & conn,
             http::verb method,
             urls::url_view path,
             RequestBody && body,
             /* request type of Connection */ req) -> response;


template<typename RequestBody>
auto request(http::verb method,
             urls::url_view path,
             RequestBody && body,
             http::fields req,
             system::error_code & ec) -> response;

template<typename RequestBody>
auto request(http::verb method,
             urls::url_view path,
             RequestBody && body,
             http::fields req) -> response;

template<typename Connection,
          typename RequestBody,
          BOOST_ASIO_COMPLETION_TOKEN_FOR(void (error_code, response)) CompletionToken
            = typename Connection::default_token>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (error_code, response))
async_request(Connection & conn,
              http::verb method,
              /* target type of Connection */ target,
              RequestBody && body,
              /* request type of Connection */ req,
              CompletionToken && completion_token = typename Connection::default_token());

template<typename RequestBody,
         BOOST_ASIO_COMPLETION_TOKEN_FOR(void (error_code, response)) CompletionToken>
BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, void (error_code, response))
async_request(http::verb method,
              urls::url_view target,
              RequestBody && body,
              http::headers req,
              CompletionToken && completion_token);


}

The target type is urls::url_view for sessions, otherwise urls::pct_string_view. The request type is requests::http::headers for sessions, otherwise request_parameters. ## response.hpp

namespace boost::requests
{


struct response_base
{
using allocator_type = std::allocator<char>;
    using buffer_type    = beast::basic_flat_buffer<allocator_type>;
  using body_type      = beast::http::basic_dynamic_body<buffer_type>;

  http::response_header headers;

  int          result_code() const {return headers.result_int(); }
  http::status result()      const {return headers.result(); }

  using string_body_type = typename beast::http::basic_string_body<char, std::char_traits<char>, allocator_type>;
using allocator_type = std::allocator<char>;
    using history_type = std::vector<typename http::response<body_type>, vector_alloc>;

  // The history contains all the redirect messages.
  history_type history;

  response_base(allocator_type alloc,         history_type history);
  response_base(http::response_header header, history_type history);

  response_base(allocator_type alloc        );
  response_base(http::response_header header);

  ~response_base() = default;

  response_base(const response_base & ) = default;
  response_base(response_base && ) noexcept = default;

  response_base& operator=(const response_base & ) = default;
  response_base& operator=(response_base && ) noexcept = default;

  // Return false if the status is not 2xx
  bool ok () const;
  // Return false if the status is not 2xx
  explicit operator bool() const { return ok(); }

  bool is_redirect() const;
  bool is_permanent_redirect() const;

  // Return an error if the status is not 2xx
  system::error_code status_as_error() const;

  // Throw if the status is not 2xx
  void throw_status_if_error() const;

  // Collect all the entires of the link header(s)
  system::result<std::vector<struct link>> link() const;
};

// The response with the body as buffer.
struct response : response_base
{
  // the full response content
  buffer_type buffer;

  response(allocator_type alloc = {});
  response(http::response_header header, buffer_type buffer);
  response(response_base         header, buffer_type buffer);

  response(http::response_header header, history_type history, buffer_type buffer);

  response(const response & ) = default;
  response(response && ) noexcept = default;

  response& operator=(const response & ) = default;
  response& operator=(response && ) noexcept = default;

  template<typename Char = char,
           typename CharTraits = std::char_traits<char>>
  auto string_view() const -> basic_string_view<Char, CharTraits>;

  template<typename Char = char,
            typename CharTraits = std::char_traits<char>,
            typename Allocator_>
  auto string(Allocator_ && alloc) const -> std::basic_string<Char, CharTraits, std::decay_t<Allocator_>>;

  template<typename Char = char,
           typename CharTraits = std::char_traits<char>>
  auto string() const -> std::basic_string<Char, CharTraits, allocator_type>;

  template<typename Byte = unsigned char>
  auto raw() const -> span<Byte>;
};


}

service.hpp

namespace boost::requests
{

struct session_service : asio::execution_context::service
{
  using executor_type = asio::any_io_executor;
  using session_type = session;

  session_service(asio::execution_context & ctx);
  ~session_service();
  void shutdown() override;
  void destroy();
};

// get the session of the execution_context of the passed executor.
auto default_session(asio::any_io_executor exec = asio::system_executor()) -> session &;

}

session.hpp

namespace boost::requests
{

struct session
{
    /// The type of the executor associated with the object.
    typedef Executor executor_type;

    /// Rebinds the timer type to another executor.
    template<typename Executor1>
    struct rebind_executor
    {
        /// The timer type when rebound to the specified executor.
        typedef session<Executor1> other;
    };

    /// Constructor.
    explicit session(const executor_type &ex);

    /// Construct a session from a context.
    template<typename ExecutionContext>
    explicit session(ExecutionContext &context);

    /// Rebinding construcotr.
    template<typename Executor2>
    explicit session(session<Executor2> && sess);

    /// Get the executor associated with the object.
    executor_type get_executor() BOOST_ASIO_NOEXCEPT;

          struct request_options & options()       {return options_;}
    const struct request_options & options() const {return options_;}

    // A variant of pools, depending on the url
    using pool_ptr = variant2::variant<std::shared_ptr<http_connection_pool<Executor>>,
                                       std::shared_ptr<https_connection_pool<Executor>>>;

    // Get a pool for a given url.
    pool_ptr get_pool(urls::url_view url, error_code & ec);
    pool_ptr get_pool(urls::url_view url);
    template<asio::token_for<void (boost::system::error_code, pool_ptr)> CompletionToken
                  = asio::default_token_t<executor_type>>
    auto async_get_pool(urls::url_view path,
                        CompletionToken && token = asio::default_token_t<executor_type>());

    void shutdown();

    using stream = stream<Executor>;
};

}

source.hpp

struct source
{
  virtual ~source() = default;
  virtual optional<std::size_t> size() const = 0;
  virtual void reset() = 0;
  virtual std::pair<std::size_t, bool> read_some(void * data, std::size_t size, system::error_code & ) = 0;
  virtual core::string_view default_content_type() {return "";}
};

The source class is used to write request bodies.

The source can be implemented to add more types by adding a tag_invoke function. Below is an exemplary implementation.

template<>
struct meme_source
{
  my_type data;
  my_type::buffer buffer{data.make_buffer();}

  meme_source(const my_type &data) : data(data) {}

  core::string_view default_content_type()
  {
    return "text/my-meme-type";
  }

// used to write again, useful for redirects
  void reset() { buffer = data.make_buffer(); }
  optional<std::size_t> size() const override {return buffer.size();};

  std::pair<std::size_t, bool> read_some(void * data, std::size_t size, system::error_code & ec) override
  {
    if (buffer.done())
        return {0u, false};
    else
        return {buffer.read_some(data, size, ec), true};
  }
};


auto tag_invoke(const make_source_tag&, const my_type & data)
{
    return meme_source(data);
}

The following tag_invoke`s are provided out of the box (the `json ones requires you to include boost/requests/json.hpp and form ones requires you to include boost/requests/form.hpp ).

RequestBody type

Default Mime-Type

source type

std::basic_string<C>

"text/plain"

basic_string_source<C>

const char[N]

"text/plain; charset=utf-8"

basic_string_view_source<char>

core::basic_string_view<C>

"text/plain"

basic_string_view_source<C>

asio::const_buffer

"application/octet-stream"

buffer_source

asio::mutable_buffer

"application/octet-stream"

buffer_source

urls::params_encoded_view

"application/x-www-form-urlencoded"

form_source

requests::form

"application/x-www-form-urlencoded"

form_source

std::filesystem::path

Taken from [default_mime_type_map] or "text/plain"

file_source

boost::filesystem::path

Taken from [default_mime_type_map] or "text/plain"

file_source

json::value

"application/json"

json_source

json::object

"application/json"

json_source

json::array

"application/json"

json_source

stream.hpp

namespace boost::requests
{

struct stream
{
  /// The type of the executor associated with the object.
  typedef Executor executor_type;

  /// Get the executor
  executor_type get_executor() noexcept;
  /// Check if the underlying connection is open.
  bool is_open() const;

  /// Read some data from the request body.
  template<typename MutableBuffer>
  std::size_t read_some(const MutableBuffer & buffer);

  /// Read some data from the request body.
  template<typename MutableBuffer>
  std::size_t read_some(const MutableBuffer & buffer, system::error_code & ec);

  /// Read some data from the request body.
  template<
      typename MutableBufferSequence,
      asio::completion_token_for<void (system::error_code, std::size_t)> CompletionToken
          = asio::default_completion_token_t<executor_type>>
  auto async_read_some(
      const MutableBufferSequence & buffers,
      CompletionToken && token = asio::default_completion_token_t<executor_type>());

  /// Read all the data from the request body.
  template<typename DynamicBuffer>
  std::size_t read(DynamicBuffer & buffer);
  template<typename DynamicBuffer>
  std::size_t read(DynamicBuffer & buffer, system::error_code & ec);

   template<
      typename DynamicBuffer,
      asio::completion_token_for<void (system::error_code, std::size_t)> CompletionToken
          = asio::default_completion_token_t<executor_type>>
  auto async_read(
      DynamicBuffer & buffers,
      CompletionToken && token asio::default_completion_token_t<executor_type>());


  /// dump the rest of the data.
  void dump();
  void dump(system::error_code & ec);
  template<asio::completion_token_for<void (boost::system::error_code)>
          CompletionToken = asio::default_completion_token_t<executor_type>>
  auto async_dump(CompletionToken && token asio::default_completion_token_t<executor_type>());

  stream(stream && lhs) = default;
  stream& operator=(stream && lhs) = default;

  stream           (const stream &) = delete;
  stream& operator=(const stream &) = delete;
  ~stream();

  using history_type = response_base::history_type;
  bool done() const;

  const http::response_header &headers() const &;
  const history_type          &history() const & { return history_; }


  // get history & headers for a lvalue
  http::response_header &&headers() &&;
  history_type          &&history() &&;

  void prepend_history(history_type && pre_history);
};

using stream = stream<>;
}

The stream is used is to read the content of a response. On destruction the remaining data gets dumped.

This documentation is

  • Copyright 2023 Klemens Morgenstern

and is distributed under the Boost Software License, Version 1.0.


1. never means the type is [response_base]