Beast Logo

PrevUpHomeNext

Message Stream Operations

Beast provides synchronous and asynchronous algorithms to parse and serialize HTTP/1 wire format messages on streams. These functions form the message-oriented stream interface:

Table 16. Message Stream Operations

Name

Description

read

Read a message from a SyncReadStream.

async_read

Read a message from an AsyncReadStream.

write

Write a message to a SyncWriteStream.

async_write

Write a message to an AsyncWriteStream.


All synchronous stream operations come in two varieties. One which throws an exception upon error, and another which accepts as the last parameter an argument of type error_code&. If an error occurs this argument will be set to contain the error code.

Reading

Because a serialized header is not length-prefixed, algorithms which parse messages from a stream may read past the end of a message for efficiency. To hold this surplus data, all stream read operations use a passed-in DynamicBuffer which must be persisted between calls. Each read operation may consume bytes remaining in the buffer, and leave behind new bytes. In this example we declare the buffer and a message variable, then read a complete HTTP request synchronously:

flat_buffer buffer;         // (The parser is optimized for flat buffers)
request<string_body> req;
read(sock, buffer, req);

This example uses flat_buffer. Beast's basic_parser is optimized for structured HTTP data located in a single contiguous (flat) memory buffer. When not using a flat buffer the implementation may perform an additional memory allocations to restructure the input into a single buffer for parsing.

[Tip] Tip

Other Implementations of DynamicBuffer may avoid parser memory allocation by always returning buffer sequences of length one.

Messages may also be read asynchronously. When performing asynchronous stream read operations the stream, buffer, and message variables must remain valid until the operation has completed. Beast asynchronous initiation functions use Asio's completion handler model. This call reads a message asynchronously and report the error code upon completion:

flat_buffer buffer;
response<string_body> res;
async_read(sock, buffer, res,
    [&](error_code ec)
    {
        std::cerr << ec.message() << std::endl;
    });

If a read stream algorithm cannot complete its operation without exceeding the maximum specified size of the dynamic buffer provided, the error buffer_overflow is returned. This may be used to impose a limit on the maximum size of an HTTP message header for protection from buffer overflow attacks. The following code will print the error message:

// This buffer's max size is too small for much of anything
flat_buffer buffer{10};

// Try to read a request
error_code ec;
request<string_body> req;
read(sock, buffer, req, ec);
if(ec == error::buffer_overflow)
    std::cerr << "Buffer limit exceeded!" << std::endl;
Writing

A set of free functions allow serialization of an entire HTTP message to a stream. If a response has no declared content length and no chunked transfer encoding, then the end of the message is indicated by the server closing the connection. When sending such a response, Beast will return the error::end_of_stream from the write algorithm to indicate to the caller that the connection should be closed. This example constructs and sends a response whose body length is determined by the number of octets received prior to the server closing the connection:

response<string_body> res;
res.version = 11;
res.result(status::ok);
res.set(field::server, "Beast");
res.body = "Hello, world!";

error_code ec;
write(sock, res, ec);
if(ec == error::end_of_stream)
    sock.close();

The asynchronous version could be used instead:

async_write(sock, res,
    [&](error_code)
    {
        if(ec)
            std::cerr << ec.message() << std::endl;
    });

PrevUpHomeNext