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