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 Apr 24, 2024
1 parent 1bbbd5c commit 9d6c39a
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 0 deletions.
4 changes: 4 additions & 0 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
* xref:Message.adoc[]
* Design Requirements
** xref:design_requirements/serializer.adoc[Serializer]
* xref:reference:boost/http_proto.adoc[Reference]
161 changes: 161 additions & 0 deletions doc/modules/ROOT/pages/design_requirements/serializer.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
//
// Copyright (c) 2024 Mohammad Nejati
//
// 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

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.

[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` 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).

[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` can 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 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{ "<!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):

[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 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();
}
----

0 comments on commit 9d6c39a

Please sign in to comment.