Responses
A response gives access to the status, headers, and body of a reply. When
you obtain one, the status line and headers have already been read; the body is
still on the connection, waiting to be consumed. This section covers reading the
metadata and the four ways to read the body.
Status, Headers, and URL
The metadata is available immediately, without any further I/O:
auto [ec, r] = co_await client.get(url).send();
r.status(); // http::status, e.g. http::status::not_found
r.status_int(); // unsigned short, e.g. 404
r.reason(); // string_view, e.g. "Not Found"
r.ok(); // true for a 2xx status
r.raise_for_status(); // throws on 4xx and 5xx
r.version(); // http::version
r.headers(); // const http::fields_base&
r.url(); // urls::url_view, the final URL after redirects
r.content_length(); // optional<uint64_t>, empty for a chunked body
Reading the Body
The body is read on demand, and there are four ways to do it:
| Approach | Use when |
|---|---|
You want the whole body as a |
|
Read in place ( |
The body fits in the parser’s buffer and you want a view with no allocation. |
Buffer source ( |
The body is large; process it in chunks from the parser’s own buffers. |
Read source ( |
You want the body read incrementally into buffers you provide. |
Converting to a Type
std::string s = co_await r.as<std::string>();
json::value v = co_await r.as<json::value>();
The built-in response conversions are:
| Type | Result |
|---|---|
The raw body bytes. |
|
|
The body parsed as JSON. |
|
The body parsed as JSON, failing if it is not of that kind. |
The body written to a file; yields the path. |
Extending shows how to add your own type.
A conversion may take extra arguments, forwarded after the type. Saving to a file takes the destination path:
auto path = co_await r.as<std::filesystem::path>("./download.bin");
Reading in Place
When the body is small enough to fit in the parser’s internal buffer,
as_view and try_as_view read
it there and return a std::string_view over it, without a separate
allocation:
std::string_view body = co_await r.as_view();
The buffer’s size is config::response_inplace_buffer. A body
that does not fit fails with http::error::in_place_overflow, which is a signal
to fall back to as<std::string> rather than a hard error. The
returned view points into memory owned by the response and is valid until the
response is destroyed or moved from.
Streaming with a Buffer Source
For a body too large to hold in memory,
as_buffer_source returns a source that exposes
the parser’s buffers directly as the body arrives. Pull buffers, consume them,
and repeat until end of stream:
auto source = r.as_buffer_source();
for(;;)
{
capy::const_buffer arr[8];
auto [ec, bufs] = co_await source.pull(arr);
if(ec == capy::cond::eof)
break;
if(ec)
throw std::system_error(ec);
for(auto const& buf : bufs)
{
write_somewhere(buf.data(), buf.size());
source.consume(buf.size());
}
}
as_read_source is the alternative when you would
rather the body be read into buffers you supply than borrow the parser’s.
The Connection’s Lifetime
A response owns the connection it arrived on. When the response is
destroyed, that connection is returned to the
pool for reuse, but only if it can be kept
alive and the whole message has been received. A body left partially read, or a
response whose connection cannot be reused, is closed instead. Reading the body
to completion is therefore what allows the connection to be reused; this happens
naturally with as<T> and as_view, and
is your responsibility when streaming.
A response remains usable after the client that produced it is gone. In that case the connection is simply closed on destruction rather than pooled.
Next Steps
-
Error Handling — What the read can fail with
-
Compression — Transparent decompression of the body
-
Connection Pool — Where the connection goes next
-
Extending — Converting the body to your own types