Skip to content

Commit

Permalink
Add serializer design notes
Browse files Browse the repository at this point in the history
  • Loading branch information
ashtum committed Mar 29, 2024
1 parent c7056c4 commit 22fa1c2
Showing 1 changed file with 167 additions and 0 deletions.
167 changes: 167 additions & 0 deletions doc/modules/ROOT/pages/serializer.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
//
// Copyright (c) 2024 Vinnie Falco ([email protected])
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/cppalliance/http_proto
//

= Serializer Design Requirements


== Use Cases and Interfaces


=== Empty Body

Useful for sending bodiless requests or responses, it should be possible to reuse the existing `serializer` and `request`/`response` objects without any need for extra memory allocation.


[source,cpp]
----
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` should take 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).


[source,cpp]
----
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` should be able to use body contents passed as a `ConstBufferSequence` without copying it into its internal buffer.

[source,cpp]
----
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 for any reason it is inconvenient for the caller to keep the object alive until the send operation is complete.

[source,cpp]
----
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;
body += "<section>";
body += "<b>";
body += "HOWDY!";
body += "</b>";
body += "</section>";
sr.start(res, [body = std::move(body)]{ return buffers::make_buffer(body);});
return {};
}
----


== Stream Body

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 must be possible to borrow buffers from the `serializer` and use them for the asynchronous read operation. As a result, the content 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):

[source,cpp]
----
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 cbs = sr.prepare().value();
system::error_code ec;
auto length = client_session.write_some(cbs, ec);
// if (ec.failed()) handle the error...
sr.consume(length);
};
for (;;)
{
auto mbs = stream.prepare(stream.capacity());
system::error_code ec;
auto length = src.read_some(mbs, ec);
stream.commit(length);
if (ec == asio::error::eof)
break;
// if (ec.failed()) handle the error...
write_some();
}
// Closing the stream to signal the serializer the end of the body contents
stream.close();
while (!sr.is_done())
write_some();
}
----

0 comments on commit 22fa1c2

Please sign in to comment.