Serializer Design Requirements
Use Cases and Interfaces
Empty Body
The interface allows for sending bodiless requests or responses; it’s possible to reuse the existing serializer
and request
/response
objects without any need for extra memory allocation.
system::result<void>
handle_request(
serializer& sr,
response& res,
request_view req)
{
res.set_start_line(status::not_found, req.version());
res.set_keep_alive(req.keep_alive());
sr.start(res);
return {};
}
Source Body
A source
-like body allows for algorithms that can generate body contents directly in the serializer
's internal buffer in one or multiple steps, for example for sending contents of a file. The serializer
takes ownership of the source
object and is responsible for driving the algorithm and offering a MutableBufferSequence
(by calling the related virtual interfaces on the source
object).
system::result<void>
handle_request(
serializer& sr,
response& res,
request_view req)
{
res.set_start_line(status::ok, req.version());
res.set_keep_alive(req.keep_alive());
res.set_chunked(true);
http_proto::file file;
system::error_code ec;
file.open("./index.html", file_mode::scan, ec);
if (ec.failed())
return ec;
sr.start<file_body>(res, std::move(file));
return {};
}
ConstBufferSequence Body
The serializer
can use body contents passed as a ConstBufferSequence
without copying it into its internal buffer.
system::result<void>
handle_request(
serializer& sr,
response& res,
request_view req)
{
res.set_start_line(status::not_found, req.version());
res.set_keep_alive(req.keep_alive());
// Assume caller has an stable reference to static_pages
sr.start(res, buffers::make_buffer(static_pages.not_found));
return {};
}
ConstBufferSequence Body with Ownership Transfer
This is useful when the caller wants to create a ConstBufferSequence
from an object which lives on the caller’s stack or it is inconvenient for the caller to keep the object alive until the send operation is complete.
system::result<void>
handle_request(
serializer& sr,
response& res,
request_view req)
{
res.set_start_line(status::ok, req.version());
res.set_keep_alive(req.keep_alive());
std::string body{ "<!doctype html><title>HOWDY!</title>" };
sr.start(res, [body = std::move(body)]{return buffers::make_buffer(body);});
return {};
}
Streaming Body Contents
Sometimes it is desirable to read the body contents asynchronously, such as when reading from a socket, file, or a pipe. In such scenarios, it is possible to borrow buffers from the serializer
and use them for the asynchronous read operation. As a result, the contents would be read directly into the serializer
's internal buffer without needing to make an extra copy.
The following snippet demonstrates a usage example (using synchronous read/write APIs to keep the code simple):
template<class ReadStream, class WriteStream>
void relay_body_contents(
serializer& sr,
response& res,
request_view req,
ReadStream& src,
WriteStream& client_session)
{
res.set_start_line(status::ok, req.version());
res.set_keep_alive(req.keep_alive());
res.set_chunked(true);
auto stream = sr.start_stream(res);
auto write_some = [&]
{
auto bs = sr.prepare().value();
system::error_code ec;
auto length = client_session.write_some(bs, ec);
// if (ec.failed()) handle the error...
sr.consume(length);
};
for (;;)
{
auto bs = stream.prepare(stream.capacity());
system::error_code ec;
auto length = src.read_some(bs, ec);
stream.commit(length);
if (ec == asio::error::eof)
break;
// if (ec.failed()) handle the error...
write_some();
}
// Closing stream to signal serializer end of body contents
stream.close();
while (!sr.is_done())
write_some();
}