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.set_message(res);
sr.start();
return {};
}
WriteSink Body
When the caller already has the body data in memory, the WriteSink interface writes caller-owned buffers through the serializer to the stream. The sink handles framing (chunked encoding, compression) automatically.
capy::task<>
handle_request(
serializer& sr,
response& res,
request_view req,
capy::WriteStream auto& socket)
{
res.set_start_line(status::not_found, req.version());
res.set_keep_alive(req.keep_alive());
// Assume caller has a stable reference to static_pages
sr.set_message(res);
auto sink = sr.sink_for(socket);
co_await sink.write_eof(
capy::make_buffer(static_pages.not_found));
}
BufferSink Body (Zero-Copy Streaming)
Sometimes it is desirable to read the body contents asynchronously, such as when reading from a socket, file, or a pipe. The BufferSink interface lets the caller write directly into the serializer’s internal buffer, avoiding an extra copy.
capy::task<>
relay_body_contents(
serializer& sr,
response& res,
request_view req,
capy::ReadStream auto& src,
capy::WriteStream auto& client_session)
{
res.set_start_line(status::ok, req.version());
res.set_keep_alive(req.keep_alive());
res.set_chunked(true);
sr.set_message(res);
auto sink = sr.sink_for(client_session);
for (;;)
{
capy::mutable_buffer arr[16];
auto bufs = sink.prepare(arr);
auto [ec, n] = co_await src.read_some(bufs);
if (ec == capy::error::eof)
{
co_await sink.commit_eof(n);
break;
}
co_await sink.commit(n);
}
}