Files
boost_beast/doc/4_7_parser_buffers.qbk
T
2017-07-20 08:12:18 -07:00

171 lines
6.1 KiB
Plaintext

[/
Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
]
[section:parser_buffers Buffer-Oriented Parsing]
In extreme cases, users may wish to create an instance of __parser__,
__header_parser__, or a user-defined type derived from __basic_parser__ and
invoke its methods directly instead of using the provided stream algorithms.
This could be useful for implementing algorithms on streams whose interface
does not conform to any __Stream__. For example, a
[@http://zeromq.org/ *ZeroMQ* socket].
The basic parser interface is interactive; the caller invokes the function
[link beast.ref.http__basic_parser.put `basic_parser::put`]
repeatedly with buffers until an error occurs or the parsing is done. The
function
[link beast.ref.http__basic_parser.put_eof `basic_parser::put_eof`]
Is used when the caller knows that there will never be more data (for example,
if the underlying connection is closed),
[heading Parser Options]
The parser provides two options which may be set before parsing begins:
[table Parser Options
[[Name][Default][Description]]
[[
[link beast.ref.http__basic_parser.eager.overload2 `eager`]
][
`false`
][
Normally the parser returns after successfully parsing a structured
element (header, chunk header, or chunk body) even if there are octets
remaining in the input. This is necessary when attempting to parse the
header first, or when the caller wants to inspect information which may
be invalidated by subsequent parsing, such as a chunk extension. The
`eager` option controls whether the parser keeps going after parsing
structured element if there are octets remaining in the buffer and no
error occurs. This option is automatically set or cleared during certain
stream operations to improve performance with no change in functionality.
]]
[[
[link beast.ref.http__basic_parser.skip.overload2 `skip`]
][
`false`
][
This option controls whether or not the parser expects to see an HTTP
body, regardless of the presence or absence of certain fields such as
Content-Length or a chunked Transfer-Encoding. Depending on the request,
some responses do not carry a body. For example, a 200 response to a
[@https://tools.ietf.org/html/rfc7231#section-4.3.6 CONNECT] request
from a tunneling proxy, or a response to a
[@https://tools.ietf.org/html/rfc7231#section-4.3.2 HEAD] request.
In these cases, callers may use this function inform the parser that
no body is expected. The parser will consider the message complete
after the header has been received.
]]
]
[heading Example: Parsing from a std::istream]
The standard library provides the type `std::istream` for performing high
level operations on character streams. The variable `std::cin` is based
on this input stream. In this example, we build a stream operation which
parses an HTTP message from a `std::istream`:
```
/** Parse an HTTP/1 message from a `std::istream`.
This function attempts to parse a complete message from the stream.
@param is The `std::istream` to read from.
@param buffer The buffer to use.
@param msg The message to store the result.
@param ec Set to the error, if any occurred.
*/
template<
class Allocator,
bool isRequest,
class Body,
class Fields>
void
parse_istream(
std::istream& is,
basic_flat_buffer<Allocator>& buffer,
message<isRequest, Body, Fields>& msg,
error_code& ec)
{
// Create the message parser
parser<isRequest, Body, Fields> parser;
do
{
// Extract whatever characters are presently available in the istream
if(is.rdbuf()->in_avail() > 0)
{
// Get a mutable buffer sequence for writing
auto const mb = buffer.prepare(is.rdbuf()->in_avail());
// Now get everything we can from the istream
buffer.commit(is.readsome(
boost::asio::buffer_cast<char*>(mb),
boost::asio::buffer_size(mb)));
}
else if(buffer.size() == 0)
{
// Our buffer is empty and we need more characters,
// see if we've reached the end of file on the istream
if(! is.eof())
{
// Get a mutable buffer sequence for writing
auto const mb = buffer.prepare(1024);
// Try to get more from the istream. This might block.
is.read(
boost::asio::buffer_cast<char*>(mb),
boost::asio::buffer_size(mb));
// If an error occurs on the istream then return it to the caller.
if(is.fail() && ! is.eof())
{
// We'll just re-use io_error since std::istream has no error_code interface.
ec = make_error_code(errc::io_error);
return;
}
// Commit the characters we got to the buffer.
buffer.commit(is.gcount());
}
else
{
// Inform the parser that we've reached the end of the istream.
parser.put_eof(ec);
if(ec)
return;
break;
}
}
// Write the data to the parser
auto const bytes_used = parser.put(buffer.data(), ec);
// This error means that the parser needs additional octets.
if(ec == error::need_more)
ec = {};
if(ec)
return;
// Consume the buffer octets that were actually parsed.
buffer.consume(bytes_used);
}
while(! parser.is_done());
// Transfer ownership of the message container in the parser to the caller.
msg = parser.release();
}
```
[tip
Parsing from a `std::istream` could be implemented using an alternate
strategy: adapt the `std::istream` interface to a __SyncReadStream__.
This would allow it to work with the library's existing algorithms.
We leave this as an exercise for the reader.
]
[endsect]