Documentation work

This commit is contained in:
Vinnie Falco
2017-06-07 18:18:50 -07:00
parent 2df6783468
commit 67df48daa6
17 changed files with 486 additions and 286 deletions

View File

@ -72,6 +72,8 @@
[import ../examples/websocket_example.cpp]
[import ../examples/echo_op.cpp]
[import ../examples/doc_http_samples.hpp]
[import ../test/core/doc_snippets.cpp]
[import ../test/http/doc_snippets.cpp]
[include 1_overview.qbk]
[include 2_examples.qbk]

View File

@ -17,21 +17,9 @@ lists these facilities by group, with descriptions.
This documentation assumes familiarity with __Asio__. Sample
code and identifiers used throughout are written as if the
following declarations are in effect:
```
#include <beast/core.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <thread>
using namespace beast;
boost::asio::io_service ios;
boost::asio::io_service work{ios};
std::thread t{[&](){ ios.run(); }};
error_code ec;
```
[snippet_core_1a]
[snippet_core_1b]
]
[include 3_1_asio.qbk]

View File

@ -20,16 +20,8 @@ or other object which meets the required stream concepts and already has
communication established with an endpoint. This example is provided as a
reminder of how to work
with sockets:
```
auto host = "www.example.com";
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::connect(sock, r.resolve(
boost::asio::ip::tcp::resolver::query{host, "http"}));
// At this point `sock` is a connected to a remote
// host and may be used to perform stream operations.
```
[snippet_core_2]
Throughout this documentation identifiers with the following names have
special meaning:

View File

@ -118,14 +118,7 @@ Using the type checks with `static_assert` on function or class template
types will provide users with helpful error messages and prevent undefined
behaviors. This example shows how a template function which writes to a
synchronous stream may check its argument:
```
template<class SyncWriteStream>
void write_string(SyncWriteStream& stream, string_view s)
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
boost::asio::write(stream, boost::asio::const_buffers_1(s.data(), s.size()));
}
```
[snippet_core_3]
[endsect]

View File

@ -54,12 +54,6 @@ this echo operation:
[core_sample_echo_op_4]
```
// echo_op is callable with the signature void(error_code, bytes_transferred),
// allowing `*this` to be used as both a ReadHandler and a WriteHandler.
//
```
A complete, runnable version of this example may be found in the examples
directory.

View File

@ -66,10 +66,8 @@ format using __Asio__. Specifically, the library provides:
the HTTP protocol specification described in __rfc7230__. Sample
code and identifiers mentioned in this section is written as if
these declarations are in effect:
```
#include <beast/http.hpp>
using namespace beast::http;
```
[http_snippet_1]
]
[include 4_01_primer.qbk]

View File

@ -116,42 +116,39 @@ the __Body__ requirements:
[heading Usage]
The code examples below show how to create and fill in request and response
objects:
objects: Here is simple example of building an
[@https://tools.ietf.org/html/rfc7231#section-4.3.1 HTTP GET]
request. The function
[link beast.ref.http__message.prepare prepare]
allows for various connection options. The use of prepare is optional,
the Connection field may be set manually if desired.
[table Create Request
[[Statements] [Serialized Result]]
[[
```
request<string_body> req;
req.version = 11; // HTTP/1.1
req.method(verb::get);
req.target("/index.htm");
req.insert("Accept", "text/html");
req.insert("Connection", "keep-alive");
req.insert("User-Agent", "Beast");
```
[http_snippet_2]
][
```
GET /index.htm HTTP/1.1\r\n
Accept: text/html\r\n
Connection: keep-alive\r\n
User-Agent: Beast\r\n
Connection: close\r\n
\r\n
```
]]
]
Here we create an HTTP response indicating success. Note that this
message has a body. The function
[link beast.ref.http__message.prepare prepare]
automatically sets the Content-Length or Transfer-Encoding field
depending on the body type. The use of prepare is optional, the
Content-Length and other fields may be set manually if desired.
[table Create Response
[[Statements] [Serialized Result]]
[[
```
response<string_body> res;
res.version = 11; // HTTP/1.1
res.result(status::ok);
res.body = "Hello, world!";
res.insert("Server", "Beast");
res.insert("Content-Length", res.body.size());
```
[http_snippet_3]
][
```
HTTP/1.1 200 OK\r\n

View File

@ -41,6 +41,8 @@ an exception upon error, and another which accepts as the last parameter an
argument of type [link beast.ref.error_code `error_code&`]. If an error
occurs this argument will be set to contain the error code.
[heading Reading]
Because a serialized header is not length-prefixed, algorithms which parse
@ -50,11 +52,8 @@ __DynamicBuffer__ which persists 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);
```
[http_snippet_4]
In this example we used the __flat_buffer__. The parser in Beast is
optimized for structured HTTP data located in a single contiguous memory
@ -74,15 +73,8 @@ valid until the operation has completed. Beast asynchronous initiation
functions use Asio's completion handler model. Here we read a message
asynchronously. When the operation completes the message in the error
code indicating the result is printed:
```
flat_buffer buffer;
response<string_body> res;
async_read(sock, buffer,
[&](error_code ec)
{
std::cerr << ec.message() << std::endl;
});
```
[http_snippet_5]
If a read stream algorithm cannot complete its operation without exceeding
the maximum specified size of the dynamic buffer provided, the error
@ -90,70 +82,27 @@ the maximum specified size of the dynamic buffer provided, the error
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 generate an error:
```
// This buffer 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;
[http_snippet_6]
```
[heading Writing]
A set of free functions allow serialization of an entire HTTP message to
a stream. This function sends a message synchronously on the socket,
throwing an exception if an error occurs:
```
template<class Body, class Fields>
void send(response<Body, Fields> const& res)
{
write(sock, res);
}
```
If a response has no declared content length, and no chunked transfer
encoding, the end of the message is indicated by the server closing
a stream. If a response has no declared content length, and no chunked
transfer encoding, the end of the message is indicated by the server closing
the connection. When sending such a response, Beast will return the
error `boost::asio::error::eof` from the write algorithm to indicate
[link beast.ref.http__error `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:
```
void send()
{
response<string_body> res;
res.version = 11;
res.result(status::ok);
res.insert("Server", "Beast");
res.body = "Hello, world!";
error_code ec;
write(sock, res, ec);
if(ec == boost::asio::error::eof)
sock.close();
else
BOOST_ASSERT(ec);
}
```
[http_snippet_7]
An asynchronous version is also available:
The asynchronous version could be used instead:
```
template<class Body, class Fields>
void send_async(response<Body, Fields> const& res)
{
async_write(sock, res,
[&](error_code)
{
if(ec)
std::cerr << ec.message() << std::endl;
});
}
```
[http_snippet_8]
[endsect]

View File

@ -22,32 +22,20 @@ Sophisticated algorithms will need to do more:
All of these operations require callers to manage the lifetime of state
information associated with the operation, by constructing a __serializer__
object with the message to be sent. The serializer type has this declaration:
```
template<
bool isRequest,
class Body,
class Fields,
class ChunkDecorator = no_chunk_decorator,
class Allocator = std::allocator<char>
>
class serializer;
```
[http_snippet_9]
The choices for template types must match the message passed on construction.
This code creates an HTTP response and the corresponding serializer:
```
response<string_body> res;
...
serializer<false, string_body, fields> sr{res};
```
[http_snippet_10]
The convenience function
[link beast.ref.http__make_serializer `make_serializer`]
is provided to avoid repetition of template argument types. The declaration
for `sr` in the code above may be written as:
```
...
auto sr = make_serializer(res);
```
[http_snippet_11]
The stream operations which work on serializers are:
@ -88,19 +76,6 @@ The stream operations which work on serializers are:
Here is an example of using a serializer to send a message on a stream
synchronously. This performs the same operation as calling `write(stream, m)`:
```
template<class SyncWriteStream, bool isRequest, class Body, class Fields>
void send(SyncWriteStream& stream, message<isRequest, Body, Fields> const& m)
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
serializer<isRequest, Body, Fields> sr{m};
do
{
write_some(stream, sr);
}
while(! sr.is_done());
}
```
[http_snippet_12]
[endsect]

View File

@ -114,22 +114,7 @@ accesses the contained object, and depending on the types used to instantiate
the parser, it may be possible to acquire ownership of the header or message
captive object and destroy the parser. In this example we read an HTTP
response with a string body using a parser, then print the response:
```
template<class SyncReadStream>
void print_response(SyncReadStream& stream)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
// Declare a parser for an HTTP response
response_parser<string_body> parser;
// Read the entire message
read(stream, parser);
// Now print the message
std::cout << parser.get() << std::endl;
}
```
[http_snippet_13]
[endsect]

View File

@ -30,63 +30,13 @@ expressed with a generic lambda. The function
[link beast.ref.http__serializer.is_done `serializer::is_done`]
will return `true` when all the buffers have been produced. This C++14
example prints the buffers to standard output:
```
template<bool isRequest, class Body, class Fields>
void print(message<isRequest, Body, Fields> const& m)
{
error_code ec;
serializer<isRequest, Body, Fields> sr{m};
do
{
sr.get(ec,
[&sr](error_code& ec, auto const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
});
}
while(! ec && ! sr.is_done());
if(! ec)
std::cout << std::endl;
else
std::cerr << ec.message() << std::endl;
}
```
[http_snippet_14]
Generic lambda expressions are only available in C++14 or later. A functor
with a templated function call operator is necessary to use C++11 as shown:
```
template<class Serializer>
struct lambda
{
Serializer& sr;
lambda(Serializer& sr_) : sr(sr_) {}
template<class ConstBufferSequence>
void operator()(error_code& ec, ConstBufferSequence const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
}
};
template<bool isRequest, class Body, class Fields>
void print(message<isRequest, Body, Fields> const& m)
{
error_code ec;
serializer<isRequest, Body, Fields> sr{m};
do
{
sr.get(ec, lambda<decltype(sr)>{sr});
}
while(! ec && ! sr.is_done());
if(! ec)
std::cout << std::endl;
else
std::cerr << ec.message() << std::endl;
}
```
[http_snippet_15]
[heading Split Serialization]
@ -102,42 +52,8 @@ to the body. The function
[link beast.ref.http__serializer.is_header_done `serializer::is_header_done`]
informs the caller whether the header has completed serialization. In this
C++14 example we print the header first, followed by the body:
```
template<bool isRequest, class Body, class Fields>
void print(message<isRequest, Body, Fields> const& m)
{
error_code ec;
serializer<isRequest, Body, Fields> sr{m};
sr.split(true);
std::cout << "Header:" << std::endl;
do
{
sr.get(ec,
[&sr](error_code& ec, auto const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
});
}
while(! sr.is_header_done());
if(! ec && ! sr.is_done())
{
std::cout << "Body:" << std::endl;
do
{
sr.get(ec,
[&sr](error_code& ec, auto const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
});
}
while(! ec && ! sr.is_done());
}
if(ec)
std::cerr << ec.message() << std::endl;
}
```
[http_snippet_16]
[heading Chunk Decorators]
@ -173,25 +89,7 @@ after subsequent calls.
Here, we declare a decorator which sets an extension variable `x` equal
to the size of the chunk in bytes, and returns a single trailer field:
```
struct decorator
{
std::string s;
template<class ConstBufferSequence>
string_view
operator()(ConstBufferSequence const& buffers)
{
s = ";x=" + std::to_string(boost::asio::buffer_size(buffers));
return s;
}
string_view
operator()(boost::asio::null_buffers)
{
return "Result: OK\r\n";
}
};
```
[http_snippet_17]
[endsect]

View File

@ -143,6 +143,9 @@ public:
//[core_sample_echo_op_4
// echo_op is callable with the signature void(error_code, bytes_transferred),
// allowing `*this` to be used as both a ReadHandler and a WriteHandler.
//
template<class AsyncStream, class Handler>
void echo_op<AsyncStream, Handler>::
operator()(beast::error_code ec, std::size_t bytes_transferred)

View File

@ -24,6 +24,7 @@ unit-test core-tests :
core/buffers_adapter.cpp
core/clamp.cpp
core/consuming_buffers.cpp
core/doc_snippets.cpp
core/error.cpp
core/flat_buffer.cpp
core/handler_alloc.cpp
@ -44,6 +45,7 @@ unit-test http-tests :
http/basic_parser.cpp
http/buffer_body.cpp
http/doc_http_samples.cpp
http/doc_snippets.cpp
http/dynamic_body.cpp
http/error.cpp
http/field.cpp

View File

@ -8,15 +8,16 @@ add_executable (core-tests
${BEAST_INCLUDES}
${EXTRAS_INCLUDES}
../../extras/beast/unit_test/main.cpp
buffer_test.hpp
async_result.cpp
bind_handler.cpp
buffer_cat.cpp
buffer_prefix.cpp
buffer_test.hpp
buffered_read_stream.cpp
buffers_adapter.cpp
clamp.cpp
consuming_buffers.cpp
buffered_read_stream.cpp
doc_snippets.cpp
error.cpp
flat_buffer.cpp
handler_alloc.cpp

View File

@ -0,0 +1,66 @@
//
// 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)
//
//[snippet_core_1a
#include <beast/core.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <thread>
//]
using namespace beast;
namespace doc_core_snippets {
void fxx()
{
//[snippet_core_1b
//
using namespace beast;
boost::asio::io_service ios;
boost::asio::io_service::work work{ios};
std::thread t{[&](){ ios.run(); }};
error_code ec;
boost::asio::ip::tcp::socket sock{ios};
//]
{
//[snippet_core_2
auto host = "www.example.com";
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket stream{ios};
boost::asio::connect(stream, r.resolve(
boost::asio::ip::tcp::resolver::query{host, "http"}));
// At this point `stream` is a connected to a remote
// host and may be used to perform stream operations.
//]
}
} // fxx()
//[snippet_core_3
template<class SyncWriteStream>
void write_string(SyncWriteStream& stream, string_view s)
{
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
boost::asio::write(stream, boost::asio::const_buffers_1(s.data(), s.size()));
}
//]
} // doc_core_snippets

View File

@ -15,6 +15,7 @@ add_executable (http-tests
basic_parser.cpp
buffer_body.cpp
doc_http_samples.cpp
doc_snippets.cpp
dynamic_body.cpp
empty_body.cpp
error.cpp

356
test/http/doc_snippets.cpp Normal file
View File

@ -0,0 +1,356 @@
//
// 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)
//
#include <beast/core.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <thread>
using namespace beast;
//[http_snippet_1
#include <beast/http.hpp>
using namespace beast::http;
//]
namespace doc_http_snippets {
void fxx() {
boost::asio::io_service ios;
boost::asio::io_service::work work{ios};
std::thread t{[&](){ ios.run(); }};
error_code ec;
boost::asio::ip::tcp::socket sock{ios};
{
//[http_snippet_2
request<string_body> req;
req.version = 11; // HTTP/1.1
req.method(verb::get);
req.target("/index.htm");
req.insert("Accept", "text/html");
req.insert("User-Agent", "Beast");
req.prepare(connection::close);
//]
}
{
//[http_snippet_3
response<string_body> res;
res.version = 11; // HTTP/1.1
res.result(status::ok);
res.insert("Server", "Beast");
res.body = "Hello, world!";
res.prepare();
//]
}
{
//[http_snippet_4
flat_buffer buffer; // (The parser is optimized for flat buffers)
request<string_body> req;
read(sock, buffer, req);
//]
}
{
//[http_snippet_5
flat_buffer buffer;
response<string_body> res;
async_read(sock, buffer, res,
[&](error_code ec)
{
std::cerr << ec.message() << std::endl;
});
//]
}
{
//[http_snippet_6
// This buffer's max size is too small for much of anything
flat_buffer buffer{10};
// Try to read a request
request<string_body> req;
read(sock, buffer, req, ec);
if(ec == error::buffer_overflow)
std::cerr << "Buffer limit exceeded!" << std::endl;
//]
}
{
//[http_snippet_7
response<string_body> res;
res.version = 11;
res.result(status::ok);
res.insert("Server", "Beast");
res.body = "Hello, world!";
write(sock, res, ec);
if(ec == error::end_of_stream)
sock.close();
//]
//[http_snippet_8
async_write(sock, res,
[&](error_code)
{
if(ec)
std::cerr << ec.message() << std::endl;
});
//]
}
{
//[http_snippet_10
response<string_body> res;
serializer<false, string_body, fields> sr{res};
//]
}
{
response<string_body> res;
//[http_snippet_11
auto sr = make_serializer(res);
//]
}
} // fxx()
//[http_snippet_12
/** Send a message to a stream synchronously.
@param stream The stream to write to. This type must support
the @b SyncWriteStream concept.
@param m The message to send. The Body type must support
the @b BodyReader concept.
*/
template<
class SyncWriteStream,
bool isRequest, class Body, class Fields>
void
send(
SyncWriteStream& stream,
message<isRequest, Body, Fields> const& m)
{
// Check the template types
static_assert(is_sync_write_stream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
static_assert(is_body_reader<Body>::value,
"BodyReader requirements not met");
// Create the instance of serializer for the message
serializer<isRequest, Body, Fields> sr{m};
// Loop until the serializer is finished
do
{
// This call guarantees it will make some
// forward progress, or otherwise return an error.
write_some(stream, sr);
}
while(! sr.is_done());
}
//]
//[http_snippet_13
template<
class SyncReadStream>
void
print_response(
SyncReadStream& stream)
{
static_assert(is_sync_read_stream<SyncReadStream>::value,
"SyncReadStream requirements not met");
// Declare a parser for an HTTP response
response_parser<string_body> parser;
// Read the entire message
read(stream, parser);
// Now print the message
std::cout << parser.get() << std::endl;
}
//]
#ifdef _MSC_VER
//[http_snippet_14
template<bool isRequest, class Body, class Fields>
void
print_cxx14(message<isRequest, Body, Fields> const& m)
{
error_code ec;
serializer<isRequest, Body, Fields> sr{m};
do
{
sr.get(ec,
[&sr](error_code& ec, auto const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
});
}
while(! ec && ! sr.is_done());
if(! ec)
std::cout << std::endl;
else
std::cerr << ec.message() << std::endl;
}
//]
#endif
//[http_snippet_15
template<class Serializer>
struct lambda
{
Serializer& sr;
lambda(Serializer& sr_) : sr(sr_) {}
template<class ConstBufferSequence>
void operator()(error_code& ec, ConstBufferSequence const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
}
};
template<bool isRequest, class Body, class Fields>
void
print(message<isRequest, Body, Fields> const& m)
{
error_code ec;
serializer<isRequest, Body, Fields> sr{m};
do
{
sr.get(ec, lambda<decltype(sr)>{sr});
}
while(! ec && ! sr.is_done());
if(! ec)
std::cout << std::endl;
else
std::cerr << ec.message() << std::endl;
}
//]
#ifdef _MSC_VER
//[http_snippet_16
template<bool isRequest, class Body, class Fields>
void
split_print_cxx14(message<isRequest, Body, Fields> const& m)
{
error_code ec;
serializer<isRequest, Body, Fields> sr{m};
sr.split(true);
std::cout << "Header:" << std::endl;
do
{
sr.get(ec,
[&sr](error_code& ec, auto const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
});
}
while(! sr.is_header_done());
if(! ec && ! sr.is_done())
{
std::cout << "Body:" << std::endl;
do
{
sr.get(ec,
[&sr](error_code& ec, auto const& buffer)
{
std::cout << buffers(buffer);
sr.consume(boost::asio::buffer_size(buffer));
});
}
while(! ec && ! sr.is_done());
}
if(ec)
std::cerr << ec.message() << std::endl;
}
//]
#endif
//[http_snippet_17
struct decorator
{
std::string s;
template<class ConstBufferSequence>
string_view
operator()(ConstBufferSequence const& buffers)
{
s = ";x=" + std::to_string(boost::asio::buffer_size(buffers));
return s;
}
string_view
operator()(boost::asio::null_buffers)
{
return "Result: OK\r\n";
}
};
//]
} // doc_http_snippets
namespace beast {
namespace http {
#if 0
//[http_snippet_9]
template<
bool isRequest,
class Body,
class Fields,
class ChunkDecorator = no_chunk_decorator,
class Allocator = std::allocator<char>
>
class serializer;
//]
#endif
} // http
} // beast