-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
167 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 {}; | ||
} | ||
---- | ||
|
||
|
||
== 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 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(); | ||
} | ||
---- |