Quick Start

This guide walks through a complete program that performs an HTTPS request and prints the response.

A Complete Program

A complete Burl program looks like this:

#include <boost/burl.hpp>
#include <boost/capy.hpp>
#include <boost/corosio.hpp>
#include <iostream>

namespace burl    = boost::burl;
namespace capy    = boost::capy;
namespace corosio = boost::corosio;

capy::task<>
run(corosio::tls_context tls_ctx)
{
    burl::client client(co_await capy::this_coro::executor, tls_ctx);

    auto body = co_await client.get("https://example.com").as<std::string>();

    std::cout << body << '\n';
}

int
main()
{
    corosio::io_context ioc;
    corosio::tls_context tls_ctx;

    capy::run_async(ioc.get_executor())(run(tls_ctx));
    ioc.run();
}

The examples that follow assume this scaffolding and show only the body of run.

Parse the Body as JSON

Asking for a different type changes how the body comes back. With Boost.JSON included, request a json::value and the body is parsed as it arrives:

auto v = co_await client.get(url).as<json::value>();

std::cout << v.at("orders") << '\n';

The responses guide lists the built-in body types, and extending shows how to add your own.

Query Parameters and Headers

The builder shapes the request before it goes out. Chain query() to add query-string parameters and header() to set headers; each takes either an http::field enumerator or a string name. Headers set on the client itself are sent with every request:

// A default header on the client, sent with every request
client.headers().set(http::field::user_agent, "BoostBurl/1.0");

auto v = co_await client.get(url)
    .query("category", "shoes")
    .query("color", "blue")
    .header(http::field::accept_language, "en")
    .header("X-Trace-Id", "abc123")
    .as<json::value>();

See the query parameters and headers guides for encoding and the client-versus-request precedence rules.

Authentication

Basic and Bearer credentials can be set once on the client or supplied per request; both populate the Authorization header:

// Default, sent with every request
client.basic_auth("user", "pass");
// or client.bearer_auth("a-secret-token")

auto v = co_await client.get(url)
    .basic_auth("postman", "password") // per-request override
    .as<json::value>();

The authentication guide covers using several sets of credentials through one client.

Send a Body

The POST, PUT, and PATCH verbs accept a body through the builder. The body’s type determines the Content-Type and framing. A json::value is sent as application/json:

auto v = co_await client.post(url)
    .body(json::object({ { "user", "John" }, { "lang", "En" } }))
    .as<json::value>();

A urlencoded_form is sent as application/x-www-form-urlencoded:

auto v = co_await client.post(url)
    .body(burl::urlencoded_form()
        .append("user", "John")
        .append("lang", "En"))
    .as<json::value>();

A multipart_form is sent as multipart/form-data and can mix text fields with file parts; the filename and MIME type are deduced from the path:

auto v = co_await client.post(url)
    .body(burl::multipart_form()
        .file("attachment", "./report.log")
        .text("priority", "high"))
    .as<json::value>();

A std::filesystem::path streams a file as the request body, and serves as a response type too, writing the body straight to disk:

namespace fs = std::filesystem;

fs::path saved = co_await client.put(url)
    .body<fs::path>("./report.log")   // upload the file as the body
    .as<fs::path>("./resp.txt");      // save the response body to a file

The request bodies guide covers every built-in body type and how the Content-Type and framing are chosen.

Inspect the Status and Headers

as<T>() is the shortcut for the common case. When you need the status line and headers before deciding what to do with the body, call send() instead. It yields (error_code, response) with the body still unread:

auto [ec, r] = co_await client.get(url).send();
if(ec)
    throw std::system_error(ec);

std::cout << "status:  " << r.status_int() << '\n';
std::cout << "headers: " << r.headers() << '\n';
std::cout << "body:    " << co_await r.as<std::string>() << '\n';

Handle Errors Without Throwing

as<T>() throws on any failure, including a 4xx or 5xx status. To inspect an error instead, use the try_ forms, which yield (error_code, T):

auto [ec, body] = co_await client.get(url)
    .try_as<std::string>();

if(ec == burl::condition::client_error)
    std::cerr << ec.message() << '\n';   // e.g. HTTP 404 Not Found

The error handling guide explains the full model, including the order in which different failures take precedence.

Next Steps